feat(tests): enable parallel execution of functional tests
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 34s
CI Code / Linux (ubuntu) (pull_request) Successful in 30m9s
CI Code / Linux (arch) (pull_request) Successful in 33m47s
CI Code / Linux (debian) (pull_request) Successful in 33m9s
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 34s
CI Code / Linux (ubuntu) (pull_request) Successful in 30m9s
CI Code / Linux (arch) (pull_request) Successful in 33m47s
CI Code / Linux (debian) (pull_request) Successful in 33m9s
This commit is contained in:
17
Makefile.am
17
Makefile.am
@@ -305,6 +305,23 @@ tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
|
||||
tests_functionaltests_functionaltests_CPPFLAGS = -Itests/
|
||||
tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS)
|
||||
tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber @FORKPTY_LIB@
|
||||
|
||||
# Parallel functional tests target (~3x faster than sequential)
|
||||
# Usage: make check-functional-parallel
|
||||
check-functional-parallel: tests/functionaltests/functionaltests
|
||||
@echo "Running functional tests in parallel (4 groups)..."
|
||||
@mkdir -p $(builddir)/test-logs
|
||||
@( \
|
||||
./tests/functionaltests/functionaltests 1 > $(builddir)/test-logs/group1.log 2>&1 & \
|
||||
./tests/functionaltests/functionaltests 2 > $(builddir)/test-logs/group2.log 2>&1 & \
|
||||
./tests/functionaltests/functionaltests 3 > $(builddir)/test-logs/group3.log 2>&1 & \
|
||||
./tests/functionaltests/functionaltests 4 > $(builddir)/test-logs/group4.log 2>&1 & \
|
||||
wait \
|
||||
) && ( \
|
||||
echo "=== Test Results ==="; \
|
||||
grep -E 'PASSED|FAILED|Running' $(builddir)/test-logs/group*.log; \
|
||||
! grep -q FAILED $(builddir)/test-logs/group*.log \
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ do
|
||||
./configure $features $*
|
||||
|
||||
$MAKE CC="${CC}"
|
||||
$MAKE check
|
||||
$MAKE check-functional-parallel
|
||||
|
||||
./profanity -v
|
||||
$MAKE clean
|
||||
|
||||
@@ -14,16 +14,28 @@
|
||||
* flaky tests caused by leftover state. The overhead is acceptable since
|
||||
* functional tests run less frequently than unit tests.
|
||||
*
|
||||
* Tests are organized into groups for better maintainability:
|
||||
* Group 1: Connection, Ping, Rooms, Presence
|
||||
* Group 2: Messages, Receipts, Roster management
|
||||
* Group 3: MUC (Multi-User Chat) functionality
|
||||
* Group 4: Carbons, Chat sessions, Software version, Disconnect
|
||||
* Tests are organized into groups for better maintainability and parallel execution:
|
||||
* Group 1: Connect, Ping, Rooms, Software (17 tests)
|
||||
* Group 2: Message, Receipts, Roster, Chat Session (17 tests)
|
||||
* Group 3: Presence (16 tests)
|
||||
* Group 4: MUC, Carbons (19 tests)
|
||||
*
|
||||
* Parallel execution:
|
||||
* ./functionaltests - run all tests sequentially
|
||||
* ./functionaltests 1 - run group 1 only
|
||||
* ./functionaltests 2 - run group 2 only
|
||||
* ./functionaltests 3 - run group 3 only
|
||||
* ./functionaltests 4 - run group 4 only
|
||||
*
|
||||
* For parallel execution, run multiple groups simultaneously:
|
||||
* ./functionaltests 1 & ./functionaltests 2 & ./functionaltests 3 & ./functionaltests 4 & wait
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -49,14 +61,21 @@
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
const struct CMUnitTest all_tests[] = {
|
||||
int group = 0; /* 0 = all groups */
|
||||
if (argc > 1) {
|
||||
group = atoi(argv[1]);
|
||||
if (group < 1 || group > 4) {
|
||||
fprintf(stderr, "Usage: %s [group]\n", argv[0]);
|
||||
fprintf(stderr, " group: 1-4 to run specific group, or omit for all\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 1: Connect, Ping, Rooms, Presence
|
||||
* Basic XMPP session establishment and presence management
|
||||
* ============================================================ */
|
||||
/* GROUP 1: Connect, Ping, Rooms, Software (17 tests)
|
||||
* Basic XMPP session establishment and server queries */
|
||||
|
||||
/* Connection tests - verify login, roster, bookmarks */
|
||||
/* Connection tests - verify login, roster, bookmarks */
|
||||
const struct CMUnitTest group1_tests[] = {
|
||||
PROF_FUNC_TEST(connect_jid_requests_roster),
|
||||
PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster),
|
||||
PROF_FUNC_TEST(connect_jid_requests_bookmarks),
|
||||
@@ -73,27 +92,18 @@ main(int argc, char* argv[])
|
||||
/* Room discovery - XEP-0045 */
|
||||
PROF_FUNC_TEST(rooms_query),
|
||||
|
||||
/* Presence tests - online/away/xa/dnd/chat status */
|
||||
PROF_FUNC_TEST(presence_online),
|
||||
PROF_FUNC_TEST(presence_online_with_message),
|
||||
PROF_FUNC_TEST(presence_away),
|
||||
PROF_FUNC_TEST(presence_away_with_message),
|
||||
PROF_FUNC_TEST(presence_xa),
|
||||
PROF_FUNC_TEST(presence_xa_with_message),
|
||||
PROF_FUNC_TEST(presence_dnd),
|
||||
PROF_FUNC_TEST(presence_dnd_with_message),
|
||||
PROF_FUNC_TEST(presence_chat),
|
||||
PROF_FUNC_TEST(presence_chat_with_message),
|
||||
PROF_FUNC_TEST(presence_set_priority),
|
||||
PROF_FUNC_TEST(presence_includes_priority),
|
||||
PROF_FUNC_TEST(presence_keeps_status),
|
||||
PROF_FUNC_TEST(presence_received),
|
||||
PROF_FUNC_TEST(presence_missing_resource_defaults),
|
||||
/* Software Version - XEP-0092 */
|
||||
PROF_FUNC_TEST(send_software_version_request),
|
||||
PROF_FUNC_TEST(display_software_version_result),
|
||||
PROF_FUNC_TEST(shows_message_when_software_version_error),
|
||||
PROF_FUNC_TEST(display_software_version_result_when_from_domainpart),
|
||||
PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
|
||||
PROF_FUNC_TEST(display_software_version_result_in_chat),
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 2: Message, Receipts, Roster
|
||||
* Core messaging and contact management
|
||||
* ============================================================ */
|
||||
/* GROUP 2: Message, Receipts, Roster, Chat Session (17 tests)
|
||||
* Core messaging and contact management */
|
||||
const struct CMUnitTest group2_tests[] = {
|
||||
|
||||
/* Basic message send/receive */
|
||||
PROF_FUNC_TEST(message_send),
|
||||
@@ -112,21 +122,51 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(sends_remove_item_nick),
|
||||
PROF_FUNC_TEST(sends_nick_change),
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 3: MUC (Multi-User Chat)
|
||||
* XEP-0045 conference room functionality
|
||||
* ============================================================ */
|
||||
/* Chat session management - bare/full JID routing */
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
|
||||
PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
|
||||
PROF_FUNC_TEST(sends_subsequent_messages_to_fulljid),
|
||||
PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
|
||||
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
|
||||
};
|
||||
|
||||
/* Room join with various options */
|
||||
/* GROUP 3: Presence (16 tests)
|
||||
* Online/away/xa/dnd/chat status management */
|
||||
const struct CMUnitTest group3_tests[] = {
|
||||
PROF_FUNC_TEST(presence_online),
|
||||
PROF_FUNC_TEST(presence_online_with_message),
|
||||
PROF_FUNC_TEST(presence_away),
|
||||
PROF_FUNC_TEST(presence_away_with_message),
|
||||
PROF_FUNC_TEST(presence_xa),
|
||||
PROF_FUNC_TEST(presence_xa_with_message),
|
||||
PROF_FUNC_TEST(presence_dnd),
|
||||
PROF_FUNC_TEST(presence_dnd_with_message),
|
||||
PROF_FUNC_TEST(presence_chat),
|
||||
PROF_FUNC_TEST(presence_chat_with_message),
|
||||
PROF_FUNC_TEST(presence_set_priority),
|
||||
PROF_FUNC_TEST(presence_includes_priority),
|
||||
PROF_FUNC_TEST(presence_keeps_status),
|
||||
PROF_FUNC_TEST(presence_received),
|
||||
PROF_FUNC_TEST(presence_missing_resource_defaults),
|
||||
|
||||
/* Disconnect - clean session termination */
|
||||
PROF_FUNC_TEST(disconnect_ends_session),
|
||||
};
|
||||
|
||||
/* GROUP 4: MUC, Carbons (19 tests)
|
||||
* Multi-user chat and message synchronization */
|
||||
const struct CMUnitTest group4_tests[] = {
|
||||
|
||||
/* MUC room join with various options - XEP-0045 */
|
||||
PROF_FUNC_TEST(sends_room_join),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick),
|
||||
PROF_FUNC_TEST(sends_room_join_with_password),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
|
||||
|
||||
/* Room information display */
|
||||
/* MUC room information display */
|
||||
PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
|
||||
PROF_FUNC_TEST(shows_subject_on_join),
|
||||
// PROF_FUNC_TEST(shows_history_message), // temporarily disabled due to timing issues in CI
|
||||
PROF_FUNC_TEST(shows_occupant_join),
|
||||
|
||||
/* MUC messaging */
|
||||
@@ -134,16 +174,11 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(shows_me_message_from_occupant),
|
||||
PROF_FUNC_TEST(shows_me_message_from_self),
|
||||
|
||||
/* Console notification settings for MUC */
|
||||
/* MUC console notification settings */
|
||||
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 4: Carbons, Chat Session, Software, Disconnect
|
||||
* Message synchronization and session management
|
||||
* ============================================================ */
|
||||
|
||||
/* Message Carbons - XEP-0280 (message sync across devices) */
|
||||
PROF_FUNC_TEST(send_enable_carbons),
|
||||
PROF_FUNC_TEST(connect_with_carbons_enabled),
|
||||
@@ -151,26 +186,31 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(receive_carbon),
|
||||
PROF_FUNC_TEST(receive_self_carbon),
|
||||
PROF_FUNC_TEST(receive_private_carbon),
|
||||
|
||||
/* Chat session management - bare/full JID routing */
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
|
||||
PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
|
||||
PROF_FUNC_TEST(sends_subsequent_messages_to_fulljid),
|
||||
PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
|
||||
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
|
||||
|
||||
/* Software Version - XEP-0092 */
|
||||
PROF_FUNC_TEST(send_software_version_request),
|
||||
PROF_FUNC_TEST(display_software_version_result),
|
||||
PROF_FUNC_TEST(shows_message_when_software_version_error),
|
||||
PROF_FUNC_TEST(display_software_version_result_when_from_domainpart),
|
||||
PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
|
||||
PROF_FUNC_TEST(display_software_version_result_in_chat),
|
||||
|
||||
/* Disconnect - clean session termination */
|
||||
PROF_FUNC_TEST(disconnect_ends_session),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(all_tests, NULL, NULL);
|
||||
int result = 0;
|
||||
|
||||
switch (group) {
|
||||
case 1:
|
||||
result = cmocka_run_group_tests_name("Group 1: Connect/Ping/Rooms/Software", group1_tests, NULL, NULL);
|
||||
break;
|
||||
case 2:
|
||||
result = cmocka_run_group_tests_name("Group 2: Message/Receipts/Roster/Session", group2_tests, NULL, NULL);
|
||||
break;
|
||||
case 3:
|
||||
result = cmocka_run_group_tests_name("Group 3: Presence", group3_tests, NULL, NULL);
|
||||
break;
|
||||
case 4:
|
||||
result = cmocka_run_group_tests_name("Group 4: MUC/Carbons", group4_tests, NULL, NULL);
|
||||
break;
|
||||
default:
|
||||
/* Run all groups sequentially */
|
||||
result |= cmocka_run_group_tests_name("Group 1: Connect/Ping/Rooms/Software", group1_tests, NULL, NULL);
|
||||
result |= cmocka_run_group_tests_name("Group 2: Message/Receipts/Roster/Session", group2_tests, NULL, NULL);
|
||||
result |= cmocka_run_group_tests_name("Group 3: Presence", group3_tests, NULL, NULL);
|
||||
result |= cmocka_run_group_tests_name("Group 4: MUC/Carbons", group4_tests, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ int fd = 0;
|
||||
int stub_port = 5230;
|
||||
pid_t child_pid = 0;
|
||||
|
||||
/*
|
||||
* Dynamic XDG paths based on stub_port for parallel test execution.
|
||||
* Each test instance gets unique directories to avoid file conflicts.
|
||||
*/
|
||||
char xdg_config_home[256];
|
||||
char xdg_data_home[256];
|
||||
|
||||
/*
|
||||
* Buffer for accumulating output from profanity.
|
||||
* 64KB is sufficient for typical test output while keeping memory usage
|
||||
@@ -77,7 +84,7 @@ _mkdir_recursive(const char *dir)
|
||||
void
|
||||
_create_config_dir(void)
|
||||
{
|
||||
GString *profanity_dir = g_string_new(XDG_CONFIG_HOME);
|
||||
GString *profanity_dir = g_string_new(xdg_config_home);
|
||||
g_string_append(profanity_dir, "/profanity");
|
||||
|
||||
if (!_mkdir_recursive(profanity_dir->str)) {
|
||||
@@ -90,7 +97,7 @@ _create_config_dir(void)
|
||||
void
|
||||
_create_data_dir(void)
|
||||
{
|
||||
GString *profanity_dir = g_string_new(XDG_DATA_HOME);
|
||||
GString *profanity_dir = g_string_new(xdg_data_home);
|
||||
g_string_append(profanity_dir, "/profanity");
|
||||
|
||||
if (!_mkdir_recursive(profanity_dir->str)) {
|
||||
@@ -103,7 +110,7 @@ _create_data_dir(void)
|
||||
void
|
||||
_create_chatlogs_dir(void)
|
||||
{
|
||||
GString *chatlogs_dir = g_string_new(XDG_DATA_HOME);
|
||||
GString *chatlogs_dir = g_string_new(xdg_data_home);
|
||||
g_string_append(chatlogs_dir, "/profanity/chatlogs");
|
||||
|
||||
if (!_mkdir_recursive(chatlogs_dir->str)) {
|
||||
@@ -116,7 +123,7 @@ _create_chatlogs_dir(void)
|
||||
void
|
||||
_create_logs_dir(void)
|
||||
{
|
||||
GString *logs_dir = g_string_new(XDG_DATA_HOME);
|
||||
GString *logs_dir = g_string_new(xdg_data_home);
|
||||
g_string_append(logs_dir, "/profanity/logs");
|
||||
|
||||
if (!_mkdir_recursive(logs_dir->str)) {
|
||||
@@ -129,7 +136,9 @@ _create_logs_dir(void)
|
||||
void
|
||||
_cleanup_dirs(void)
|
||||
{
|
||||
int res = system("rm -rf ./tests/functionaltests/files");
|
||||
char cmd[512];
|
||||
snprintf(cmd, sizeof(cmd), "rm -rf ./tests/functionaltests/files/%d", stub_port);
|
||||
int res = system(cmd);
|
||||
if (res == -1) {
|
||||
assert_true(FALSE);
|
||||
}
|
||||
@@ -231,14 +240,20 @@ init_prof_test(void **state)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate unique XDG paths based on stub_port for parallel execution
|
||||
snprintf(xdg_config_home, sizeof(xdg_config_home),
|
||||
"./tests/functionaltests/files/%d/xdg_config_home", stub_port);
|
||||
snprintf(xdg_data_home, sizeof(xdg_data_home),
|
||||
"./tests/functionaltests/files/%d/xdg_data_home", stub_port);
|
||||
|
||||
// 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);
|
||||
setenv("XDG_CONFIG_HOME", xdg_config_home, 1);
|
||||
setenv("XDG_DATA_HOME", xdg_data_home, 1);
|
||||
|
||||
_cleanup_dirs();
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#ifndef __H_PROFTEST
|
||||
#define __H_PROFTEST
|
||||
|
||||
#define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home"
|
||||
#define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home"
|
||||
/*
|
||||
* XDG paths are now dynamic, generated per-test based on stub_port.
|
||||
* This allows parallel test execution without file conflicts.
|
||||
* Use xdg_config_home and xdg_data_home variables instead of macros.
|
||||
*/
|
||||
extern char xdg_config_home[256];
|
||||
extern char xdg_data_home[256];
|
||||
|
||||
extern int stub_port;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user