Skip to content

Commit 3ed4aa8

Browse files
authored
Optimize unit tests by parallelizing them (#590)
Split long tests into subtests and use all available CPUs to run them.
1 parent ad8ac20 commit 3ed4aa8

File tree

3 files changed

+121
-32
lines changed

3 files changed

+121
-32
lines changed

CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,25 @@ IF(LSQUIC_TESTS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
402402
# enabled.
403403
#
404404
enable_testing()
405+
406+
# Enable parallel test execution with CTest
407+
include(ProcessorCount)
408+
ProcessorCount(N)
409+
if(NOT N EQUAL 0)
410+
message(STATUS "CTest will run tests in parallel with ${N} jobs by default")
411+
412+
# Add a serial test target
413+
add_custom_target(test-serial
414+
COMMAND ${CMAKE_CTEST_COMMAND} --force-new-ctest-process
415+
COMMENT "Running tests serially"
416+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
417+
)
418+
419+
# Set the default test arguments to use parallel execution
420+
# This works by setting CTEST_TEST_ARGS which is used by the built-in test target
421+
set(CMAKE_CTEST_ARGUMENTS "-j${N};--force-new-ctest-process")
422+
endif()
423+
405424
add_subdirectory(tests)
406425
ENDIF()
407426

tests/CMakeLists.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ IF (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
8282
SET(TESTS ${TESTS} ack_merge)
8383
# No open_memstream() on Windows
8484
SET(TESTS ${TESTS} hcsi_reader)
85-
# Takes forever on Windows, for whatever reason. Or maybe it's the
86-
# MS C compilers. Something to investigate... later.
87-
LIST(APPEND TESTS h3_framing)
85+
# h3_framing test is split into subsets for parallel execution
86+
# The main test is not added to TESTS list, but handled separately below
8887
ENDIF()
8988

9089

@@ -120,6 +119,23 @@ ADD_TEST(stream_hash test_stream -h)
120119
ADD_TEST(stream_A test_stream -A)
121120
ADD_TEST(stream_hash_A test_stream -A -h)
122121

122+
# Split h3_framing test into subsets for parallel execution
123+
IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
124+
ADD_EXECUTABLE(test_h3_framing test_h3_framing.c ${ADDL_SOURCES})
125+
TARGET_LINK_LIBRARIES(test_h3_framing ${LIBS} ${LIB_FLAGS})
126+
# Split pwritev test by combo index (6 combos, each ~207k iterations)
127+
ADD_TEST(h3_framing_pwritev_combo0 test_h3_framing -s 0)
128+
ADD_TEST(h3_framing_pwritev_combo1 test_h3_framing -s 1)
129+
ADD_TEST(h3_framing_pwritev_combo2 test_h3_framing -s 2)
130+
ADD_TEST(h3_framing_pwritev_combo3 test_h3_framing -s 3)
131+
ADD_TEST(h3_framing_pwritev_combo4 test_h3_framing -s 4)
132+
ADD_TEST(h3_framing_pwritev_combo5 test_h3_framing -s 5)
133+
# Other h3_framing subtests
134+
ADD_TEST(h3_framing_hq test_h3_framing -s 10)
135+
ADD_TEST(h3_framing_header_split test_h3_framing -s 11)
136+
ADD_TEST(h3_framing_zero_size test_h3_framing -s 12)
137+
ENDIF()
138+
123139
IF(NOT MSVC)
124140
ADD_EXECUTABLE(graph_cubic graph_cubic.c ${ADDL_SOURCES})
125141
TARGET_LINK_LIBRARIES(graph_cubic ${LIBS})

tests/test_h3_framing.c

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,8 +1027,18 @@ test_pwritev (enum lsquic_version version, int http, int sched_immed,
10271027
}
10281028

10291029

1030+
static const struct { unsigned iovecs, frames; } combos[] =
1031+
{
1032+
{ 32, 16, },
1033+
{ 16, 16, },
1034+
{ 16, 8, },
1035+
{ 3, 7, },
1036+
{ 7, 3, },
1037+
{ 100, 100, },
1038+
};
1039+
10301040
static void
1031-
main_test_pwritev (void)
1041+
main_test_pwritev_combo (int combo_start, int combo_end)
10321042
{
10331043
const int limits[] = { INT_MAX, -1, -2, -3, -7, -10, -50, -100, -201, -211,
10341044
-1000, -2003, -3000, -4000, -17803, -20000, 16 * 1024, 16 * 1024 - 1,
@@ -1039,41 +1049,39 @@ main_test_pwritev (void)
10391049
unsigned i, j, k;
10401050
enum lsquic_version version;
10411051
int http, sched_immed;
1042-
const struct { unsigned iovecs, frames; } combos[] =
1043-
{
1044-
{ 32, 16, },
1045-
{ 16, 16, },
1046-
{ 16, 8, },
1047-
{ 3, 7, },
1048-
{ 7, 3, },
1049-
{ 100, 100, },
1050-
}, *combo = combos;
1052+
int combo_idx;
10511053

10521054
s_can_write_ack = 1;
10531055

1054-
run_test:
1055-
for (version = 0; version < N_LSQVER; ++version)
1056-
if ((1 << version) & LSQUIC_SUPPORTED_VERSIONS)
1057-
for (http = 0; http < 2; ++http)
1058-
for (sched_immed = 0; sched_immed <= 1; ++sched_immed)
1059-
for (i = 0; i < sizeof(limits) / sizeof(limits[i]); ++i)
1060-
for (j = 0; j < sizeof(packet_sz) / sizeof(packet_sz[0]);
1061-
++j)
1062-
for (k = 0; k < sizeof(prologues) / sizeof(prologues[0]); ++k)
1063-
for (n_packets = 1; n_packets < 21; ++n_packets)
1064-
test_pwritev(version, http, sched_immed,
1065-
limits[i], packet_sz[j], prologues[k], n_packets);
1066-
1067-
if (combo < combos + sizeof(combos) / sizeof(combos[0]))
1056+
for (combo_idx = combo_start; combo_idx < combo_end; ++combo_idx)
10681057
{
1069-
lsquic_stream_set_pwritev_params(combo->iovecs, combo->frames);
1070-
++combo;
1071-
goto run_test;
1058+
if (combo_idx < (int)(sizeof(combos) / sizeof(combos[0])))
1059+
{
1060+
lsquic_stream_set_pwritev_params(combos[combo_idx].iovecs, combos[combo_idx].frames);
1061+
1062+
for (version = 0; version < N_LSQVER; ++version)
1063+
if ((1 << version) & LSQUIC_SUPPORTED_VERSIONS)
1064+
for (http = 0; http < 2; ++http)
1065+
for (sched_immed = 0; sched_immed <= 1; ++sched_immed)
1066+
for (i = 0; i < sizeof(limits) / sizeof(limits[i]); ++i)
1067+
for (j = 0; j < sizeof(packet_sz) / sizeof(packet_sz[0]); ++j)
1068+
for (k = 0; k < sizeof(prologues) / sizeof(prologues[0]); ++k)
1069+
for (n_packets = 1; n_packets < 21; ++n_packets)
1070+
test_pwritev(version, http, sched_immed,
1071+
limits[i], packet_sz[j], prologues[k], n_packets);
1072+
}
10721073
}
10731074

10741075
s_can_write_ack = 0;
10751076
}
10761077

1078+
static void
1079+
main_test_pwritev (void)
1080+
{
1081+
/* Run all combos */
1082+
main_test_pwritev_combo(0, sizeof(combos) / sizeof(combos[0]));
1083+
}
1084+
10771085

10781086
/* Instead of the not-very-random testing done in main_test_pwritev(),
10791087
* the fuzz-guided testing initializes parameters based on the fuzz input
@@ -1622,12 +1630,12 @@ main (int argc, char **argv)
16221630
{
16231631
const char *fuzz_hq_framing_input = NULL;
16241632
const char *fuzz_pwritev_input = NULL;
1625-
int opt, add_one_more;
1633+
int opt, add_one_more, test_subset = -1;
16261634
unsigned n_packets, extra_sz;
16271635

16281636
lsquic_global_init(LSQUIC_GLOBAL_SERVER);
16291637

1630-
while (-1 != (opt = getopt(argc, argv, "f:p:l:")))
1638+
while (-1 != (opt = getopt(argc, argv, "f:p:l:s:")))
16311639
{
16321640
switch (opt)
16331641
{
@@ -1641,6 +1649,9 @@ main (int argc, char **argv)
16411649
lsquic_log_to_fstream(stderr, LLTS_NONE);
16421650
lsquic_logger_lopt(optarg);
16431651
break;
1652+
case 's':
1653+
test_subset = atoi(optarg);
1654+
break;
16441655
default:
16451656
exit(1);
16461657
}
@@ -1652,6 +1663,49 @@ main (int argc, char **argv)
16521663
fuzz_guided_hq_framing_testing(fuzz_hq_framing_input);
16531664
else if (fuzz_pwritev_input)
16541665
fuzz_guided_pwritev_testing(fuzz_pwritev_input);
1666+
else if (test_subset >= 0)
1667+
{
1668+
/* Run a specific subset of tests for parallel execution */
1669+
switch (test_subset)
1670+
{
1671+
case 0: /* pwritev combo 0 (32 iovecs, 16 frames) */
1672+
main_test_pwritev_combo(0, 1);
1673+
break;
1674+
case 1: /* pwritev combo 1 (16 iovecs, 16 frames) */
1675+
main_test_pwritev_combo(1, 2);
1676+
break;
1677+
case 2: /* pwritev combo 2 (16 iovecs, 8 frames) */
1678+
main_test_pwritev_combo(2, 3);
1679+
break;
1680+
case 3: /* pwritev combo 3 (3 iovecs, 7 frames) */
1681+
main_test_pwritev_combo(3, 4);
1682+
break;
1683+
case 4: /* pwritev combo 4 (7 iovecs, 3 frames) */
1684+
main_test_pwritev_combo(4, 5);
1685+
break;
1686+
case 5: /* pwritev combo 5 (100 iovecs, 100 frames) */
1687+
main_test_pwritev_combo(5, 6);
1688+
break;
1689+
case 10: /* hq_framing tests */
1690+
main_test_hq_framing();
1691+
break;
1692+
case 11: /* frame header split tests */
1693+
for (n_packets = 1; n_packets <= 2; ++n_packets)
1694+
for (extra_sz = 0; extra_sz <= 2; ++extra_sz)
1695+
for (add_one_more = 0; add_one_more <= 1; ++add_one_more)
1696+
test_frame_header_split(n_packets, extra_sz, add_one_more);
1697+
break;
1698+
case 12: /* zero size frame tests */
1699+
test_zero_size_frame();
1700+
test_reading_zero_size_data_frame();
1701+
test_reading_zero_size_data_frame_scenario2();
1702+
test_reading_zero_size_data_frame_scenario3();
1703+
break;
1704+
default:
1705+
fprintf(stderr, "Unknown test subset: %d\n", test_subset);
1706+
exit(1);
1707+
}
1708+
}
16551709
else
16561710
{
16571711
main_test_pwritev();

0 commit comments

Comments
 (0)