diff --git a/src/scheduler/meson.build b/src/scheduler/meson.build index f5d3f2ff..e8dea613 100644 --- a/src/scheduler/meson.build +++ b/src/scheduler/meson.build @@ -193,4 +193,28 @@ sched_sim_isolation_lib = static_library( dependencies: scheduler_deps, install: false) +# Build-time dedup: every testsuite target that links the real scheduler +# source list (the sched_unit_test aggregate plus the zero-#include +# standalone tests, seventeen targets in all) used to compile all ~50 of +# these files as its own TUs -- ~800 redundant compiles of the same sources +# in one clean build. This archive compiles them once; those tests link it +# instead. Only extern-API consumers may use it: a test that #includes a +# src/*.c textually (white-box statics access) defines those symbols in its +# own TU and could pull the same archive member in for a duplicate-symbol +# link error, so the textual-includer tests keep their own compilation of +# the full list. -Wl,--wrap= interposition is unaffected (link-time symbol +# redirection works identically against archive members). +sched_srcs_lib_srcs = [] +foreach f : scheduler_srcs + if f != 'src/main.c' + sched_srcs_lib_srcs += [f] + endif +endforeach +sched_srcs_lib = static_library( + 'sched_srcs', + sched_srcs_lib_srcs, + include_directories: incdir, + dependencies: scheduler_deps, + install: false) + subdir('testsuite') diff --git a/src/scheduler/testsuite/meson.build b/src/scheduler/testsuite/meson.build index b4fbe3c9..5b32c892 100644 --- a/src/scheduler/testsuite/meson.build +++ b/src/scheduler/testsuite/meson.build @@ -42,17 +42,18 @@ scheduler_test = executable('sched_test', # each statically linking all of scheduler_srcs -- 120+ executables whose # compile+link dominated the suite wall clock. This suite aggregates them # into ONE process (same pattern as controller/testsuite/controller_test.c): -# the production scheduler sources are compiled once here as separate TUs, -# each test file keeps only its own suite entry (void test_sched_*(void) -# { UnitySetTestFile(__FILE__); RUN_TEST(...); }), and +# the production scheduler sources come in via sched_srcs_lib (compiled once +# for the whole testsuite; these tests only call extern APIs declared in +# scheduler.h/core.h), each test file keeps only its own suite entry (void +# test_sched_*(void) { UnitySetTestFile(__FILE__); RUN_TEST(...); }), and # sched_unit_runner.c provides main()/setUp()/tearDown() dispatching through # its SUITES registry. The per-file transformations were done by hand, one # file at a time, with every file compiled+run in the aggregate before the # next was touched. # # Only tests with NO textual #include of ../src/*.c qualify: a test that -# #includes a scheduler source textually duplicates its symbols and cannot -# coexist with the aggregate's own real TUs (those tests stay standalone, +# #includes a scheduler source textually defines those symbols in its own TU +# and could collide with the archive's members (those tests stay standalone, # exactly as before). Tests needing -Wl,--wrap= link interposition, # #define-shaping of the allocator, or that spawn XCE_SCHEDULER_BIN as a # subprocess also stay standalone for the same reason. @@ -66,11 +67,6 @@ scheduler_test = executable('sched_test', # link_with here would duplicate the aggregate's own core/sim TUs -- # instead `depends:` orders their build). sched_unit_test_srcs = ['sched_unit_runner.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_unit_test_srcs += ['../' + f] - endif -endforeach foreach f : ['test_sched_election_null_schedobj.c', 'test_picker_iface.c', 'test_picker_pendreason_factory_audit.c', @@ -107,6 +103,7 @@ sched_unit_test = executable( sched_unit_test_srcs, include_directories:[incdir], dependencies: [scheduler_test_deps], + link_with: sched_srcs_lib, c_args: ['-Werror=incompatible-pointer-types', '-DXCE_SCHEDULER_ROOT_PATH="' + (meson.current_source_dir() / '..') @@ -1464,19 +1461,12 @@ sched_repl_equivalence_test = executable( # translation unit inside libxce.a with several OTHER xcecli_* entry points # this test's real scheduler_srcs dependency graph also needs, so simply # excluding client.c from the link was not viable. -scheduler_decision_publish_retry_test_srcs = [ - 'test_scheduler_decision_publish_retry.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_decision_publish_retry_test_srcs += ['../' + f] - endif -endforeach - sched_scheduler_decision_publish_retry_test = executable( 'sched_scheduler_decision_publish_retry_test', - scheduler_decision_publish_retry_test_srcs, + 'test_scheduler_decision_publish_retry.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, c_args: ['-DXCE_MAIN_C_PATH="' + (meson.current_source_dir() / '..' / 'src' / 'main.c') + '"'], @@ -1496,18 +1486,12 @@ sched_scheduler_decision_publish_retry_test = executable( # allocator threads) and xcecli_set_pend_reason (blocks on a test-controlled # condvar, standing in for a stuck outage retry) -- see the test file's own # header comment for the full design and worst-case derivation. -scheduler_backpressure_test_srcs = ['test_scheduler_backpressure.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_backpressure_test_srcs += ['../' + f] - endif -endforeach - sched_scheduler_backpressure_test = executable( 'sched_scheduler_backpressure_test', - scheduler_backpressure_test_srcs, + 'test_scheduler_backpressure.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=xcecli_set_pend_reason'], @@ -1526,19 +1510,12 @@ sched_scheduler_backpressure_test = executable( # driver.c's absolute path (XCE_DRIVER_C_PATH) alongside scheduler.c's # (XCE_SCHEDULER_C_PATH, same pattern as sched_scheduler_connect_test) for # its Req 7 AC5 source-text scan. -scheduler_decision_redelivery_test_srcs = [ - 'test_scheduler_decision_redelivery.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_decision_redelivery_test_srcs += ['../' + f] - endif -endforeach - sched_scheduler_decision_redelivery_test = executable( 'sched_scheduler_decision_redelivery_test', - scheduler_decision_redelivery_test_srcs, + 'test_scheduler_decision_redelivery.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, c_args: ['-DXCE_SCHEDULER_C_PATH="' + (meson.current_source_dir() / '..' / 'src' / 'scheduler.c') + '"', @@ -1600,18 +1577,12 @@ sched_snapshot_load_allocator_regression_test = executable( # the decisive "did the real allocator run" signal a cache HIT and a # genuine exhausted attempt cannot otherwise be told apart by (both pend, # and Req 7.5 requires them to report the SAME reason category). -scheduler_cache_dispatch_test_srcs = ['test_scheduler_cache_dispatch.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_cache_dispatch_test_srcs += ['../' + f] - endif -endforeach - sched_cache_dispatch_test = executable( 'sched_cache_dispatch_test', - scheduler_cache_dispatch_test_srcs, + 'test_scheduler_cache_dispatch.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=sched_meet_host_selection'], @@ -1624,19 +1595,12 @@ sched_cache_dispatch_test = executable( # a real run with both tests in one binary crashed with a genuine SEGV in # fcfs_del_job() the first time this was tried -- see this test file's own # header comment). -scheduler_cache_dispatch_compound_test_srcs = [ - 'test_scheduler_cache_dispatch_compound.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_cache_dispatch_compound_test_srcs += ['../' + f] - endif -endforeach - sched_cache_dispatch_compound_test = executable( 'sched_cache_dispatch_compound_test', - scheduler_cache_dispatch_compound_test_srcs, + 'test_scheduler_cache_dispatch_compound.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=sched_meet_host_selection'], @@ -2068,18 +2032,12 @@ test('scheduler repl equivalence test', # make_alloc() cannot build a record. Real scheduler_srcs (as # sched_scheduler_connect_test) plus `-Wl,--wrap=strdup` to make a chosen # strdup() inside make_alloc() fail. -sched_make_allocs_oom_test_srcs = ['test_sched_make_allocs_oom.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_make_allocs_oom_test_srcs += ['../' + f] - endif -endforeach - sched_make_allocs_oom_test = executable( 'sched_make_allocs_oom_test', - sched_make_allocs_oom_test_srcs, + 'test_sched_make_allocs_oom.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=strdup'], install: false) @@ -2163,19 +2121,12 @@ test('scheduler cache dispatch compound test', # per-host slot counts. Same real-scheduler_srcs link technique as # sched_snapshot_load_allocator_regression_test/sched_cache_dispatch_test # above (see this test file's own header comment for the full rationale). -scheduler_parjob_e2e_multislot_test_srcs = [ - 'test_parjob_e2e_multislot.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - scheduler_parjob_e2e_multislot_test_srcs += ['../' + f] - endif -endforeach - sched_parjob_e2e_multislot_test = executable( 'sched_parjob_e2e_multislot_test', - scheduler_parjob_e2e_multislot_test_srcs, + 'test_parjob_e2e_multislot.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join'], install: false) @@ -2196,19 +2147,12 @@ test('scheduler parjob e2e multislot test', # for the full rationale -- one link technique for the whole file avoids a # duplicate-symbol conflict with meson's own separate compilation of # core.c/core_events.c/snapshot_load.c below). -sched_maxtasks_wiring_ingest_test_srcs = [ - 'test_maxtasks_wiring_ingest.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_maxtasks_wiring_ingest_test_srcs += ['../' + f] - endif -endforeach - sched_maxtasks_wiring_ingest_test = executable( 'sched_maxtasks_wiring_ingest_test', - sched_maxtasks_wiring_ingest_test_srcs, + 'test_maxtasks_wiring_ingest.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join'], install: false) @@ -2343,18 +2287,12 @@ test('scheduler maxtasks wiring sim driver parity test', # carries the session start in whole seconds as `from`, stamped by # sched_make_primary_alloc_() from the session core. Same real- # scheduler_srcs link technique as sched_parjob_e2e_multislot_test. -sched_alloc_from_stamp_test_srcs = ['test_sched_alloc_from_stamp.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_alloc_from_stamp_test_srcs += ['../' + f] - endif -endforeach - sched_alloc_from_stamp_test = executable( 'sched_alloc_from_stamp_test', - sched_alloc_from_stamp_test_srcs, + 'test_sched_alloc_from_stamp.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join'], install: false) @@ -2394,18 +2332,12 @@ test('scheduler sim discrete clock test', # technique as sched_sim_e2e_limit_visibility_test. Set # XCE_REPL_BASELINE_REGEN=1 to regenerate the fixtures (see the file's # header comment). -sched_repl_trace_baseline_test_srcs = ['test_repl_trace_baseline.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_repl_trace_baseline_test_srcs += ['../' + f] - endif -endforeach - sched_repl_trace_baseline_test = executable( 'sched_repl_trace_baseline_test', - sched_repl_trace_baseline_test_srcs, + 'test_repl_trace_baseline.c', include_directories:[incdir], dependencies: [scheduler_test_deps], + link_with: sched_srcs_lib, c_args: ['-DXCE_SCHEDULER_BIN="' + scheduler.full_path() + '"', '-DXCE_BASELINE_DIR="' + (meson.current_source_dir() / 'fixtures' @@ -2428,19 +2360,13 @@ test('scheduler repl trace baseline test', # the inline traces, their placement in step/continue mode and at # breakpoints, `info trace`, and the byte-identical trace file copy. The # replay writer links the real scheduler_srcs, as the baseline test does. -sched_repl_trace_e2e_test_srcs = ['test_repl_trace_e2e.c', - 'test_repl_trace_e2e_fixture.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_repl_trace_e2e_test_srcs += ['../' + f] - endif -endforeach - sched_repl_trace_e2e_test = executable( 'sched_repl_trace_e2e_test', - sched_repl_trace_e2e_test_srcs, + ['test_repl_trace_e2e.c', + 'test_repl_trace_e2e_fixture.c'], include_directories:[incdir], dependencies: [scheduler_test_deps], + link_with: sched_srcs_lib, c_args: ['-DXCE_SCHEDULER_BIN="' + scheduler.full_path() + '"', '-DXCE_BASELINE_DIR="' + (meson.current_source_dir() / 'fixtures' @@ -2515,18 +2441,12 @@ test('scheduler trace capture test', # Requirements 11.1-11.3). Real-scheduler_srcs link technique (every source # its own translation unit), so --wrap=sched_trace_rec_new counts every # record the allocation path creates. -sched_trace_hooks_gate_test_srcs = ['test_trace_hooks_gate.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_trace_hooks_gate_test_srcs += ['../' + f] - endif -endforeach - sched_trace_hooks_gate_test = executable( 'sched_trace_hooks_gate_test', - sched_trace_hooks_gate_test_srcs, + 'test_trace_hooks_gate.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=sched_trace_rec_new'], @@ -2541,18 +2461,12 @@ test('scheduler trace hooks gate test', # 6.4, 11.2) on the real allocator thread. Real-scheduler_srcs link # technique, so the decision-constructor wraps see the allocator's calls # (decision-build failures). -sched_trace_hooks_alloc_loop_test_srcs = ['test_trace_hooks_alloc_loop.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_trace_hooks_alloc_loop_test_srcs += ['../' + f] - endif -endforeach - sched_trace_hooks_alloc_loop_test = executable( 'sched_trace_hooks_alloc_loop_test', - sched_trace_hooks_alloc_loop_test_srcs, + 'test_trace_hooks_alloc_loop.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=make_pend_decision', @@ -2568,18 +2482,12 @@ test('scheduler trace hooks alloc loop test', # rows; Requirements 3.1-3.6) on the real sched_make_allocation() chain over # a bare simulation core. Real-scheduler_srcs link technique; libxce's # merge_job_alloc() is wrapped for the merge failure. -sched_trace_hooks_resreq_test_srcs = ['test_trace_hooks_resreq.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_trace_hooks_resreq_test_srcs += ['../' + f] - endif -endforeach - sched_trace_hooks_resreq_test = executable( 'sched_trace_hooks_resreq_test', - sched_trace_hooks_resreq_test_srcs, + 'test_trace_hooks_resreq.c', include_directories:[incdir], dependencies: [scheduler_deps, unity_dep], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=xcecli_start', '-Wl,--wrap=xcecli_join', '-Wl,--wrap=merge_job_alloc'], @@ -2754,18 +2662,12 @@ test('scheduler repl trace wiring test', # wraps watch verification, recording and the breakpoint check for # trace-only decisions, the session hook in a non-interactive run, and make # an allocation fail on demand. -sched_sim_trace_driver_test_srcs = ['test_sim_trace_driver.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_sim_trace_driver_test_srcs += ['../' + f] - endif -endforeach - sched_sim_trace_driver_test = executable( 'sched_sim_trace_driver_test', - sched_sim_trace_driver_test_srcs, + 'test_sim_trace_driver.c', include_directories:[incdir], dependencies: [scheduler_test_deps], + link_with: sched_srcs_lib, link_args: ['-Wl,--wrap=sim_decision_eq', '-Wl,--wrap=sched_record_session_add_decision', '-Wl,--wrap=xce_repl_on_decision', @@ -2852,20 +2754,14 @@ test('scheduler trace bounds test', # snapshot, the verify result and the REPL's accounting dumps. Links the # 6.1 fixture file for write_extra_replay() and the real scheduler_srcs for # the replay writer and decoders. -sched_trace_equivalence_test_srcs = ['test_trace_equivalence.c', - 'test_trace_equivalence_cmp.c', - 'test_repl_trace_e2e_fixture.c'] -foreach f : scheduler_srcs - if f != 'src/main.c' - sched_trace_equivalence_test_srcs += ['../' + f] - endif -endforeach - sched_trace_equivalence_test = executable( 'sched_trace_equivalence_test', - sched_trace_equivalence_test_srcs, + ['test_trace_equivalence.c', + 'test_trace_equivalence_cmp.c', + 'test_repl_trace_e2e_fixture.c'], include_directories:[incdir], dependencies: [scheduler_test_deps], + link_with: sched_srcs_lib, c_args: ['-DXCE_SCHEDULER_BIN="' + scheduler.full_path() + '"', '-DXCE_BASELINE_DIR="' + (meson.current_source_dir() / 'fixtures'