Files
cproof/tests/functionaltests/proftest.c
jabber.developer2 b4dd64a0c9
Some checks failed
CI Code / Check spelling (pull_request) Successful in 27s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (ubuntu) (pull_request) Failing after 3m37s
CI Code / Linux (debian) (pull_request) Failing after 9m30s
CI Code / Linux (arch) (pull_request) Successful in 13m27s
fix(tests): update functional tests to use regex assertions and increase timeout for expect operations
2025-12-21 13:53:02 +03:00

314 lines
7.5 KiB
C

#include <sys/stat.h>
#include <sys/wait.h>
#include <glib.h>
#include <stdlib.h>
#include "prof_cmocka.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stabber.h>
#include <expect.h>
#include "proftest.h"
char *config_orig;
char *data_orig;
int fd = 0;
int stub_port = 5230;
gboolean
_create_dir(const char *name)
{
struct stat sb;
if (stat(name, &sb) != 0) {
if (errno != ENOENT || mkdir(name, S_IRWXU) != 0) {
return FALSE;
}
} else {
if ((sb.st_mode & S_IFDIR) != S_IFDIR) {
return FALSE;
}
}
return TRUE;
}
gboolean
_mkdir_recursive(const char *dir)
{
int i;
gboolean result = TRUE;
for (i = 1; i <= strlen(dir); i++) {
if (dir[i] == '/' || dir[i] == '\0') {
gchar *next_dir = g_strndup(dir, i);
result = _create_dir(next_dir);
g_free(next_dir);
if (!result) {
break;
}
}
}
return result;
}
void
_create_config_dir(void)
{
GString *profanity_dir = g_string_new(XDG_CONFIG_HOME);
g_string_append(profanity_dir, "/profanity");
if (!_mkdir_recursive(profanity_dir->str)) {
assert_true(FALSE);
}
g_string_free(profanity_dir, TRUE);
}
void
_create_data_dir(void)
{
GString *profanity_dir = g_string_new(XDG_DATA_HOME);
g_string_append(profanity_dir, "/profanity");
if (!_mkdir_recursive(profanity_dir->str)) {
assert_true(FALSE);
}
g_string_free(profanity_dir, TRUE);
}
void
_create_chatlogs_dir(void)
{
GString *chatlogs_dir = g_string_new(XDG_DATA_HOME);
g_string_append(chatlogs_dir, "/profanity/chatlogs");
if (!_mkdir_recursive(chatlogs_dir->str)) {
assert_true(FALSE);
}
g_string_free(chatlogs_dir, TRUE);
}
void
_create_logs_dir(void)
{
GString *logs_dir = g_string_new(XDG_DATA_HOME);
g_string_append(logs_dir, "/profanity/logs");
if (!_mkdir_recursive(logs_dir->str)) {
assert_true(FALSE);
}
g_string_free(logs_dir, TRUE);
}
void
_cleanup_dirs(void)
{
int res = system("rm -rf ./tests/functionaltests/files");
if (res == -1) {
assert_true(FALSE);
}
}
void
prof_start(void)
{
// Set timeout for expect operations (default is too short for CI)
exp_timeout = 30;
// helper script sets terminal columns, avoids assertions failing
// based on the test runner terminal size
fd = exp_spawnl("sh",
"sh",
"-c",
"./tests/functionaltests/start_profanity.sh",
NULL);
FILE *fp = fdopen(fd, "r+");
assert_true(fp != NULL);
setbuf(fp, (char *)0);
}
int
init_prof_test(void **state)
{
gboolean started = FALSE;
for (int p = 5230; p < 5250; ++p) {
if (stbbr_start(STBBR_LOGDEBUG, p, 0) == 0) {
stub_port = p;
started = TRUE;
break;
}
}
if (!started) {
assert_true(FALSE); // could not start stabber on any port in range
return -1;
}
// Give stabber server thread time to start listening
usleep(100000); // 100ms
config_orig = getenv("XDG_CONFIG_HOME");
data_orig = getenv("XDG_DATA_HOME");
setenv("XDG_CONFIG_HOME", XDG_CONFIG_HOME, 1);
setenv("XDG_DATA_HOME", XDG_DATA_HOME, 1);
_cleanup_dirs();
_create_config_dir();
_create_data_dir();
_create_chatlogs_dir();
_create_logs_dir();
prof_start();
assert_true(prof_output_regex("CProof\\. Type /help for help information\\."));
// set UI options to make expect assertions faster and more reliable
prof_input("/inpblock timeout 5");
assert_true(prof_output_regex("Input blocking set to 5 milliseconds"));
prof_input("/inpblock dynamic off");
assert_true(prof_output_regex("Dynamic input blocking disabled"));
prof_input("/notify chat off");
assert_true(prof_output_regex("Chat notifications disabled"));
prof_input("/notify room off");
assert_true(prof_output_regex("Room notifications disabled"));
prof_input("/wrap off");
assert_true(prof_output_regex("Word wrap disabled"));
prof_input("/roster hide");
assert_true(prof_output_regex("Roster disabled"));
prof_input("/occupants default hide");
assert_true(prof_output_regex("Occupant list disabled"));
prof_input("/time console off");
prof_input("/time console off");
assert_true(prof_output_regex("Console time display disabled\\."));
prof_input("/time chat off");
assert_true(prof_output_regex("Chat time display disabled\\."));
prof_input("/time muc off");
assert_true(prof_output_regex("MUC time display disabled\\."));
prof_input("/time config off");
assert_true(prof_output_regex("Config time display disabled\\."));
prof_input("/time private off");
assert_true(prof_output_regex("Private chat time display disabled\\."));
prof_input("/time xml off");
assert_true(prof_output_regex("XML Console time display disabled\\."));
return 0;
}
int
close_prof_test(void **state)
{
if (fd > 0) {
prof_input("/quit");
// Give profanity time to process quit command
sleep(1);
waitpid(exp_pid, NULL, 0);
}
_cleanup_dirs();
if (config_orig) {
setenv("XDG_CONFIG_HOME", config_orig, 1);
}
if (data_orig) {
setenv("XDG_DATA_HOME", data_orig, 1);
}
stbbr_stop();
// Give stabber time to release the port
usleep(100000); // 100ms
return 0;
}
void
prof_input(const char *input)
{
GString *inp_str = g_string_new(input);
g_string_append(inp_str, "\r");
ssize_t _wn = write(fd, inp_str->str, inp_str->len);
(void)_wn;
g_string_free(inp_str, TRUE);
}
int
prof_output_exact(const char *text)
{
return (1 == exp_expectl(fd, exp_exact, text, 1, exp_end));
}
int
prof_output_regex(const char *text)
{
return (1 == exp_expectl(fd, exp_regexp, text, 1, exp_end));
}
void
prof_connect_with_roster(const char *roster)
{
GString *roster_str = g_string_new(
"<iq type='result' to='stabber@localhost/profanity'>"
"<query xmlns='jabber:iq:roster' ver='362'>"
);
g_string_append(roster_str, roster);
g_string_append(roster_str,
"</query>"
"</iq>"
);
stbbr_for_query("jabber:iq:roster", roster_str->str);
g_string_free(roster_str, TRUE);
stbbr_auth_passwd("password");
char connect_cmd[128];
snprintf(connect_cmd, sizeof(connect_cmd), "/connect stabber@localhost server 127.0.0.1 port %d tls disable auth legacy", stub_port);
prof_input(connect_cmd);
assert_true(prof_output_regex("password:"));
prof_input("password");
exp_timeout = 60;
assert_true(prof_output_regex("Connecting as stabber@localhost"));
assert_true(prof_output_regex("logged in successfully"));
assert_true(prof_output_regex(".+online.+ \\(priority 0\\)\\."));
exp_timeout = 60;
// Wait for presence stanza to be sent (content-based, not ID-based)
// Match the actual attribute order from stanza_attach_caps
assert_true(stbbr_received(
"<presence id=\"*\">"
"<c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://profanity-im.github.io\" ver=\"*\"/>"
"</presence>"
));
}
void
prof_timeout(int timeout)
{
exp_timeout = timeout;
}
void
prof_timeout_reset(void)
{
exp_timeout = 60;
}
void
prof_connect(void)
{
prof_connect_with_roster(
"<item jid='buddy1@localhost' subscription='both' name='Buddy1'/>"
"<item jid='buddy2@localhost' subscription='both' name='Buddy2'/>"
);
}