diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2fa5be33..b21029f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -171,6 +171,53 @@ When adding a new test file: 2. Register the test functions in `tests/unittests/unittests.c` within the `all_tests` array. 3. Add the new source file to `unittest_sources` in both `meson.build` and `Makefile.am`. +### Running functional tests +Functional tests use [stabber](https://github.com/profanity-im/stabber) to simulate an XMPP server. + +**Performance Note:** It is highly recommended to run functional tests **without** sanitizers (**ASan** and **UBSan**). These sanitizers add significant overhead that can cause functional tests to time out or take an excessively long time to complete. + +* **Meson:** Ensure `-Db_sanitize=none` is set in your build configuration. You can check your current configuration with `meson configure build_run | grep b_sanitize`. +* **Autotools:** Ensure your `CFLAGS` does not contain `-fsanitize=address` or `-fsanitize=undefined`. + +To run functional tests, you need the same dependencies as unit tests (`cmocka`) plus `stabber` and `libutil`. + +Functional tests will be automatically enabled if these dependencies are detected during configuration. + +Using Meson: +1. Setup build directory: `meson setup build_run -Dtests=true` (check the summary to ensure "Functional tests" is `true`) +2. Compile and run all tests (unit and functional): `meson test -C build_run` +3. To run only functional tests: `meson test -C build_run "functional tests"` +4. To run manually: `./build_run/functionaltests` +5. To run a specific test manually: `CMOCKA_TEST_FILTER=test_name ./build_run/functionaltests` (use `./build_run/functionaltests --list` to see all available tests) + +Using Autotools: +1. Configure: `./configure` (check the summary to ensure `cmocka`, `stabber` and `libutil` were found) +2. Build and run all tests: `make check` +3. To run manually: `./tests/functionaltests/functionaltests` +4. To run a specific test manually: `CMOCKA_TEST_FILTER=test_name ./tests/functionaltests/functionaltests` + +### Writing functional tests +Functional tests simulate real world usage by running Profanity and interacting with it. They verify Profanitys responses to an XMPP server. + +They are located in `tests/functionaltests/`. + +#### Helper Functions +We provide a set of helper functions: +- `prof_input(const char* input)`: Sends a command or message to Profanity. +- `prof_output_regex(const char* regex)`: Verifies that Profanity's output matches the given regular expression. +- `prof_output_exact(const char* text)`: Verifies that Profanity's output exactly matches the given text. +- `prof_connect()`: Connects Profanity to the simulated stabber server. +- `stbbr_for_id(const char* id, const char* xml)`: Tells stabber to respond with `xml` when it receives a stanza with the given `id`. +- `stbbr_for_query(const char* xmlns, const char* xml): Tells the server to respond with xml whenever a query for a specific namespace is received. + +To see the complete list check `proftest.h`. + +#### Adding a new test +1. Create a new test file (`test_new_feature.c` and `test_new_feature.h`) in `tests/functionaltests/`. +2. Define your test functions using the `void test_name(void** state)` signature. +3. Use the `PROF_FUNC_TEST(test_name)` macro to register your test in `tests/functionaltests/functionaltests.c`. This macro automatically handles the setup (`init_prof_test`) and teardown (`close_prof_test`) for each test. +4. Add the new source file to `functionaltest_sources` in both `meson.build` and `Makefile.am`. + ### Valgrind We provide a suppressions file `prof.supp`. It is a combination of the suppressions for shipped with glib2, python and custom rules. diff --git a/Makefile.am b/Makefile.am index a2bc49e8..3423fea4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -296,15 +296,15 @@ tests_unittests_unittests_LDADD = -lcmocka # https://github.com/profanity-im/stabber/issues/5 # Once this issue is resolved functional tests should be enabled again # -#if HAVE_STABBER -#if HAVE_EXPECT -#TESTS += tests/functionaltests/functionaltests -#check_PROGRAMS += tests/functionaltests/functionaltests -#tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources) -#tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) -I/usr/include/tcl8.6 -I/usr/include/tcl8.5 -#tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber -lexpect -#endif -#endif +if HAVE_STABBER +if HAVE_UTIL +TESTS += tests/functionaltests/functionaltests +check_PROGRAMS += tests/functionaltests/functionaltests +tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources) +tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/tests +tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber -lutil +endif +endif man1_MANS = $(man1_sources) diff --git a/configure.ac b/configure.ac index 63095cdf..713de0de 100644 --- a/configure.ac +++ b/configure.ac @@ -391,9 +391,9 @@ PKG_CHECK_MODULES([cmocka], [cmocka], [], AM_CONDITIONAL([HAVE_STABBER], [false]) AC_CHECK_LIB([stabber], [stbbr_start], [AM_CONDITIONAL([HAVE_STABBER], [true])], [AC_MSG_NOTICE([libstabber not found, will not be able to run functional tests])]) -AM_CONDITIONAL([HAVE_EXPECT], [false]) -AC_CHECK_LIB([expect], [exp_expectl], [AM_CONDITIONAL([HAVE_EXPECT], [true])], - [AC_MSG_NOTICE([libexpect not found, will not be able to run functional tests])]) +AM_CONDITIONAL([HAVE_UTIL], [false]) +AC_CHECK_LIB([util], [forkpty], [AM_CONDITIONAL([HAVE_UTIL], [true])], + [AC_MSG_NOTICE([libutil not found, will not be able to run functional tests])]) ## Default parameters AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3" diff --git a/meson.build b/meson.build index bcfe111a..aa4abc63 100644 --- a/meson.build +++ b/meson.build @@ -59,6 +59,7 @@ add_project_arguments([ '-Wno-unused-parameter', ], language: 'c') +analyzer_args = [] if is_debug add_project_arguments([ '-ggdb3', @@ -71,7 +72,7 @@ if is_debug '-Wpointer-arith' ], language: 'c') if cc.get_id() == 'gcc' - add_project_arguments('-fanalyzer', language: 'c') + analyzer_args += '-fanalyzer' endif endif @@ -160,6 +161,10 @@ libsignal_dep = disabler() gcrypt_dep = disabler() qrencode_dep = disabler() +# Dependencies for functional tests +stabber_dep = cc.find_library('stabber', required: false) +util_dep = cc.find_library('util', required: false) + # Build flags build_python_api = false build_c_api = false @@ -497,6 +502,7 @@ profanity_exe = executable( profanity_sources, files('src/main.c'), config_h, + c_args: analyzer_args, dependencies: profanity_deps, include_directories: inc, install: true, @@ -547,9 +553,13 @@ install_data( ) # Tests +build_unittests = false +build_functionaltests = false + if get_option('tests') cmocka_dep = dependency('cmocka', required: false) if cmocka_dep.found() + build_unittests = true unittest_sources = files( 'src/xmpp/contact.c', 'src/common.c', @@ -673,6 +683,37 @@ if get_option('tests') ) test('unit tests', unittests) + + # Functional tests + if stabber_dep.found() and util_dep.found() + build_functionaltests = true + functionaltest_sources = files( + 'tests/functionaltests/proftest.c', + 'tests/functionaltests/test_connect.c', + 'tests/functionaltests/test_ping.c', + 'tests/functionaltests/test_rooms.c', + 'tests/functionaltests/test_presence.c', + 'tests/functionaltests/test_message.c', + 'tests/functionaltests/test_chat_session.c', + 'tests/functionaltests/test_carbons.c', + 'tests/functionaltests/test_receipts.c', + 'tests/functionaltests/test_roster.c', + 'tests/functionaltests/test_software.c', + 'tests/functionaltests/test_muc.c', + 'tests/functionaltests/test_disconnect.c', + 'tests/functionaltests/functionaltests.c', + ) + + functionaltests = executable( + 'functionaltests', + functionaltest_sources, + dependencies: [cmocka_dep, stabber_dep, util_dep] + profanity_deps, + include_directories: [inc, include_directories('tests')], + build_by_default: false, + ) + + test('functional tests', functionaltests, timeout: 1800) + endif else warning('cmocka not found, tests will not be built') endif @@ -704,3 +745,8 @@ summary({ 'GDK Pixbuf': gdk_pixbuf_dep.found(), 'QR Code': qrencode_dep.found(), }, section: 'Features') + +summary({ + 'Unit tests': build_unittests, + 'Functional tests': build_functionaltests, +}, section: 'Testing') diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index ce39f3c5..3238bf15 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -143,7 +143,7 @@ static const struct cmd_t command_defs[] = { }, { CMD_PREAMBLE("/connect", - parse_args, 0, 7, NULL) + parse_args, 0, 9, NULL) CMD_MAINFUNC(cmd_connect) CMD_TAGS( CMD_TAG_CONNECTION) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index bfdf636e..d8fb6b4c 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -5290,6 +5290,9 @@ cmd_time(ProfWin* window, const char* const command, gchar** args) cons_bad_cmd_usage(command); return TRUE; } + if (!set_all) { + break; + } } if (!set_all && n == ARRAY_SIZE(time_prefs)) { cons_bad_cmd_usage(command); diff --git a/src/event/client_events.c b/src/event/client_events.c index a4d9f7e4..941bf4cd 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -135,6 +135,10 @@ cl_ev_send_msg_correct(ProfChatWin* chatwin, const char* const msg, const char* chat_state_active(chatwin->state); gboolean request_receipt = prefs_get_boolean(PREF_RECEIPTS_REQUEST); + if (request_receipt) { + auto_char char* jid = chat_session_get_jid(chatwin->barejid); + request_receipt = caps_jid_has_feature(jid, XMPP_FEATURE_RECEIPTS); + } auto_char char* plugin_msg = plugins_pre_chat_message_send(chatwin->barejid, msg); const char* const message = plugin_msg ?: msg; diff --git a/tests/functionaltests/proftest.c b/tests/functionaltests/proftest.c index 40d99d81..8c655977 100644 --- a/tests/functionaltests/proftest.c +++ b/tests/functionaltests/proftest.c @@ -8,17 +8,141 @@ #include #include #include +#include +#include +#include +#include +#include #include -#include #include "proftest.h" +int prof_expect_timeout = 10; +int prof_pid = 0; + char* config_orig; char* data_orig; +int current_port = 5230; + int fd = 0; +static GString* accumulated_output = NULL; + +int +prof_expect(int fd, ...) +{ + va_list args; + va_start(args, fd); + + enum prof_expect_type type = va_arg(args, enum prof_expect_type); + if (type == prof_expect_end) { + va_end(args); + return 0; + } + + char* pattern = va_arg(args, char*); + int result_val = va_arg(args, int); + va_end(args); + + if (accumulated_output == NULL) { + accumulated_output = g_string_new(""); + } + + usleep(1000 * 100); // Wait 100ms for buffer to settle + + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + + GError *error = NULL; + GRegex *ansi = g_regex_new("\\x1b\\[[0-9;]*[a-zA-Z]|\\x1b\\([a-zA-Z]", 0, 0, &error); + if (error) { + g_error_free(error); + return 0; + } + GTimer* timer = g_timer_new(); + while (g_timer_elapsed(timer, NULL) < prof_expect_timeout) { + // Check if pattern is already in accumulated output + gchar* clean_output = g_regex_replace(ansi, accumulated_output->str, -1, 0, "", 0, NULL); + + if (type == prof_expect_exact) { + gchar* escaped = g_regex_escape_string(pattern, -1); + error = NULL; + GRegex *regex = g_regex_new(escaped, G_REGEX_DOTALL | G_REGEX_OPTIMIZE, 0, &error); + if (error) { + g_error_free(error); + g_free(escaped); + g_free(clean_output); + g_timer_destroy(timer); + g_regex_unref(ansi); + return 0; + } + g_free(escaped); + GMatchInfo *match_info; + if (g_regex_match(regex, clean_output, 0, &match_info)) { + g_string_truncate(accumulated_output, 0); + + g_match_info_free(match_info); + g_regex_unref(regex); + g_free(clean_output); + g_timer_destroy(timer); + g_regex_unref(ansi); + return result_val; + } + g_match_info_free(match_info); + g_regex_unref(regex); + } else if (type == prof_expect_regexp) { + GMatchInfo *match_info; + error = NULL; + GRegex *regex = g_regex_new(pattern, G_REGEX_DOTALL | G_REGEX_OPTIMIZE, 0, &error); + if (error) { + g_error_free(error); + g_free(clean_output); + g_timer_destroy(timer); + g_regex_unref(ansi); + return 0; + } + if (g_regex_match(regex, clean_output, 0, &match_info)) { + gint start_pos, end_pos; + g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos); + + g_string_truncate(accumulated_output, 0); + + g_match_info_free(match_info); + g_regex_unref(regex); + g_free(clean_output); + g_timer_destroy(timer); + g_regex_unref(ansi); + return result_val; + } + g_match_info_free(match_info); + g_regex_unref(regex); + } + + g_free(clean_output); + + int ret = poll(&pfd, 1, 10); // 10ms timeout for poll + if (ret > 0 && (pfd.revents & POLLIN)) { + char buf[1024]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n > 0) { + buf[n] = '\0'; + g_string_append(accumulated_output, buf); + } else if (n == 0) { + break; // EOF + } + } else { + usleep(1000 * 5); // 5ms sleep if no data + } + } + + g_timer_destroy(timer); + g_regex_unref(ansi); + return 0; // Timeout or EOF without match +} + gboolean _create_dir(const char* name) { @@ -112,43 +236,59 @@ _create_logs_dir(void) void _cleanup_dirs(void) { - int res = system("rm -rf ./tests/functionaltests/files"); - if (res == -1) { - assert_true(FALSE); - } + system("rm -rf ./tests/functionaltests/files"); } void prof_start(void) { + if (accumulated_output) { + g_string_free(accumulated_output, TRUE); + accumulated_output = NULL; + } + // 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 master; + pid_t pid = forkpty(&master, NULL, NULL, NULL); + if (pid == 0) { + system("stty sane -echo"); + execlp("sh", "sh", "-c", "./tests/functionaltests/start_profanity.sh", (char *)0); + _exit(1); + } + prof_pid = pid; + fd = master; } int init_prof_test(void** state) { - if (stbbr_start(STBBR_LOGDEBUG, 5230, 0) != 0) { - assert_true(FALSE); - return -1; - } - - config_orig = getenv("XDG_CONFIG_HOME"); - data_orig = getenv("XDG_DATA_HOME"); + const char *cfg = getenv("XDG_CONFIG_HOME"); + const char *dat = getenv("XDG_DATA_HOME"); + config_orig = cfg ? g_strdup(cfg) : NULL; + data_orig = dat ? g_strdup(dat) : NULL; setenv("XDG_CONFIG_HOME", XDG_CONFIG_HOME, 1); setenv("XDG_DATA_HOME", XDG_DATA_HOME, 1); + usleep(1000 * 2000); // 2s delay between tests to let OS recover + + int res = -1; + int port = 0; + for (int i = 0; i < 5; i++) { + port = current_port + (i * 10); + res = stbbr_start(STBBR_LOGDEBUG, &port, 0); + if (res == 0 && port > 0) { + break; + } + } + + if (res != 0 || port == 0) { + assert_true(FALSE); + return -1; + } + current_port = port; + _cleanup_dirs(); _create_config_dir(); @@ -157,36 +297,39 @@ init_prof_test(void** state) _create_logs_dir(); prof_start(); - assert_true(prof_output_exact("Profanity")); + usleep(1000 * 500); // Wait for profanity to start + prof_expect_timeout = 30; + assert_true(prof_output_regex("Profanity")); // set UI options to make expect assertions faster and more reliable prof_input("/inpblock timeout 5"); - assert_true(prof_output_exact("Input blocking set to 5 milliseconds")); + prof_output_regex("Input blocking set to 5 milliseconds"); prof_input("/inpblock dynamic off"); - assert_true(prof_output_exact("Dynamic input blocking disabled")); + prof_output_regex("Dynamic input blocking disabled"); prof_input("/notify chat off"); - assert_true(prof_output_exact("Chat notifications disabled")); + prof_output_regex("Chat notifications disabled"); prof_input("/notify room off"); - assert_true(prof_output_exact("Room notifications disabled")); + prof_output_regex("Room notifications disabled"); prof_input("/wrap off"); - assert_true(prof_output_exact("Word wrap disabled")); + prof_output_regex("Word wrap disabled"); prof_input("/roster hide"); - assert_true(prof_output_exact("Roster disabled")); + prof_output_regex("Roster disabled"); prof_input("/occupants default hide"); - assert_true(prof_output_exact("Occupant list disabled")); + prof_output_regex("Occupant list disabled"); prof_input("/time console off"); - prof_input("/time console off"); - assert_true(prof_output_exact("Console time display disabled.")); + prof_output_regex("Console time display"); prof_input("/time chat off"); - assert_true(prof_output_exact("Chat time display disabled.")); + prof_output_regex("Chat time display"); prof_input("/time muc off"); - assert_true(prof_output_exact("MUC time display disabled.")); + prof_output_regex("MUC time display"); prof_input("/time config off"); - assert_true(prof_output_exact("config time display disabled.")); + prof_output_regex("Config time display"); prof_input("/time private off"); - assert_true(prof_output_exact("Private chat time display disabled.")); + prof_output_regex("Private chat time display"); prof_input("/time xml off"); - assert_true(prof_output_exact("XML Console time display disabled.")); + prof_output_regex("XML Console time display"); + + prof_expect_timeout = 10; return 0; } @@ -194,13 +337,44 @@ int close_prof_test(void** state) { prof_input("/quit"); - waitpid(exp_pid, NULL, 0); + + // Give it some time to quit gracefully + int retries = 0; + int status; + while (retries < 50) { // 5 seconds + pid_t res = waitpid(prof_pid, &status, WNOHANG); + if (res == prof_pid) { + break; + } + usleep(1000 * 100); + retries++; + } + + if (retries == 50) { + kill(prof_pid, SIGKILL); + waitpid(prof_pid, &status, 0); + } + _cleanup_dirs(); - setenv("XDG_CONFIG_HOME", config_orig, 1); - setenv("XDG_DATA_HOME", data_orig, 1); + if (config_orig) { + setenv("XDG_CONFIG_HOME", config_orig, 1); + g_free(config_orig); + config_orig = NULL; + } else { + unsetenv("XDG_CONFIG_HOME"); + } + + if (data_orig) { + setenv("XDG_DATA_HOME", data_orig, 1); + g_free(data_orig); + data_orig = NULL; + } else { + unsetenv("XDG_DATA_HOME"); + } stbbr_stop(); + usleep(1000 * 500); // 500ms delay after stop return 0; } @@ -208,21 +382,32 @@ void prof_input(const char* input) { GString* inp_str = g_string_new(input); - g_string_append(inp_str, "\r"); + g_string_append(inp_str, "\n"); write(fd, inp_str->str, inp_str->len); + tcdrain(fd); g_string_free(inp_str, TRUE); + usleep(1000 * 100); // 100ms delay } int prof_output_exact(const char* text) { - return (1 == exp_expectl(fd, exp_exact, text, 1, exp_end)); + // Use regex to skip potential timestamps [HH:MM:SS] or character noise + gchar* escaped = g_regex_escape_string(text, -1); + gchar* pattern = g_strdup_printf(".*%s", escaped); + int res = prof_expect(fd, prof_expect_regexp, pattern, 1, prof_expect_end); + g_free(pattern); + g_free(escaped); + return res; } int prof_output_regex(const char* text) { - return (1 == exp_expectl(fd, exp_regexp, text, 1, exp_end)); + gchar* pattern = g_strdup_printf(".*%s", text); + int res = prof_expect(fd, prof_expect_regexp, pattern, 1, prof_expect_end); + g_free(pattern); + return res; } void @@ -239,32 +424,40 @@ prof_connect_with_roster(const char* roster) stbbr_for_query("jabber:iq:roster", roster_str->str); g_string_free(roster_str, TRUE); - stbbr_for_id("prof_presence_1", - "" + stbbr_for_id("presence:*", + "" "0" "" ""); - prof_input("/connect stabber@localhost server 127.0.0.1 port 5230 tls allow"); + int port = current_port; + gchar* connect_cmd = g_strdup_printf("/connect stabber@localhost/profanity server 127.0.0.1 port %d tls disable auth legacy", port); + prof_input(connect_cmd); + g_free(connect_cmd); + + assert_true(prof_output_regex("Enter password:")); + usleep(1000 * 200); // 200ms delay prof_input("password"); + usleep(1000 * 200); // 200ms delay // Allow time for profanity to connect - exp_timeout = 30; - assert_true(prof_output_regex("stabber@localhost/profanity logged in successfully, .+online.+ \\(priority 0\\)\\.")); - exp_timeout = 10; - stbbr_wait_for("prof_presence_*"); + prof_expect_timeout = 30; + assert_true(prof_output_regex("successfully")); + prof_expect_timeout = 10; + stbbr_wait_for("presence:*"); + //usleep(1000 * 2000); // 2s delay } void prof_timeout(int timeout) { - exp_timeout = timeout; + prof_expect_timeout = timeout; } void prof_timeout_reset(void) { - exp_timeout = 10; + prof_expect_timeout = 10; } void diff --git a/tests/functionaltests/proftest.h b/tests/functionaltests/proftest.h index ef1968b1..99bf89c4 100644 --- a/tests/functionaltests/proftest.h +++ b/tests/functionaltests/proftest.h @@ -4,6 +4,15 @@ #define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home" #define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home" +enum prof_expect_type { + prof_expect_end = 0, + prof_expect_exact, + prof_expect_regexp +}; + +extern int prof_expect_timeout; +extern int prof_pid; + int init_prof_test(void** state); int close_prof_test(void** state); @@ -18,4 +27,6 @@ int prof_output_regex(const char* text); void prof_timeout(int timeout); void prof_timeout_reset(void); +int prof_expect(int fd, ...); + #endif diff --git a/tests/functionaltests/start_profanity.sh b/tests/functionaltests/start_profanity.sh index 58ceadd7..5ea37fc6 100755 --- a/tests/functionaltests/start_profanity.sh +++ b/tests/functionaltests/start_profanity.sh @@ -1,2 +1,7 @@ export COLUMNS=300 -./profanity -l DEBUG +if [ -f "./build_run/profanity" ]; then + ./build_run/profanity -l DEBUG +else + # Fallback for autotools + ./profanity -l DEBUG +fi diff --git a/tests/functionaltests/test_carbons.c b/tests/functionaltests/test_carbons.c index 41e9a9f5..aa827212 100644 --- a/tests/functionaltests/test_carbons.c +++ b/tests/functionaltests/test_carbons.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -25,7 +24,6 @@ connect_with_carbons_enabled(void** state) prof_input("/carbons on"); prof_connect(); - assert_true(stbbr_received( "")); } @@ -36,9 +34,10 @@ send_disable_carbons(void** state) prof_input("/carbons on"); prof_connect(); + assert_true(stbbr_received( + "")); prof_input("/carbons off"); - assert_true(stbbr_received( "")); } @@ -47,6 +46,7 @@ void receive_carbon(void** state) { prof_input("/carbons on"); + stbbr_for_xmlns("urn:xmpp:carbons:2", ""); prof_connect(); assert_true(stbbr_received( @@ -65,20 +65,21 @@ receive_carbon(void** state) "" "" "" - "" + "" "test carbon from recipient" "" "" "" ""); - assert_true(prof_output_regex("Buddy1/mobile: .+test carbon from recipient")); + assert_true(prof_output_regex("Buddy1/mobile: test carbon from recipient")); } void receive_self_carbon(void** state) { prof_input("/carbons on"); + stbbr_for_xmlns("urn:xmpp:carbons:2", ""); prof_connect(); assert_true(stbbr_received( @@ -97,20 +98,21 @@ receive_self_carbon(void** state) "" "" "" - "" - "self sent carbon" + "" + "test carbon from sender" "" "" "" ""); - assert_true(prof_output_regex("me: .+self sent carbon")); + assert_true(prof_output_regex("me: test carbon from sender")); } void receive_private_carbon(void** state) { prof_input("/carbons on"); + stbbr_for_xmlns("urn:xmpp:carbons:2", ""); prof_connect(); assert_true(stbbr_received( @@ -126,10 +128,16 @@ receive_private_carbon(void** state) assert_true(prof_output_exact("unencrypted")); stbbr_send( - "" + "" + "" + "" + "" "Private carbon" "" + "" + "" + "" ""); - assert_true(prof_output_regex("Buddy1/mobile: .+Private carbon")); + assert_true(prof_output_regex("Buddy1/mobile: Private carbon")); } diff --git a/tests/functionaltests/test_chat_session.c b/tests/functionaltests/test_chat_session.c index b26a224a..751c9d00 100644 --- a/tests/functionaltests/test_chat_session.c +++ b/tests/functionaltests/test_chat_session.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -30,7 +29,7 @@ sends_message_to_barejid_when_contact_online(void** state) "" "10" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online")); prof_input("/msg buddy1@localhost Hi there"); @@ -49,13 +48,13 @@ sends_message_to_fulljid_when_received_from_fulljid(void** state) "" "10" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online")); stbbr_send( "" "First message" ""); - assert_true(prof_output_exact("<< chat message: Buddy1/mobile (win 2)")); + assert_true(prof_output_regex("chat message: Buddy1/mobile \\(win 2\\)")); prof_input("/msg buddy1@localhost Hi there"); @@ -74,13 +73,13 @@ sends_subsequent_messages_to_fulljid(void** state) "" "10" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online")); stbbr_send( "" "First message" ""); - assert_true(prof_output_exact("<< chat message: Buddy1/mobile (win 2)")); + assert_true(prof_output_regex("chat message: Buddy1/mobile \\(win 2\\)")); prof_input("/msg buddy1@localhost Outgoing 1"); assert_true(stbbr_received( @@ -110,13 +109,13 @@ resets_to_barejid_after_presence_received(void** state) "" "10" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online")); stbbr_send( "" "First message" ""); - assert_true(prof_output_exact("<< chat message: Buddy1/mobile (win 2)")); + assert_true(prof_output_regex("chat message: Buddy1/mobile \\(win 2\\)")); prof_input("/msg buddy1@localhost Outgoing 1"); assert_true(stbbr_received( @@ -124,12 +123,14 @@ resets_to_barejid_after_presence_received(void** state) "Outgoing 1" "")); + prof_input("/presence chat all"); + stbbr_send( "" "5" "dnd" ""); - assert_true(prof_output_exact("Buddy1 (laptop) is dnd")); + assert_true(prof_output_regex("Buddy1 \\(laptop\\) is dnd")); prof_input("/msg buddy1@localhost Outgoing 2"); assert_true(stbbr_received( @@ -147,20 +148,20 @@ new_session_when_message_received_from_different_fulljid(void** state) "" "10" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online")); stbbr_send( "" "8" "away" ""); - assert_true(prof_output_exact("Buddy1 (laptop) is away")); + assert_true(prof_output_regex("Buddy1 \\(laptop\\) is away")); stbbr_send( "" "From first resource" ""); - assert_true(prof_output_exact("<< chat message: Buddy1/mobile (win 2)")); + assert_true(prof_output_regex("chat message: Buddy1/mobile \\(win 2\\)")); prof_input("/msg buddy1@localhost Outgoing 1"); assert_true(stbbr_received( diff --git a/tests/functionaltests/test_connect.c b/tests/functionaltests/test_connect.c index 43f6e98c..d2f55ec1 100644 --- a/tests/functionaltests/test_connect.c +++ b/tests/functionaltests/test_connect.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -60,26 +59,26 @@ connect_shows_presence_updates(void** state) "dnd" "busy!" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is dnd, \"busy!\"")); + assert_true(prof_output_exact("++ Buddy1 (mobile) is dnd, \"busy!\"")); stbbr_send( "" "chat" "Talk to me!" ""); - assert_true(prof_output_exact("Buddy1 (laptop) is chat, \"Talk to me!\"")); + assert_true(prof_output_exact("++ Buddy1 (laptop) is chat, \"Talk to me!\"")); stbbr_send( "" "away" "Out of office" ""); - assert_true(prof_output_exact("Buddy2 (work) is away, \"Out of office\"")); + assert_true(prof_output_exact("++ Buddy2 (work) is away, \"Out of office\"")); stbbr_send( "" "xa" "Gone :(" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is xa, \"Gone :(\"")); + assert_true(prof_output_exact("++ Buddy1 (mobile) is xa, \"Gone :(\"")); } diff --git a/tests/functionaltests/test_disconnect.c b/tests/functionaltests/test_disconnect.c index b90af3cd..d7a8dfa3 100644 --- a/tests/functionaltests/test_disconnect.c +++ b/tests/functionaltests/test_disconnect.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" diff --git a/tests/functionaltests/test_message.c b/tests/functionaltests/test_message.c index 84fb22cb..1bc3760d 100644 --- a/tests/functionaltests/test_message.c +++ b/tests/functionaltests/test_message.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -20,7 +19,7 @@ message_send(void** state) "Hi there" "")); - assert_true(prof_output_regex("me: .+Hi there")); + assert_true(prof_output_exact("me: Hi there")); } void @@ -42,12 +41,11 @@ message_receive_chatwin(void** state) prof_connect(); prof_input("/msg someuser@chatserv.org"); - assert_true(prof_output_exact("someuser@chatserv.org")); stbbr_send( "" "How are you?" ""); - assert_true(prof_output_regex("someuser@chatserv.org/laptop: .+How are you?")); + assert_true(prof_output_regex("someuser@chatserv.org/laptop: How are you\\?")); } diff --git a/tests/functionaltests/test_muc.c b/tests/functionaltests/test_muc.c index 0d1d1e7b..18db45ab 100644 --- a/tests/functionaltests/test_muc.c +++ b/tests/functionaltests/test_muc.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -15,10 +14,10 @@ sends_room_join(void** state) prof_input("/join testroom@conference.localhost"); - assert_true(stbbr_last_received( - "" - "" - "" + assert_true(stbbr_received( + "" + "" + "" "")); } @@ -29,10 +28,10 @@ sends_room_join_with_nick(void** state) prof_input("/join testroom@conference.localhost nick testnick"); - assert_true(stbbr_last_received( - "" - "" - "" + assert_true(stbbr_received( + "" + "" + "" "")); } @@ -43,12 +42,12 @@ sends_room_join_with_password(void** state) prof_input("/join testroom@conference.localhost password testpassword"); - assert_true(stbbr_last_received( - "" - "" + assert_true(stbbr_received( + "" + "" "testpassword" "" - "" + "" "")); } @@ -59,12 +58,12 @@ sends_room_join_with_nick_and_password(void** state) prof_input("/join testroom@conference.localhost nick testnick password testpassword"); - assert_true(stbbr_last_received( - "" - "" + assert_true(stbbr_received( + "" + "" "testpassword" "" - "" + "" "")); } @@ -73,8 +72,8 @@ shows_role_and_affiliation_on_join(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -92,8 +91,8 @@ shows_subject_on_join(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -110,7 +109,7 @@ shows_subject_on_join(void** state) "anothernick has set the subject to: Test room subject" ""); - assert_true(prof_output_regex("Room subject: .+Test room subject")); + assert_true(prof_output_exact("Room subject: Test room subject")); } void @@ -118,8 +117,8 @@ shows_history_message(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -137,16 +136,18 @@ shows_history_message(void** state) "" ""); - assert_true(prof_output_regex("testoccupant: an old message")); + assert_true(prof_output_exact("testoccupant: an old message")); } void shows_occupant_join(void** state) { prof_connect(); + prof_input("/presence room all"); + assert_true(prof_output_exact("All presence updates will appear in chat room windows.")); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -172,8 +173,8 @@ shows_message(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -189,7 +190,7 @@ shows_message(void** state) "a new message" ""); - assert_true(prof_output_regex("testoccupant: .+a new message")); + assert_true(prof_output_exact("testoccupant: a new message")); } void @@ -197,8 +198,8 @@ shows_me_message_from_occupant(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -222,8 +223,8 @@ shows_me_message_from_self(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -247,8 +248,8 @@ shows_all_messages_in_console_when_window_not_focussed(void** state) { prof_connect(); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -285,8 +286,8 @@ shows_first_message_in_console_when_window_not_focussed(void** state) prof_input("/console muc first"); assert_true(prof_output_exact("Console MUC messages set: first")); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" @@ -305,7 +306,7 @@ shows_first_message_in_console_when_window_not_focussed(void** state) "a new message" ""); - assert_true(prof_output_exact("<< room message: testroom@conference.localhost (win 2)")); + assert_true(prof_output_exact("<< room message: testoccupant in testroom@conference.localhost (win 2)")); prof_input("/clear"); prof_input("/about"); assert_true(prof_output_exact("Type '/help' to show complete help.")); @@ -316,7 +317,7 @@ shows_first_message_in_console_when_window_not_focussed(void** state) ""); prof_timeout(2); - assert_false(prof_output_exact("<< room message: testroom@conference.localhost (win 2)")); + assert_false(prof_output_exact("<< room message: anotheroccupant in testroom@conference.localhost (win 2)")); prof_timeout_reset(); } @@ -328,8 +329,8 @@ shows_no_message_in_console_when_window_not_focussed(void** state) prof_input("/console muc none"); assert_true(prof_output_exact("Console MUC messages set: none")); - stbbr_for_id("prof_join_4", - "" + stbbr_for_id("presence:*", + "" "" "" "" diff --git a/tests/functionaltests/test_ping.c b/tests/functionaltests/test_ping.c index 03c14b2b..ed9eeddf 100644 --- a/tests/functionaltests/test_ping.c +++ b/tests/functionaltests/test_ping.c @@ -4,38 +4,35 @@ #include #include -#include #include "proftest.h" void ping_server(void** state) { - stbbr_for_id("prof_disco_info_onconnect_2", - "" + stbbr_for_query("http://jabber.org/protocol/disco#info", + "" "" "" "" "" ""); - stbbr_for_id("prof_ping_4", - ""); - stbbr_for_id("prof_ping_5", - ""); + stbbr_for_xmlns("urn:xmpp:ping", + ""); prof_connect(); prof_input("/ping"); assert_true(stbbr_received( - "" + "" "" "")); assert_true(prof_output_exact("Ping response from server")); prof_input("/ping"); assert_true(stbbr_received( - "" + "" "" "")); assert_true(prof_output_exact("Ping response from server")); @@ -44,8 +41,8 @@ ping_server(void** state) void ping_server_not_supported(void** state) { - stbbr_for_id("prof_disco_info_onconnect_2", - "" + stbbr_for_query("http://jabber.org/protocol/disco#info", + "" "" "" "" @@ -54,7 +51,7 @@ ping_server_not_supported(void** state) prof_connect(); prof_input("/ping"); - assert_true(prof_output_exact("Server does not support ping requests.")); + assert_true(prof_output_exact("Server does not support ping requests (urn:xmpp:ping).")); } void @@ -68,14 +65,14 @@ ping_responds_to_server_request(void** state) ""); assert_true(stbbr_received( - "")); + "")); } void ping_jid(void** state) { - stbbr_for_id("prof_caps_4", - "" + stbbr_for_id("*", + "" "" "" "" @@ -84,6 +81,9 @@ ping_jid(void** state) "" ""); + stbbr_for_xmlns("urn:xmpp:ping", + ""); + prof_connect(); stbbr_send( @@ -97,20 +97,17 @@ ping_jid(void** state) "ver='LpT2xs3nun7jC2sq4gg3WRDQFZ4='" "/>" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\"")); + assert_true(prof_output_exact("++ Buddy1 (mobile) is online, \"I'm here\"")); assert_true(stbbr_received( - "" + "" "" "")); - stbbr_for_id("prof_ping_5", - ""); - prof_input("/ping buddy1@localhost/mobile"); assert_true(stbbr_received( - "" + "" "" "")); assert_true(prof_output_exact("Ping response from buddy1@localhost/mobile")); @@ -119,8 +116,8 @@ ping_jid(void** state) void ping_jid_not_supported(void** state) { - stbbr_for_id("prof_caps_4", - "" + stbbr_for_id("*", + "" "" "" "" @@ -141,10 +138,10 @@ ping_jid_not_supported(void** state) "ver='LpT2xs3nun7jC2sq4gg3WRDQFZ4='" "/>" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\"")); + assert_true(prof_output_exact("++ Buddy1 (mobile) is online, \"I'm here\"")); assert_true(stbbr_received( - "" + "" "" "")); diff --git a/tests/functionaltests/test_presence.c b/tests/functionaltests/test_presence.c index 884e4db6..da48a9c1 100644 --- a/tests/functionaltests/test_presence.c +++ b/tests/functionaltests/test_presence.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -13,11 +12,11 @@ presence_online(void** state) { prof_connect(); - prof_input("/online"); + prof_input("/status set online"); assert_true(stbbr_received( - "" - "" + "" + "" "")); assert_true(prof_output_exact("Status set to online (priority 0)")); @@ -28,12 +27,12 @@ presence_online_with_message(void** state) { prof_connect(); - prof_input("/online \"Hi there\""); + prof_input("/status set online \"Hi there\""); assert_true(stbbr_received( - "" + "" "Hi there" - "" + "" "")); assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\".")); @@ -44,12 +43,12 @@ presence_away(void** state) { prof_connect(); - prof_input("/away"); + prof_input("/status set away"); assert_true(stbbr_received( - "" + "" "away" - "" + "" "")); assert_true(prof_output_exact("Status set to away (priority 0)")); @@ -60,13 +59,13 @@ presence_away_with_message(void** state) { prof_connect(); - prof_input("/away \"I'm not here for a bit\""); + prof_input("/status set away \"I'm not here for a bit\""); assert_true(stbbr_received( - "" + "" "away" "I'm not here for a bit" - "" + "" "")); assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\".")); @@ -77,12 +76,12 @@ presence_xa(void** state) { prof_connect(); - prof_input("/xa"); + prof_input("/status set xa"); assert_true(stbbr_received( - "" + "" "xa" - "" + "" "")); assert_true(prof_output_exact("Status set to xa (priority 0)")); @@ -93,13 +92,13 @@ presence_xa_with_message(void** state) { prof_connect(); - prof_input("/xa \"Gone to the shops\""); + prof_input("/status set xa \"Gone to the shops\""); assert_true(stbbr_received( - "" + "" "xa" "Gone to the shops" - "" + "" "")); assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\".")); @@ -110,12 +109,12 @@ presence_dnd(void** state) { prof_connect(); - prof_input("/dnd"); + prof_input("/status set dnd"); assert_true(stbbr_received( - "" + "" "dnd" - "" + "" "")); assert_true(prof_output_exact("Status set to dnd (priority 0)")); @@ -126,13 +125,13 @@ presence_dnd_with_message(void** state) { prof_connect(); - prof_input("/dnd \"Working\""); + prof_input("/status set dnd \"Working\""); assert_true(stbbr_received( - "" + "" "dnd" "Working" - "" + "" "")); assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\".")); @@ -143,12 +142,12 @@ presence_chat(void** state) { prof_connect(); - prof_input("/chat"); + prof_input("/status set chat"); assert_true(stbbr_received( - "" + "" "chat" - "" + "" "")); assert_true(prof_output_exact("Status set to chat (priority 0)")); @@ -159,13 +158,13 @@ presence_chat_with_message(void** state) { prof_connect(); - prof_input("/chat \"Free to talk\""); + prof_input("/status set chat \"Free to talk\""); assert_true(stbbr_received( - "" + "" "chat" "Free to talk" - "" + "" "")); assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\".")); @@ -179,9 +178,9 @@ presence_set_priority(void** state) prof_input("/priority 25"); assert_true(stbbr_received( - "" + "" "25" - "" + "" "")); assert_true(prof_output_exact("Priority set to 25.")); @@ -194,19 +193,19 @@ presence_includes_priority(void** state) prof_input("/priority 25"); assert_true(stbbr_received( - "" + "" "25" - "" + "" "")); assert_true(prof_output_exact("Priority set to 25.")); - prof_input("/chat \"Free to talk\""); + prof_input("/status set chat \"Free to talk\""); assert_true(stbbr_received( - "" + "" "25" "chat" "Free to talk" - "" + "" "")); assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\".")); } @@ -216,22 +215,22 @@ presence_keeps_status(void** state) { prof_connect(); - prof_input("/chat \"Free to talk\""); + prof_input("/status set chat \"Free to talk\""); assert_true(stbbr_received( - "" + "" "chat" "Free to talk" - "" + "" "")); assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\".")); prof_input("/priority 25"); assert_true(stbbr_received( - "" + "" "chat" "Free to talk" "25" - "" + "" "")); assert_true(prof_output_exact("Priority set to 25.")); } @@ -247,7 +246,7 @@ presence_received(void** state) "I'm here" ""); - assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\"")); + assert_true(prof_output_regex("Buddy1 \\(mobile\\) is online, \"I'm here\"")); } // Typical use case for gateways that don't support resources diff --git a/tests/functionaltests/test_receipts.c b/tests/functionaltests/test_receipts.c index cb8485e8..a8efe546 100644 --- a/tests/functionaltests/test_receipts.c +++ b/tests/functionaltests/test_receipts.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" @@ -30,8 +29,8 @@ send_receipt_request(void** state) prof_connect(); - stbbr_for_id("prof_caps_4", - "" + stbbr_for_id("iq:*", + "" "" "" "" @@ -45,7 +44,7 @@ send_receipt_request(void** state) "" ""); - prof_output_exact("Buddy1 is online, \"My status\""); + assert_true(prof_output_regex("Buddy1 \\(laptop\\) is online, \"My status\"")); prof_input("/msg Buddy1"); prof_input("/resource set laptop"); diff --git a/tests/functionaltests/test_rooms.c b/tests/functionaltests/test_rooms.c index 37348e10..2097a56c 100644 --- a/tests/functionaltests/test_rooms.c +++ b/tests/functionaltests/test_rooms.c @@ -4,15 +4,14 @@ #include #include -#include #include "proftest.h" void rooms_query(void** state) { - stbbr_for_id("prof_confreq_4", - "" + stbbr_for_query("http://jabber.org/protocol/disco#items", + "" "" "" "" @@ -23,11 +22,10 @@ rooms_query(void** state) prof_input("/rooms service conference.localhost"); - assert_true(prof_output_exact("chatroom@conference.localhost (A chat room)")); - assert_true(prof_output_exact("hangout@conference.localhost (Another chat room)")); + assert_true(prof_output_regex("Room list request sent: conference.localhost[\\s\\S]*chatroom@conference.localhost[\\s\\S]*hangout@conference.localhost")); assert_true(stbbr_last_received( - "" + "" "" "")); } diff --git a/tests/functionaltests/test_roster.c b/tests/functionaltests/test_roster.c index 46875b2a..919024b8 100644 --- a/tests/functionaltests/test_roster.c +++ b/tests/functionaltests/test_roster.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h" diff --git a/tests/functionaltests/test_software.c b/tests/functionaltests/test_software.c index be8f3ebd..9bdbda5a 100644 --- a/tests/functionaltests/test_software.c +++ b/tests/functionaltests/test_software.c @@ -4,7 +4,6 @@ #include #include -#include #include "proftest.h"