feat(xce-harness-component): relocate xce_test into standalone harness component

Move xce_test's 15 production sources + headers from src/controller/testsuite/
into a new src/harness/ component with its own meson.build, and strip every
now-dangling controller-side reference (11 controller_unit_test_src entries,
4 standalone test-binary blocks, 11 controller_test.c extern/RUN_TEST lines)
so the controller build is never left broken.

Fixes a broken relative-symlink bridge (xce_test_controller_link) added to
keep two controller-embedded tests (test_test_cli.c, test_xcetest_process_entry.c)
finding the relocated xce_test binary until task 2.1 moves them too.

Discovered and documented in design.md: four controller-domain regression
tests call functions directly from four of xce_test's own files, requiring
controller_unit_test to permanently recompile them from src/harness/ — a
real dependency neither the original discovery nor design audit caught.

controller_unit_test: 1228 Tests 0 Failures 0 Ignored (verified via the
Unity binary's own printed summary line, not just meson's aggregate count).

Task 1.1 of xce-harness-component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-14 05:08:15 +00:00
parent b8ef82c1b2
commit cf55d691de
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
36 changed files with 550 additions and 180 deletions

View file

@ -31,7 +31,7 @@ controller_src = [
]
incdir = include_directories('.', '../include', '../lib',
'include')
'include', '../harness/include')
# cc = meson.get_compiler('c')
# math_dep = cc.find_library('m', required : false)
@ -55,35 +55,6 @@ controller = executable('xce_controller',
link_args: wrap_link_args,
install: true)
xce_test_src = [ 'testsuite/test.c', 'testsuite/waldump.c', 'testsuite/walcompare.c', 'testsuite/snapverify.c', 'testsuite/snapdiff.c', 'testsuite/configcompare.c', 'testsuite/resval_typeresolve.c', 'testsuite/joblist_display.c', 'testsuite/join_worker_args.c', 'testsuite/rcfile.c', 'testsuite/flagspec.c', 'testsuite/interruptcall.c', 'testsuite/confirm.c', 'testsuite/listfmt_json.c', 'testsuite/listrow_json.c']
# BSD-licensed readline replacement (scheduler-core spec, task 12.1 review
# finding): GNU readline is GPL; xce_test's REPL only needs readline()/
# add_history(), both provided by libedit's editline/readline.h emulation
# header under a permissive license. Single dependency, no separate
# ncurses/history split (libedit's own .pc pulls ncurses in privately).
libedit_dep = dependency('libedit')
ssl_dep = dependency('openssl') # also needed by configcompare.c (SHA256());
# controller_unit_test_deps below re-assigns
# the same dependency() result, harmless
xce_test_deps = [
libxce_dep,
librtti_dep,
struct2json_dep,
libedit_dep,
tinylog_dep,
zlib_dep, # waldump: crc32 WAL validation
c_capnproto_dep, # waldump: decode JOB_SUBMIT jobid
ssl_dep, # configcompare: SHA256() active-config hashing
thread_dep, # interruptcall.c: xcetest_run_interruptible() detached worker (task 1.4)
]
xce_test = executable('xce_test',
xce_test_src,
include_directories: [incdir],
dependencies: xce_test_deps,
install: true)
# Controller Unity test suite (persist types and future controller unit tests)
controller_unit_test_src = [
'testsuite/controller_test.c',
@ -157,27 +128,24 @@ controller_unit_test_src = [
'testsuite/test_term_adoption_vectors.c',
'testsuite/test_ha_core_safety.c',
'testsuite/test_ha_partition.c',
'testsuite/test_waldump.c',
'testsuite/waldump.c',
'testsuite/test_walcompare.c',
'testsuite/walcompare.c',
'testsuite/test_snapverify.c',
'testsuite/snapverify.c',
'testsuite/test_snapdiff.c',
'testsuite/snapdiff.c',
'testsuite/test_configcompare.c',
'testsuite/configcompare.c',
'testsuite/test_rcfile.c',
'testsuite/rcfile.c',
'testsuite/test_flagspec.c',
'testsuite/flagspec.c',
'testsuite/test_xcetest_interrupt.c',
'testsuite/test_interruptcall.c',
'testsuite/interruptcall.c',
'testsuite/test_confirm.c',
'testsuite/confirm.c',
'testsuite/test_listfmt_json.c',
'testsuite/listfmt_json.c',
# xce-harness-component task 1.1: these 4 of the 15 files relocated to
# src/harness/ are NOT purely xce_test-private -- test_config_repl.c/
# test_bug857_regression.c call snapdiff_paths() (snapdiff.c),
# test_ha_core_safety.c calls waldump_path_visit()/walcompare()
# (waldump.c/walcompare.c), and test_upgrade_snapshot_apply.c calls
# snapverify_path() (snapverify.c) -- a real cross-dependency the
# spec's own research.md audit missed (it only checked what xce_test's
# sources #include, not who #includes xce_test's headers back). Their
# own white-box test pairs (test_waldump.c etc.) DID move to the
# harness's future harness_unit_test (task 2.1) and are correctly gone
# from this list below; these 4 production files themselves stay
# compiled a second time here too, exactly mirroring today's existing
# dual-compilation pattern (recompiled from their new ../harness/
# location -- see incdir's added '../harness/include' entry above).
'../harness/waldump.c',
'../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',
@ -253,9 +221,31 @@ 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 /
@ -347,32 +337,6 @@ test('apiserver push test', apiserver_push_unit_test,
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# resource-value-verification task 1.1: resval_typeresolve.c is a small,
# dependency-light module (no networking/REPL/global state) extracted from
# test.c's set_resval() specifically so its name -> Value_u_which resolution
# logic can be unit-tested in isolation -- test.c itself (4298 lines, its own
# main(), heavy libedit/network dependencies) cannot be white-box included
# into a Unity test. Standalone binary (not folded into controller_unit_test_
# src) since it has zero dependency on controller.c/cluster.c internals,
# mirroring sched_push_unit_test's/apiserver_push_unit_test's own standalone-
# binary rationale above.
resval_typeresolve_unit_test_src = [
'testsuite/test_resval_typeresolve.c',
'testsuite/resval_typeresolve.c',
]
resval_typeresolve_unit_test = executable('resval_typeresolve_unit_test',
resval_typeresolve_unit_test_src,
include_directories: [incdir],
dependencies: controller_unit_test_deps,
c_args: ['-DXCE_TESTING'],
install: false)
test('resval typeresolve test', resval_typeresolve_unit_test,
suite: 'controller',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# bug-1735: misc.c's update_host_runusage()/aggregate_job_runusage() are
# small, dependency-light functions (calloc/realloc/strcmp only, no RTTI
# locking, no networking) -- standalone binary linking the REAL misc.c
@ -397,85 +361,4 @@ test('host runusage test', host_runusage_unit_test,
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# xce-test-cli-ux task 2.5 remediation: joblist_display.c is a small,
# dependency-light module (no networking/REPL/global state) extracted from
# test.c's worker_status_str()/job_state_str()/format_job_time() specifically
# so their bug-1711 display-formatting fixes can be unit-tested in isolation
# -- test.c itself cannot be white-box included into a Unity test, same
# rationale as resval_typeresolve_unit_test above. Standalone binary (not
# folded into controller_unit_test_src) since it has zero dependency on
# controller.c/cluster.c internals, mirroring resval_typeresolve_unit_test's/
# host_runusage_unit_test's own standalone-binary rationale.
joblist_display_unit_test_src = [
'testsuite/test_joblist_display.c',
'testsuite/joblist_display.c',
]
joblist_display_unit_test = executable('joblist_display_unit_test',
joblist_display_unit_test_src,
include_directories: [incdir],
dependencies: controller_unit_test_deps,
c_args: ['-DXCE_TESTING'],
install: false)
test('joblist display test', joblist_display_unit_test,
suite: 'controller',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# xce-test-cli-ux task 2.6 remediation: join_worker_args.c is a small,
# dependency-light module (no networking/REPL/global state) extracted from
# test.c's cmd_join_worker() specifically so the bug-1557 maxmem-default fix
# can be unit-tested in isolation -- test.c itself cannot be white-box
# included into a Unity test, same rationale as joblist_display_unit_test/
# resval_typeresolve_unit_test above. Standalone binary (not folded into
# controller_unit_test_src) since it has zero dependency on
# controller.c/cluster.c internals, mirroring those tests' own
# standalone-binary rationale.
join_worker_args_unit_test_src = [
'testsuite/test_join_worker_args.c',
'testsuite/join_worker_args.c',
'testsuite/flagspec.c', # xce-test-cli-ux task 3.1: join_worker_parse_attrs
# migrated onto flagspec_build_opts/flagspec_reject
]
join_worker_args_unit_test = executable('join_worker_args_unit_test',
join_worker_args_unit_test_src,
include_directories: [incdir],
dependencies: controller_unit_test_deps,
c_args: ['-DXCE_TESTING'],
install: false)
test('join worker args test', join_worker_args_unit_test,
suite: 'controller',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# xce-test-cli-ux task 3.4: listrow_json.c is a small, dependency-light
# module (no networking/REPL/global state) extracted from cmd_reslist/
# cmd_workerlist/joblist_query specifically so their new --json output
# mode's per-command field-mapping logic can be unit-tested in isolation
# -- test.c itself cannot be white-box included into a Unity test, same
# rationale as joblist_display_unit_test/resval_typeresolve_unit_test
# above. Standalone binary (not folded into controller_unit_test_src)
# mirroring those tests' own standalone-binary rationale. Links
# listfmt_json.c directly (not struct2json_dep alone) since listrow_json.c
# calls its add_string()/add_number()/new_row() helpers.
listrow_json_unit_test_src = [
'testsuite/test_listrow_json.c',
'testsuite/listrow_json.c',
'testsuite/listfmt_json.c',
]
listrow_json_unit_test = executable('listrow_json_unit_test',
listrow_json_unit_test_src,
include_directories: [incdir],
dependencies: controller_unit_test_deps,
c_args: ['-DXCE_TESTING'],
install: false)
test('listrow json test', listrow_json_unit_test,
suite: 'controller',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])

View file

@ -65,17 +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_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);
extern void test_drive_async_config(void);
@ -252,17 +241,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_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);
TEST(test_drive_async_config);

56
src/harness/meson.build Normal file
View file

@ -0,0 +1,56 @@
# TEMPORARY (xce-harness-component task 1.1): '../controller/testsuite' is
# only here so the bare `#include "ctrlstatus_fmt.h"` in test.c/
# joblist_display.c keeps resolving -- ctrlstatus_fmt.h itself still
# physically lives at src/controller/testsuite/ctrlstatus_fmt.h pending
# task 1.2, which promotes it to src/include/xce/ctrlstatus_fmt.h and
# repoints these two #include lines at it. Remove this entry when task 1.2
# lands (it will make it redundant, since src/include is already on this
# path via '../include').
incdir = include_directories('.', '../include', '../lib', 'include',
'../controller/testsuite')
zlib_dep = dependency('zlib')
# BSD-licensed readline replacement (scheduler-core spec, task 12.1 review
# finding): GNU readline is GPL; xce_test's REPL only needs readline()/
# add_history(), both provided by libedit's editline/readline.h emulation
# header under a permissive license. Single dependency, no separate
# ncurses/history split (libedit's own .pc pulls ncurses in privately).
libedit_dep = dependency('libedit')
ssl_dep = dependency('openssl') # also needed by configcompare.c (SHA256())
xce_test_src = [
'test.c',
'waldump.c',
'walcompare.c',
'snapverify.c',
'snapdiff.c',
'configcompare.c',
'resval_typeresolve.c',
'joblist_display.c',
'join_worker_args.c',
'rcfile.c',
'flagspec.c',
'interruptcall.c',
'confirm.c',
'listfmt_json.c',
'listrow_json.c',
]
xce_test_deps = [
libxce_dep,
librtti_dep,
struct2json_dep,
libedit_dep,
tinylog_dep,
zlib_dep, # waldump: crc32 WAL validation
c_capnproto_dep, # waldump: decode JOB_SUBMIT jobid
ssl_dep, # configcompare: SHA256() active-config hashing
thread_dep, # interruptcall.c: xcetest_run_interruptible() detached worker (task 1.4)
]
xce_test = executable('xce_test',
xce_test_src,
include_directories: [incdir],
dependencies: xce_test_deps,
install: true)

View file

@ -64,6 +64,7 @@ if component != 'xcecli'
]
subdir('lib')
subdir('harness')
subdir('controller')
subdir('worker')
subdir('autoscaler')