From b5873a67aa10db961c87be054624752f3ee302f1 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Mon, 14 Sep 2026 05:49:31 +0000 Subject: [PATCH] feat(xce-harness-component): consolidate harness_unit_test binary Relocate the 13 test files that lived inside controller_unit_test into src/harness/testsuite/, add a new Unity runner (harness_test.c, modeled on controller_test.c) carrying their extern/RUN_TEST entries, and register the new harness_unit_test executable + suite in src/harness/meson.build with an explicit build-order dependency on xce_test (two of its tests spawn that binary over a pty). Finish clearing the controller side: remove test_test_cli.c/ test_xcetest_process_entry.c from controller_unit_test_src and their extern/RUN_TEST entries from controller_test.c, and remove the temporary xce_test_controller_link symlink bridge task 1.1 added (no longer needed now that both binaries build as siblings under harness/). Verified: harness suite 132 Tests 0 Failures 0 Ignored; controller suite 1199 Tests 0 Failures 0 Ignored. 1199+132=1331 matches the pre-move controller_unit_test baseline exactly (cross-checked against buglog bug-1798/bug-1805). Task 2.1 of xce-harness-component. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .kiro/specs/xce-harness-component/tasks.md | 3 +- src/controller/meson.build | 24 ---- src/controller/testsuite/controller_test.c | 4 - src/harness/meson.build | 80 +++++++++++++ src/harness/testsuite/harness_test.c | 110 ++++++++++++++++++ .../testsuite/test_configcompare.c | 0 .../testsuite/test_confirm.c | 0 .../testsuite/test_flagspec.c | 0 .../testsuite/test_interruptcall.c | 0 .../testsuite/test_listfmt_json.c | 0 .../testsuite/test_rcfile.c | 0 .../testsuite/test_snapdiff.c | 0 .../testsuite/test_snapverify.c | 0 .../testsuite/test_test_cli.c | 0 .../testsuite/test_walcompare.c | 0 .../testsuite/test_waldump.c | 0 .../testsuite/test_xcetest_interrupt.c | 0 .../testsuite/test_xcetest_process_entry.c | 0 18 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 src/harness/testsuite/harness_test.c rename src/{controller => harness}/testsuite/test_configcompare.c (100%) rename src/{controller => harness}/testsuite/test_confirm.c (100%) rename src/{controller => harness}/testsuite/test_flagspec.c (100%) rename src/{controller => harness}/testsuite/test_interruptcall.c (100%) rename src/{controller => harness}/testsuite/test_listfmt_json.c (100%) rename src/{controller => harness}/testsuite/test_rcfile.c (100%) rename src/{controller => harness}/testsuite/test_snapdiff.c (100%) rename src/{controller => harness}/testsuite/test_snapverify.c (100%) rename src/{controller => harness}/testsuite/test_test_cli.c (100%) rename src/{controller => harness}/testsuite/test_walcompare.c (100%) rename src/{controller => harness}/testsuite/test_waldump.c (100%) rename src/{controller => harness}/testsuite/test_xcetest_interrupt.c (100%) rename src/{controller => harness}/testsuite/test_xcetest_process_entry.c (100%) diff --git a/.kiro/specs/xce-harness-component/tasks.md b/.kiro/specs/xce-harness-component/tasks.md index 6bad43b1..7a0ceb69 100644 --- a/.kiro/specs/xce-harness-component/tasks.md +++ b/.kiro/specs/xce-harness-component/tasks.md @@ -20,7 +20,7 @@ - _Requirements: 6.1_ - [ ] 2. Core: migrate the five harness test binaries -- [ ] 2.1 Consolidate the controller-embedded harness tests into a new binary and finish clearing them from the controller's own test binary +- [x] 2.1 Consolidate the controller-embedded harness tests into a new binary and finish clearing them from the controller's own test binary - Physically relocate the 13 test files for this group into `src/harness/testsuite/`: the 11 whose `controller_unit_test_src`/`controller_test.c` references were already removed in task 1.1 (`test_waldump.c` through `test_xcetest_interrupt.c`, left on disk at their old path until now), plus `test_test_cli.c` and `test_xcetest_process_entry.c` (still validly part of `controller_unit_test` until this task) - Write a new Unity runner `harness_test.c` (modeled on `controller_test.c`) carrying all 13 `extern`/`RUN_TEST` entries; register the new `harness_unit_test` executable and its `meson test()` under suite `harness` in `src/harness/meson.build`; declare its build-order dependency on the `xce_test` executable target (two of its tests spawn that binary over a pty) - In the same task, remove `test_test_cli.c`'s and `test_xcetest_process_entry.c`'s entries from `controller_unit_test_src` and their `extern`/`RUN_TEST` lines from `controller_test.c` — the only two of the 13 not already removed in task 1.1 (`test_ctrlstatus`'s entry stays — it tests controller's own `ctrlstatus.c`) @@ -102,3 +102,4 @@ - **Task 1.1**: `test_test_cli.c`/`test_xcetest_process_entry.c` locate `xce_test` as a build-dir sibling via `resolve_xce_test_path()`. Since they still live in `src/controller/testsuite/` until task 2.1, a temporary `xce_test_controller_link` custom_target symlink bridge was added to `src/controller/meson.build` (documented in-code as removable once task 2.1 relocates both files). Task 2.1 must remove this custom_target and the `test()`'s `depends:` on it as part of that relocation. - **Task 1.1 gotcha**: a meson `custom_target` command's `@INPUT0@`/`@OUTPUT0@` tokens expand to build-root-relative paths, but a symlink's target string is resolved relative to the *symlink's own directory* at dereference time, not the invocation cwd or the string's own reference frame — using `@INPUT0@` directly as an `ln -sf` target from a different subdirectory produces a broken link. Use an explicit `../`-relative (or absolute) path instead. - **Build dir note**: this repo's default build dir resolves to `src/xce.build/` (relative to `src/tools/xce-build.sh`'s own cwd), not a bare `xce.build/` at the repo root. +- **Task 2.1**: confirmed `resolve_xce_test_path()` (`/proc/self/exe` dirname + sibling lookup) needed zero code change once both `xce_test` and `harness_unit_test` build under the same `src/harness/meson.build` — they land in the same build-dir subdir automatically. Test counts cross-checked and consistent end to end: 1331 (pre-move) → 1228 (after task 1.1's 11-entry removal) → 1199 controller + 132 harness = 1331 (after task 2.1). Minor non-blocking nit for a future pass: `test_xcetest_process_entry.c`'s header comment still describes the old controller-embedded build layout — stale prose only, not a functional issue. diff --git a/src/controller/meson.build b/src/controller/meson.build index b4db549f..c711c895 100644 --- a/src/controller/meson.build +++ b/src/controller/meson.build @@ -146,8 +146,6 @@ controller_unit_test_src = [ '../harness/walcompare.c', '../harness/snapverify.c', '../harness/snapdiff.c', - 'testsuite/test_xcetest_process_entry.c', - 'testsuite/test_test_cli.c', 'testsuite/test_persist_snapshot_entities.c', 'testsuite/test_apply_seam.c', 'testsuite/test_apply_committed_entry.c', @@ -221,31 +219,9 @@ controller_unit_test = executable('controller_unit_test', c_args: ['-DXCE_TESTING'], install: false) -# TEMPORARY (xce-harness-component task 1.1): test_test_cli.c and -# test_xcetest_process_entry.c stay registered in controller_unit_test_src -# above until task 2.1 relocates them into src/harness/testsuite/. Both -# spawn the built xce_test binary as a subprocess via resolve_xce_test_path() -# (test_xcetest_process_entry.c), which finds it by looking in the SAME -# build directory as the running test binary (dirname(/proc/self/exe) + -# "/xce_test") -- an assumption that held when xce_test built into this -# same controller/ build subdir, and breaks now that xce_test builds into -# harness/ instead (task 1.1's whole point). This symlink keeps that -# assumption satisfied without touching either test file, both of which -# are out of this task's boundary -- remove this custom_target and the -# test()'s `depends:` below once task 2.1 physically relocates both files, -# at which point they become genuine build-dir siblings of xce_test again -# (mirroring harness_unit_test's own explicit dependency on the xce_test -# target, per the design's System Flows). -xce_test_controller_link = custom_target('xce_test_controller_link', - output: 'xce_test', - input: xce_test, - command: ['ln', '-sf', '../harness/xce_test', '@OUTPUT0@'], -) - test('controller test', controller_unit_test, suite: 'controller', timeout: 300, - depends: [xce_test_controller_link], env: ['ASAN_OPTIONS=detect_leaks=0']) # job-array-single-wal-record task 5.1: sched.c's push logic (sched_job_new_fun / diff --git a/src/controller/testsuite/controller_test.c b/src/controller/testsuite/controller_test.c index 37eb3250..3427d377 100644 --- a/src/controller/testsuite/controller_test.c +++ b/src/controller/testsuite/controller_test.c @@ -65,8 +65,6 @@ extern void test_ctrlstatus(void); extern void test_ha_heartbeat_committed(void); extern void test_ha_vote_floor_heartbeat(void); extern void test_term_safety_adapter(void); -extern void test_xcetest_process_entry(void); -extern void test_test_cli(void); extern void test_drive_async_config(void); extern void test_slowlane(void); extern void test_commit_admission_pool(void); @@ -241,8 +239,6 @@ int main(int argc, char* argv[]) TEST(test_ha_heartbeat_committed); TEST(test_ha_vote_floor_heartbeat); TEST(test_term_safety_adapter); - TEST(test_xcetest_process_entry); - TEST(test_test_cli); TEST(test_drive_async_config); TEST(test_slowlane); TEST(test_commit_admission_pool); diff --git a/src/harness/meson.build b/src/harness/meson.build index 3e0bf389..9ee5ae78 100644 --- a/src/harness/meson.build +++ b/src/harness/meson.build @@ -45,3 +45,83 @@ xce_test = executable('xce_test', include_directories: [incdir], dependencies: xce_test_deps, install: true) + +# Harness Unity test suite (xce-harness-component task 2.1): the 13 white-box +# tests that used to live inside controller_unit_test. Task 1.1 already +# removed their controller-side meson/controller_test.c registration but left +# the files on disk in src/controller/testsuite/; this task physically +# relocates them here and gives them their own Unity runner (harness_test.c) +# and their own binary, registered under the 'harness' suite. Mirrors +# controller_unit_test's own production+test pairing for the 10 files that +# have one (waldump, walcompare, snapverify, snapdiff, configcompare, rcfile, +# flagspec, interruptcall, confirm, listfmt_json) -- see git history +# (src/controller/meson.build prior to task 1.1) for the exact pairing this +# preserves. test_xcetest_interrupt.c needs no separate production file +# (xcetest_install_sigint() is a `static inline` in include/xcetest_interrupt.h); +# test_test_cli.c and test_xcetest_process_entry.c need none either -- both +# spawn the built xce_test binary as a subprocess instead of linking any of +# its production sources directly. +# (unity_dep comes from src/lib/meson.build, ssl_dep from this file's own +# top -- both already in scope, reused rather than redeclared.) + +harness_unit_test_src = [ + 'testsuite/harness_test.c', + 'testsuite/test_waldump.c', + 'waldump.c', + 'testsuite/test_walcompare.c', + 'walcompare.c', + 'testsuite/test_snapverify.c', + 'snapverify.c', + 'testsuite/test_snapdiff.c', + 'snapdiff.c', + 'testsuite/test_configcompare.c', + 'configcompare.c', + 'testsuite/test_rcfile.c', + 'rcfile.c', + 'testsuite/test_flagspec.c', + 'flagspec.c', + 'testsuite/test_xcetest_interrupt.c', + 'testsuite/test_interruptcall.c', + 'interruptcall.c', + 'testsuite/test_confirm.c', + 'confirm.c', + 'testsuite/test_listfmt_json.c', + 'listfmt_json.c', + 'testsuite/test_xcetest_process_entry.c', + 'testsuite/test_test_cli.c', +] + +harness_unit_test_deps = [ + libxce_dep, + librtti_dep, + unity_dep, + tinylog_dep, + thread_dep, + zlib_dep, + c_capnproto_dep, + ssl_dep, + struct2json_dep, # testsuite/listfmt_json.c + test_listfmt_json.c: cJSON +] + +harness_unit_test = executable('harness_unit_test', + harness_unit_test_src, + include_directories: [incdir], + dependencies: harness_unit_test_deps, + c_args: ['-DXCE_TESTING'], + install: false) + +# harness_unit_test's two subprocess-spawning tests (test_test_cli.c, +# test_xcetest_process_entry.c) locate xce_test as a build-dir sibling via +# resolve_xce_test_path() (dirname(/proc/self/exe) + "/xce_test") -- true +# naturally once both binaries build into this same harness/ subdir, with no +# symlink bridge needed (unlike the temporary one task 1.1 added to +# src/controller/meson.build for the period these files still lived there). +# `depends:` below is still an explicit meson build-order dependency, not an +# informal expectation -- design.md's System Flows names this as the intended +# mechanism (mirrors how controller/meson.build depended on its own bridge +# custom_target before this task removed it). +test('harness unit test', harness_unit_test, + suite: 'harness', + timeout: 300, + depends: [xce_test], + env: ['ASAN_OPTIONS=detect_leaks=0']) diff --git a/src/harness/testsuite/harness_test.c b/src/harness/testsuite/harness_test.c new file mode 100644 index 00000000..7c4bc808 --- /dev/null +++ b/src/harness/testsuite/harness_test.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +extern void test_waldump(void); +extern void test_walcompare(void); +extern void test_snapverify(void); +extern void test_snapdiff(void); +extern void test_configcompare(void); +extern void test_rcfile(void); +extern void test_flagspec(void); +extern void test_xcetest_interrupt(void); +extern void test_interruptcall(void); +extern void test_confirm(void); +extern void test_listfmt_json(void); +extern void test_xcetest_process_entry(void); +extern void test_test_cli(void); + +void setUp(void) { return; } + +void tearDown(void) +{ + printf("\n-----------------------------------------------------\n"); + return; +} + +static char doc[] = "harness_test"; +static char args_doc[] = ""; + +#define MAX_TESTS (256) + +struct arguments { + int ntests; + char* tests[MAX_TESTS]; +}; + +static struct argp_option options[] = { + { "run", 't', "STR", 0, "run test" }, + { 0 } +}; + +static error_t parse_opt(int key, char* arg, struct argp_state* state) +{ + struct arguments* arguments = state->input; + + switch (key) { + case 't': + if (arguments->ntests >= MAX_TESTS) { + fprintf(stderr, "Too many tests\n"); + exit(-1); + } + arguments->tests[arguments->ntests] = arg; + arguments->ntests++; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; +struct arguments arguments; + +static int check_test(struct arguments* args, char* x) +{ + int i; + + if (args->ntests == 0) { + return 1; + } + for (i = 0; i < args->ntests; i++) { + if (strcmp(x, args->tests[i]) == 0) { + return 1; + } + } + return 0; +} + +#define TEST(x) \ + do { \ + if (check_test(&arguments, #x)) { \ + x(); \ + } \ + } while (0) + +int main(int argc, char* argv[]) +{ + argp_parse(&argp, argc, argv, 0, 0, &arguments); + + UNITY_BEGIN(); + TEST(test_waldump); + TEST(test_walcompare); + TEST(test_snapverify); + TEST(test_snapdiff); + TEST(test_configcompare); + TEST(test_rcfile); + TEST(test_flagspec); + TEST(test_xcetest_interrupt); + TEST(test_interruptcall); + TEST(test_confirm); + TEST(test_listfmt_json); + TEST(test_xcetest_process_entry); + TEST(test_test_cli); + + return UNITY_END(); +} diff --git a/src/controller/testsuite/test_configcompare.c b/src/harness/testsuite/test_configcompare.c similarity index 100% rename from src/controller/testsuite/test_configcompare.c rename to src/harness/testsuite/test_configcompare.c diff --git a/src/controller/testsuite/test_confirm.c b/src/harness/testsuite/test_confirm.c similarity index 100% rename from src/controller/testsuite/test_confirm.c rename to src/harness/testsuite/test_confirm.c diff --git a/src/controller/testsuite/test_flagspec.c b/src/harness/testsuite/test_flagspec.c similarity index 100% rename from src/controller/testsuite/test_flagspec.c rename to src/harness/testsuite/test_flagspec.c diff --git a/src/controller/testsuite/test_interruptcall.c b/src/harness/testsuite/test_interruptcall.c similarity index 100% rename from src/controller/testsuite/test_interruptcall.c rename to src/harness/testsuite/test_interruptcall.c diff --git a/src/controller/testsuite/test_listfmt_json.c b/src/harness/testsuite/test_listfmt_json.c similarity index 100% rename from src/controller/testsuite/test_listfmt_json.c rename to src/harness/testsuite/test_listfmt_json.c diff --git a/src/controller/testsuite/test_rcfile.c b/src/harness/testsuite/test_rcfile.c similarity index 100% rename from src/controller/testsuite/test_rcfile.c rename to src/harness/testsuite/test_rcfile.c diff --git a/src/controller/testsuite/test_snapdiff.c b/src/harness/testsuite/test_snapdiff.c similarity index 100% rename from src/controller/testsuite/test_snapdiff.c rename to src/harness/testsuite/test_snapdiff.c diff --git a/src/controller/testsuite/test_snapverify.c b/src/harness/testsuite/test_snapverify.c similarity index 100% rename from src/controller/testsuite/test_snapverify.c rename to src/harness/testsuite/test_snapverify.c diff --git a/src/controller/testsuite/test_test_cli.c b/src/harness/testsuite/test_test_cli.c similarity index 100% rename from src/controller/testsuite/test_test_cli.c rename to src/harness/testsuite/test_test_cli.c diff --git a/src/controller/testsuite/test_walcompare.c b/src/harness/testsuite/test_walcompare.c similarity index 100% rename from src/controller/testsuite/test_walcompare.c rename to src/harness/testsuite/test_walcompare.c diff --git a/src/controller/testsuite/test_waldump.c b/src/harness/testsuite/test_waldump.c similarity index 100% rename from src/controller/testsuite/test_waldump.c rename to src/harness/testsuite/test_waldump.c diff --git a/src/controller/testsuite/test_xcetest_interrupt.c b/src/harness/testsuite/test_xcetest_interrupt.c similarity index 100% rename from src/controller/testsuite/test_xcetest_interrupt.c rename to src/harness/testsuite/test_xcetest_interrupt.c diff --git a/src/controller/testsuite/test_xcetest_process_entry.c b/src/harness/testsuite/test_xcetest_process_entry.c similarity index 100% rename from src/controller/testsuite/test_xcetest_process_entry.c rename to src/harness/testsuite/test_xcetest_process_entry.c