diff --git a/.kiro/specs/xce-harness-component/design.md b/.kiro/specs/xce-harness-component/design.md new file mode 100644 index 00000000..4aae4a2b --- /dev/null +++ b/.kiro/specs/xce-harness-component/design.md @@ -0,0 +1,348 @@ +# Design Document: xce-harness-component + +## Overview + +**Purpose**: This feature relocates `xce_test` — an already-decoupled wire-protocol test client — out of `src/controller/testsuite/` into its own standalone `src/harness/` component, giving it an independent build target, its own unit-test binary, and its own documentation, with zero change to its runtime behavior. + +**Users**: XCE engineers who build the project, run HA soak / multi-host / resource-verification workflows, or maintain `xce_test` itself. + +**Impact**: Changes `src/controller/meson.build` (removes the `xce_test` executable, 13 test-file entries from `controller_unit_test_src`, and 4 standalone test-binary blocks), `controller_test.c` (removes 13 extern/RUN_TEST entries), `src/tools/xce-build.sh` (decouples the `controller` build case, adds a `harness` case), four downstream skills, and three steering/documentation files. No change to `xce_test`'s command surface, output, or the controller's own domain test coverage. + +### Goals +- `xce_test` becomes buildable, testable, and documented as an independent component (`src/harness/`). +- Building `controller` no longer implicitly builds `xce_test`, and vice versa. +- Every white-box/integration test that validates one of xce_test's own source files keeps running, now attributed to the harness's own test suite. +- Every downstream workflow that assumed the old coupling keeps working. + +### Non-Goals +- No new `xce_test` command, flag, or output change. +- No rename of the `xce_test` binary. +- No change to `xcecli`, `xceadmin`, the Go frontend, or controller's own domain unit tests (HA, persistence, commit, replication, job). +- No nix/AppImage packaging change (xce_test is not part of either today). + +## Boundary Commitments + +### This Spec Owns +- The `src/harness/` component directory: its `meson.build`, `CLAUDE.md`, production sources, and test sources. +- The `xce_test` executable and its five test binaries (`harness_unit_test`, `resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`) and their meson `test()` registration. +- The `controller)` and new `harness)` cases in `src/tools/xce-build.sh`, and root `CLAUDE.md`'s component list. +- The relocation of the one genuinely shared header, `ctrlstatus_fmt.h`, to `src/include/xce/` (the existing cross-daemon shared-header prefix, e.g. `cluster.h`, `types.h`, `errnox.h` — not the main libxce API headers). +- Updates to the four downstream skills (`xce-test-ha`, `xce-deploy-ha`, `xce-test-multihost`, `xce-test-resources`) so they no longer assume the old coupling. +- Updates to `structure.md` and `build-deployment.md` describing the new component. + +### Out of Boundary +- Any xce_test behavioral change (owned by `xce-test-cli-ux`, `xce-test-e2e-primitives`, `xce-test-rc-file`, and the still-open roadmap item for further WAL/snapshot inspectors). +- Controller's own domain test files (`test_ha_*.c`, `test_persist_*.c`, `test_commit_*.c`, etc.) — these stay in `src/controller/testsuite/` untouched. +- Any change to `xcecli`/`xceadmin`/Go frontend build wiring. +- Re-litigating which controller tests belong where beyond the 17 files whose production counterpart is physically moving. + +### Allowed Dependencies +- The harness component may depend on libxce (`src/lib/`) and its existing dependency set (`librtti`, `struct2json`, `libedit`, `tinylog`, `zlib`, `c_capnproto`, `openssl`, `thread`) — unchanged from today. +- The harness component may depend on `src/include/xce/ctrlstatus_fmt.h` once promoted there — the one small, header-only, link-free utility shared with controller's own test suite. Placed under the existing `xce/` cross-daemon shared-header prefix (not bare `src/include/`) so it doesn't read as part of libxce's real runtime API surface. This is the **only** sanctioned harness→controller-adjacent dependency; no other controller header or source file may be referenced. +- `harness_unit_test` may declare a build-order dependency on the `xce_test` executable target (two of its tests spawn that binary directly) — this is an intra-harness dependency, not a cross-component one. +- **Sanctioned exception, discovered during task 1.1 (not part of the original design)**: `controller_unit_test` may compile `waldump.c`, `walcompare.c`, `snapverify.c`, and `snapdiff.c` a second time directly from `src/harness/`, because four controller-domain regression tests (`test_config_repl.c`, `test_ha_core_safety.c`, `test_upgrade_snapshot_apply.c`, `test_bug857_regression.c`) call their functions directly and are themselves out of this spec's boundary (controller's own domain tests, untouched). This is a **controller→harness** dependency — the reverse direction from the `ctrlstatus_fmt.h` exception above — and does not violate Requirement 1.4 (which constrains harness's dependencies on controller, not the reverse). It is permanent, not a migration artifact to be removed by a later task. + +### Revalidation Triggers +- Any future xce_test source file that grows a `#include` of a controller-private header (anything under `src/controller/` other than `src/include/xce/ctrlstatus_fmt.h`) breaks this spec's boundary and must be caught in review. +- Any new downstream skill/script that assumes xce_test exists without an explicit harness-build step must be added to Requirement 5's coverage. +- If `ctrlstatus_fmt.h` ever grows beyond a header-only `static inline` function (e.g. needs a `.c` file with real controller state), the shared-header decision must be revisited. + +### Invariant Cross-Check +This spec touches zero controller/scheduler runtime code paths (async I/O thread-role placement, fail-loud/never-self-exit process lifecycle, consensus/replication safety, commit-before-materialize ordering) and adds no persistence/save-restore mechanism — it moves build/test infrastructure and one header-only formatting helper only. `xce-audit-invariants`' controller/scheduler dimension lists and `persistence.md`'s save/restore field-completeness rule are therefore both **not applicable**; noted here so a later reviewer does not have to re-derive that determination. + +## Architecture + +### Existing Architecture Analysis + +Today, `xce_test`'s 15 source files live in `src/controller/testsuite/` and are compiled into **six** separate binaries by `src/controller/meson.build`: the `xce_test` executable itself; 10 of the 15 production files recompiled a second time into `controller_unit_test` alongside their white-box tests (plus 3 more test files with no separate production pairing — `test_xcetest_interrupt.c`, `test_test_cli.c`, `test_xcetest_process_entry.c` — 13 test files total in that group); and the remaining 4 production files (`resval_typeresolve.c`, `joblist_display.c`, `join_worker_args.c`, `listrow_json.c`) each recompiled into their own already-standalone test binary (`resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`). `src/tools/xce-build.sh`'s `controller)` case compiles all of these together and is the sole mechanism that currently makes `xce_test` buildable. A confirmed dependency audit (see `research.md`) found one genuinely shared header (`ctrlstatus_fmt.h`, header-only) between controller's own tests and xce_test's sources. **Correction discovered during task 1.1 implementation**: the audit's "already fully decoupled at the source level" claim was incomplete — four controller-domain regression tests (`test_config_repl.c`, `test_ha_core_safety.c`, `test_upgrade_snapshot_apply.c`, `test_bug857_regression.c`) call functions directly from four of xce_test's own production files (`snapdiff_paths`/`waldump_path_visit`/`walcompare`/`snapverify_path`, i.e. `waldump.c`, `walcompare.c`, `snapverify.c`, `snapdiff.c`). This is a real, permanent controller→harness compile dependency, not a temporary migration artifact — `controller_unit_test_src` now recompiles those four files a second time via a relative path into `src/harness/`. See the updated Allowed Dependencies below. This spec otherwise formalizes the harness/controller split into the build graph and directory layout, preserving the existing 5-test-binary shape (`harness_unit_test` plus the four already-standalone binaries) rather than consolidating it. + +### Architecture Pattern & Boundary Map + +```mermaid +graph TB + subgraph Before + CtrlBuildOld[controller build case] + XceTestOld[xce_test executable] + CtrlUnitOld[controller_unit_test executable] + SharedHeaderOld[ctrlstatus_fmt header] + CtrlBuildOld --> XceTestOld + CtrlBuildOld --> CtrlUnitOld + CtrlUnitOld --> SharedHeaderOld + XceTestOld --> SharedHeaderOld + end + subgraph After + CtrlBuildNew[controller build case] + HarnessBuildNew[harness build case] + XceTestNew[xce_test executable] + HarnessUnitNew[harness_unit_test executable] + CtrlUnitNew[controller_unit_test executable] + SharedHeaderNew[shared include ctrlstatus_fmt] + CtrlBuildNew --> CtrlUnitNew + HarnessBuildNew --> XceTestNew + HarnessBuildNew --> HarnessUnitNew + HarnessUnitNew --> XceTestNew + HarnessUnitNew --> SharedHeaderNew + CtrlUnitNew --> SharedHeaderNew + end +``` + +**Architecture Integration**: +- **Selected pattern**: Directory/build-graph split following the project's existing C-daemon component pattern, with the flat-root variant controller already established as an exception. +- **Domain/feature boundaries**: harness owns everything that is xce_test-specific (its 15 sources + 17 tests, across 5 test binaries — see below); controller keeps everything that is controller-domain (its own daemon sources + its own domain tests); the one shared header moves to the pre-existing neutral `xce/` shared-header prefix (`src/include/xce/`) rather than living inside either component. +- **Existing patterns preserved**: component-per-directory with its own `meson.build`/`CLAUDE.md`; `/xce-build ` dispatch pattern in `xce-build.sh`; meson `test(..., suite: '')` registration pattern; the project's existing practice of splitting a lean, dependency-light module into its own standalone Unity binary rather than folding it into one monolith (already used today for `resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`) — **preserved as-is, not consolidated** (user decision, validate-design review). +- **New components rationale**: `src/harness/` is needed because no existing component directory can host xce_test without re-creating the same coupling this spec removes. Five distinct test-binary targets move with it: the ~13-file group that lives inside `controller_unit_test` today becomes a new `harness_unit_test` binary (needs its own new Unity runner, since `controller_test.c` stays with controller's own remaining tests), and the four already-standalone binaries (`resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`) relocate verbatim with only their `suite:` tag changing from `controller` to `harness`. +- **Steering compliance**: matches `structure.md`'s "component-per-directory, own Meson build + CLAUDE.md" philosophy; matches `build-deployment.md`'s three-part `setup_build` / `meson compile` / `meson test` dispatch pattern per component. + +### Technology Stack + +| Layer | Choice / Version | Role in Feature | Notes | +|-------|------------------|------------------|-------| +| Build | Meson ≥ 1.5 (unchanged) | New `src/harness/meson.build`; `subdir('harness')` added to `src/meson.build` | Same build system, no version change | +| Build orchestration | `src/tools/xce-build.sh` (bash) | New `harness)` case; `controller)` case trimmed | Mirrors the existing `scheduler)`/`worker)` case template exactly | +| C toolchain | gnu17 (unchanged) | Harness sources compile with the same standard/flags as controller | No new dependency | +| Test framework | Unity (unchanged) | New `harness_unit_test` Unity runner, modeled on `controller_test.c` | Reuses the existing `-DXCE_TESTING` pattern where applicable | + +No new external dependencies are introduced; the harness reuses xce_test's existing dependency set unchanged (`libxce`, `librtti`, `struct2json`, `libedit`, `tinylog`, `zlib`, `c_capnproto`, `openssl`, `thread`). + +## File Structure Plan + +### Directory Structure +``` +src/ +├── harness/ # NEW component (this spec) +│ ├── meson.build # xce_test executable + FIVE test executables (see below) +│ ├── CLAUDE.md # component guide +│ ├── include/ # the 15 xce_test-local headers (waldump.h, rcfile.h, ...) +│ ├── test.c # REPL/dispatch entry point (unchanged content, new location) +│ ├── waldump.c ... listrow_json.c # remaining 14 production files (unchanged content) +│ └── testsuite/ # the 17 white-box / integration test files, across 5 binaries: +│ ├── harness_test.c # NEW Unity runner (modeled on controller_test.c) for the +│ │ # 13-file group below -- this is the ONE binary that is new +│ ├── test_waldump.c, test_walcompare.c, test_snapverify.c, test_snapdiff.c, +│ │ test_configcompare.c, test_rcfile.c, test_flagspec.c, test_xcetest_interrupt.c, +│ │ test_interruptcall.c, test_confirm.c, test_listfmt_json.c, test_test_cli.c, +│ │ test_xcetest_process_entry.c # 13 files -> new "harness_unit_test" binary +│ ├── test_resval_typeresolve.c # -> "resval_typeresolve_unit_test" (relocated as-is) +│ ├── test_joblist_display.c # -> "joblist_display_unit_test" (relocated as-is) +│ ├── test_join_worker_args.c # -> "join_worker_args_unit_test" (relocated as-is) +│ └── test_listrow_json.c # -> "listrow_json_unit_test" (relocated as-is) +├── controller/ +│ ├── meson.build # MODIFIED: remove xce_test_src/xce_test_deps/xce_test executable; +│ │ # remove the moved entries from controller_unit_test_src; +│ │ # remove the 4 standalone test-binary blocks that move +│ │ # (resval_typeresolve_unit_test, joblist_display_unit_test, +│ │ # join_worker_args_unit_test, listrow_json_unit_test) +│ └── testsuite/ +│ └── controller_test.c # MODIFIED: remove the 13 extern + RUN_TEST entries for the +│ # functions moving into the new harness_unit_test runner +│ # (test_ctrlstatus stays -- it tests controller's own ctrlstatus.c) +└── include/ + └── xce/ + └── ctrlstatus_fmt.h # MODIFIED location: promoted here from controller/testsuite/ +``` + +> `src/harness/` follows controller's own documented flat-root exception (files directly in the component root, not nested under `src/`) rather than the general worker/scheduler `src/`+`include/` nested pattern — see `research.md`'s Architecture Pattern Evaluation for why. + +### Modified Files +- `src/controller/meson.build` — remove the `xce_test_src`/`xce_test_deps`/`xce_test` executable block; remove the 13 harness-owned entries from `controller_unit_test_src`; remove the four standalone test-binary blocks that move (`resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`, each currently its own `executable()`+`test()` pair). +- `src/controller/testsuite/controller_test.c` — remove the 13 `extern`/`RUN_TEST` entries (lines 68-80 today: `test_waldump` through `test_test_cli`) for the functions moving into the new harness Unity runner; `test_ctrlstatus`'s extern/RUN_TEST stays (it tests controller's own `ctrlstatus.c`). +- `src/meson.build` — add `subdir('harness')`. +- `src/tools/xce-build.sh:292-299` — drop `controller/xce_test` from the `controller)` case's compile target list; add a new `harness)` case following the identical `setup_build` / `meson compile` / conditional `meson test --suite harness` template already used by `scheduler)`/`worker)`. +- `CLAUDE.md` (root) — add `harness` to the `/xce-build` component list; add `src/harness/` to Subdirectory Guidelines pointing at its new `CLAUDE.md`. +- `.kiro/steering/build-deployment.md` — remove the "controller always builds xce_test" claim (current line 24); add `harness` to the `/xce-build` command table and the Meson Structure tree. +- `.kiro/steering/structure.md` — add `harness` to the component table and to the flat-root Exceptions list alongside controller. +- `.claude/skills/xce-test-ha/run-ha-soak.sh`, `.claude/skills/xce-test-ha/SKILL.md` — ensure the harness is built before the soak runs. +- `.claude/skills/xce-deploy-ha/SKILL.md` — document the harness under the existing soft-skip pattern (same treatment as worker/scheduler/xceadmin). +- `.claude/skills/xce-test-multihost/run-multihost-verify.sh`, `.claude/skills/xce-test-multihost/SKILL.md` — ensure the harness is built before verification runs. +- `.claude/skills/xce-test-resources/run-resource-verify.sh`, `.claude/skills/xce-test-resources/SKILL.md` — ensure the harness is built before verification runs. + +## System Flows + +```mermaid +sequenceDiagram + participant Engineer + participant XceBuild as xce build script + participant Meson + participant XceTestBin as xce_test binary + participant HarnessTests as harness_unit_test + + Engineer->>XceBuild: request harness build + XceBuild->>Meson: compile harness targets + Meson->>XceTestBin: build xce_test executable + Meson->>HarnessTests: build harness_unit_test + Note over Meson,HarnessTests: harness_unit_test depends on
XceTestBin already being built + XceBuild->>Meson: run test suite harness + HarnessTests->>XceTestBin: spawn over pty for CLI and process entry tests + HarnessTests-->>XceBuild: pass fail report under suite harness +``` + +**Key decision**: `harness_unit_test`'s two subprocess-spawning tests (`test_test_cli.c`, `test_xcetest_process_entry.c`) require `xce_test` to already be built — `src/harness/meson.build` declares this as an explicit target dependency so meson's own build graph enforces the order, rather than relying on incidental compile-order luck. + +## Requirements Traceability + +| Requirement | Summary | Components | Flows | +|-------------|---------|------------|-------| +| 1.1, 1.2 | Standalone directory matching worker/scheduler-style structure | src/harness/ layout | — | +| 1.3 | Binary name unchanged | xce_test executable target | — | +| 1.4 | No controller-private dependency | ctrlstatus_fmt.h relocation to src/include/xce/ | — | +| 2.1, 2.2 | Independent, decoupled build | xce-build.sh harness)/controller) cases | Build & test sequence | +| 2.3 | Harness is a selectable component | xce-build.sh, root CLAUDE.md | — | +| 2.4 | All-components build includes harness | src/meson.build subdir('harness') | — | +| 3.1, 3.2, 3.4 | Unit tests move and report under harness's own suite, as 5 separate binaries | harness unit-test suite (5 binaries), meson test(suite: harness) | Build & test sequence | +| 3.3 | Controller's own suite unaffected | controller_unit_test + controller_test.c (trimmed, not gutted) | — | +| 4.1, 4.2, 4.3 | Behavioral parity | xce_test executable (content unchanged) | — | +| 5.1, 5.2, 5.3 | Downstream workflows keep working | xce-test-ha, xce-deploy-ha, xce-test-multihost, xce-test-resources | — | +| 5.4 | Clear, actionable failure when harness missing | same four skills | — | +| 6.1, 6.2, 6.3 | Documentation consistency | src/harness/CLAUDE.md, build-deployment.md, structure.md | — | + +## Components and Interfaces + +| Component | Domain/Layer | Intent | Req Coverage | Key Dependencies (P0/P1) | Contracts | +|-----------|--------------|--------|---------------|--------------------------|-----------| +| harness component (xce_test) | C CLI / build | Standalone wire-protocol test client, structurally unchanged | 1.1-1.4, 4.1-4.3 | libxce (P0), src/include/xce/ctrlstatus_fmt.h (P1) | Batch | +| harness unit-test suite (5 binaries) | C test binaries | Runs the 17 moved white-box/integration tests, preserved as 5 separate binaries (not consolidated) under the `harness` suite | 3.1-3.4 | xce_test executable (P0, build-order, `harness_unit_test` only) | Batch | +| xce-build.sh dispatch | Build orchestration | Independent `harness` build entry, decoupled `controller` entry | 2.1-2.4 | Meson (P0) | Batch | +| Downstream verification skills | Workflow automation | Build/verify harness exists before use, with a clear failure path | 5.1-5.4 | harness component (P0) | Batch | +| Component/steering docs | Documentation | Describe harness accurately | 6.1-6.3 | — | — | + +### Build & Test Domain + +#### Harness Component (xce_test) + +| Field | Detail | +|-------|--------| +| Intent | Wire-protocol REPL test client, relocated with no behavioral change | +| Requirements | 1.1, 1.2, 1.3, 1.4, 4.1, 4.2, 4.3 | + +**Responsibilities & Constraints** +- Owns all 15 xce_test production source files and their headers. +- Must not include any controller-private header or source file (Allowed Dependencies lists the one sanctioned exception). +- Must produce byte-identical CLI/REPL output to today's binary. + +**Dependencies** +- Inbound: `harness_unit_test` (P0) — two of its tests spawn this binary directly. +- Outbound: libxce (P0), `src/include/xce/ctrlstatus_fmt.h` (P1, via `test.c`/`joblist_display.c`). +- External: libedit, OpenSSL, zlib, Cap'n Proto runtime — unchanged from today. + +**Contracts**: Batch [x] + +##### Batch / Job Contract +- Trigger: `/xce-build harness` (new) or `/xce-build all`. +- Input / validation: none beyond today's existing CLI argument parsing (unchanged). +- Output / destination: `/harness/xce_test` binary. +- Idempotency & recovery: a rebuild simply recompiles; no state to recover. + +**Implementation Notes** +- Integration: `src/harness/meson.build` defines this executable exactly as `src/controller/meson.build` does today, just relocated. +- Validation: rerun the existing `xce-test-cli-ux`/`xce-test-e2e-primitives`/`xce-test-rc-file` test coverage against the relocated binary and diff output against a pre-move capture. +- Risks: none beyond the header relocation already resolved in `research.md`. + +#### Harness Unit-Test Suite (5 binaries) + +| Field | Detail | +|-------|--------| +| Intent | Unity-based unit/integration test coverage for harness's own sources, preserved as 5 separate binaries matching today's structure (validate-design review: consolidation rejected in favor of file-for-file fidelity) | +| Requirements | 3.1, 3.2, 3.3, 3.4 | + +**Responsibilities & Constraints** +- **`harness_unit_test`** (new binary, new name) — the 13 files that live inside `controller_unit_test` today (`test_waldump.c` through `test_test_cli.c`, listed in the File Structure Plan) plus their production pairs, with a **new** Unity runner `harness_test.c` (modeled on `controller_test.c`, carrying only these 13 `extern`/`RUN_TEST` entries). +- **`resval_typeresolve_unit_test`**, **`joblist_display_unit_test`**, **`join_worker_args_unit_test`**, **`listrow_json_unit_test`** — relocate verbatim from `src/controller/meson.build` (each already a self-contained 2-3 file `executable()`/`test()` pair with no shared runner); only their `include_directories`/`dependencies` source path and `suite:` tag change (`controller` → `harness`). +- All five register under the meson suite name `harness`, distinct from `controller`. +- `harness_unit_test` specifically must not silently pass if the `xce_test` executable its two pty-spawning tests (`test_test_cli.c`, `test_xcetest_process_entry.c`) rely on is stale or missing — that build-order dependency must be a hard prerequisite, not an informal expectation. The other four binaries have no such dependency (they never spawn `xce_test`). + +**Dependencies** +- Inbound: none (leaf targets). +- Outbound: `xce_test` executable (P0, build-order — `harness_unit_test` only, declared via meson's target-dependency mechanism), `src/include/xce/ctrlstatus_fmt.h` (P1 — `joblist_display_unit_test` only, since `joblist_display.c` is its production file). + +**Contracts**: Batch [x] + +##### Batch / Job Contract +- Trigger: `/xce-build harness`'s test step, or `meson test -C --suite harness`. +- Input / validation: none. +- Output / destination: Unity `PASS`/`FAIL` report per test, aggregate suite result per binary. +- Idempotency & recovery: rerunning is safe; no persisted state. + +**Implementation Notes** +- Integration: the four standalone binaries keep the exact registration pattern they already have today (`test(' test', , suite: 'harness', timeout: 60, env: [...])`); `harness_unit_test` mirrors `controller_unit_test`'s own pattern (`suite: 'harness'`, `timeout: 300`). +- Validation: verify via meson's own dependency graph (not just documentation) that `harness_unit_test` cannot start before `xce_test` finishes building; per `testing.md`'s standing rule, cross-check each standalone binary's own stdout `:FAIL:` count, not just meson's aggregate `Ok:N Fail:0`, before treating a run as green. +- Risks: a future edit could accidentally drop the build-order dependency — `research.md`'s follow-up recommends one regression check that the two spawning tests fail loudly (not silently pass) if `xce_test` is absent. A second risk specific to this corrected design: missing one of the four standalone `executable()`/`test()` blocks during the move — mitigated by naming all four explicitly here and in the File Structure Plan rather than referring to "the test files" generically. + +#### xce-build.sh Dispatch + +| Field | Detail | +|-------|--------| +| Intent | Independent `harness` build entry point; `controller` no longer produces xce_test as a side effect | +| Requirements | 2.1, 2.2, 2.3, 2.4 | + +**Responsibilities & Constraints** +- `controller)` case (`xce-build.sh:292-299`) drops `controller/xce_test` from its compile target list. +- New `harness)` case follows the identical three-part template already used by `scheduler)`/`worker)`: `setup_build`, an explicit `meson compile` target list (`harness/xce_test harness/harness_unit_test`), then a conditional `meson test --suite harness`. +- `all)` requires no code change — it already compiles with no explicit target list, so it picks up the new `subdir('harness')` automatically. + +**Dependencies** +- Inbound: engineers, the four downstream verification skills. +- Outbound: Meson (P0). + +**Contracts**: Batch [x] + +##### Batch / Job Contract +- Trigger: `/xce-build harness [profile]`, `/xce-build controller [profile]`, `/xce-build all [profile]`. +- Input / validation: existing profile validation (`asan`/`debug`/`release`) — unchanged. +- Output / destination: component-scoped binaries under the chosen build dir. +- Idempotency & recovery: same full-wipe-vs-`--incremental` semantics that already exist for every other component pair (`build-deployment.md` bug-1565 note) — unchanged behavior, now also applicable to the harness/controller pair. + +**Implementation Notes** +- Integration: no new orchestration concept — this is the same case-block template every other component already uses. +- Validation: run `/xce-build harness` alone (no prior controller build) and confirm `xce_test` is produced; run `/xce-build controller` alone and confirm `xce_test` is absent; run `/xce-build all` and confirm both exist. +- Risks: none beyond the pre-existing, already-documented full-wipe behavior. + +#### Downstream Verification Skills + +| Field | Detail | +|-------|--------| +| Intent | Keep `/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost`, `/xce-test-resources` working once the old coupling is removed | +| Requirements | 5.1, 5.2, 5.3, 5.4 | + +**Responsibilities & Constraints** +- Each workflow must ensure the harness is built before it is needed — either by invoking the harness build itself or by checking for the binary and failing with a clear, actionable message (matching the existing soft-skip pattern `xce-deploy-ha` already uses for worker/scheduler/xceadmin). +- Must not fail with an unrelated error (e.g. a raw "no such file" from a spawned process) when the real cause is "harness was never built." + +**Dependencies** +- Outbound: harness component (P0). + +**Contracts**: Batch [x] + +##### Batch / Job Contract +- Trigger: `/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost`, `/xce-test-resources`. +- Input / validation: presence check for `/harness/xce_test` (or equivalent) before use. +- Output / destination: existing workflow outputs, unchanged; only the failure path for a missing binary changes (from an unrelated/confusing error to `xce-deploy-ha`'s existing clear soft-skip style). +- Idempotency & recovery: re-running after building the harness proceeds normally. + +**Implementation Notes** +- Integration: `xce-deploy-ha` already has a soft-skip precedent (worker/scheduler/xceadmin) to extend to harness rather than inventing a new pattern. +- Validation: run each of the four workflows from a clean build directory (harness never built) and confirm a clear, actionable message rather than a confusing downstream failure. +- Risks: missing one of the four in the actual edit pass — mitigated by the grep-verified inventory in `research.md` (three shell scripts + four `SKILL.md` files, all named explicitly above). + +## Error Handling + +### Error Strategy +This feature has no runtime user-facing error surface (it is build/workflow infrastructure); "errors" here are build-time and workflow-time failure modes. + +### Error Categories and Responses +- **Missing harness binary at workflow time** (Requirement 5.4): the consuming skill must report a clear, actionable message (e.g. "harness not built — run `/xce-build harness` first"), matching `xce-deploy-ha`'s existing soft-skip wording style, rather than surfacing a raw spawn/connect failure. +- **Stale harness binary vs. a fresh `xce_test` change**: prevented structurally by `harness_unit_test`'s explicit build-order dependency on the `xce_test` target (Architecture, System Flows). +- **Build-dir clobbering** (pre-existing project behavior, `build-deployment.md` bug-1565): unchanged — a harness build after a controller build in the same dir without `--incremental` wipes the controller's binaries, exactly as any other two-component pair behaves today; no new mitigation required, only awareness. + +### Monitoring +No new monitoring surface — this is dev/CI-time tooling, not a running service. + +## Testing Strategy + +- **Unit Tests** (Requirement 3): all 17 relocated test files pass under the harness's five `suite: 'harness'` binaries (`harness_unit_test` plus the four relocated standalone binaries) with identical pass/fail semantics to their pre-move behavior; `controller_unit_test`'s remaining test count (and `controller_test.c`'s remaining extern/RUN_TEST list) is unchanged aside from the 13 entries removed for the new `harness_unit_test`, and the four standalone `executable()`/`test()` blocks are confirmed gone from `src/controller/meson.build` (no accidental loss). +- **Integration Tests** (Requirement 2): `/xce-build harness` alone (no prior controller build) produces `xce_test`; `/xce-build controller` alone does not produce `xce_test`; `/xce-build all` produces both; deliberately removing the built `xce_test` binary before running `harness_unit_test` causes `test_test_cli.c`/`test_xcetest_process_entry.c` to fail loudly (proves the build-order dependency is real, not incidental). +- **Workflow Tests** (Requirement 5): run `/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost`, `/xce-test-resources` starting from a clean build directory (harness never built) and confirm each either builds it or fails with a clear, actionable message — never a silent skip or an unrelated error. +- **Behavioral Parity Tests** (Requirement 4): capture `xce_test`'s command/flag/output behavior before the move (via the existing `xce-test-cli-ux`/`xce-test-e2e-primitives`/`xce-test-rc-file` coverage), rerun the same coverage against the relocated binary, and diff for any deviation. + +## Supporting References +- `.kiro/specs/xce-harness-component/research.md` — full discovery log, header-dependency audit, and design-decision rationale. +- `.kiro/specs/xce-harness-component/brief.md` — original discovery brief and locked-in scope decisions. diff --git a/.kiro/specs/xce-harness-component/tasks.md b/.kiro/specs/xce-harness-component/tasks.md new file mode 100644 index 00000000..b4e45f14 --- /dev/null +++ b/.kiro/specs/xce-harness-component/tasks.md @@ -0,0 +1,104 @@ +# Implementation Plan: xce-harness-component + +- [ ] 1. Foundation: relocate xce_test itself and establish the component +- [x] 1.1 Relocate xce_test's production sources, wire its standalone build, and strip every controller-side reference to the moved files + - Move all 15 xce_test source files (`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`) and their headers out of `src/controller/testsuite/` into `src/harness/`, content unchanged + - Create `src/harness/meson.build` defining only the `xce_test` executable so far (same sources/dependencies as today, paths updated); add `subdir('harness')` to `src/meson.build` + - In the same task, from `src/controller/meson.build` remove: the `xce_test_src`/`xce_test_deps`/`xce_test` executable block; every `controller_unit_test_src` entry that references one of the 15 moved files, which is 10 production+test pairs (`waldump`, `walcompare`, `snapverify`, `snapdiff`, `configcompare`, `rcfile`, `flagspec`, `interruptcall`, `confirm`, `listfmt_json`, each with its `test_*.c`) plus `test_xcetest_interrupt.c` (extra `interruptcall.c` coverage) — 11 test files total; and the four entire standalone `executable()`/`test()` blocks (`resval_typeresolve_unit_test`, `joblist_display_unit_test`, `join_worker_args_unit_test`, `listrow_json_unit_test`), since each references one of the 15 moved production files directly + - Also remove those same 11 test files' `extern`/`RUN_TEST` entries from `controller_test.c`. Leave `test_test_cli.c`'s and `test_xcetest_process_entry.c`'s entries in place — neither links any of the 15 moved files (they spawn the built `xce_test` binary as a subprocess instead), so they stay valid in `controller_unit_test` until task 2.1 relocates them + - The 11 removed test files' `.c` sources are left on disk at their current `src/controller/testsuite/` path for now (referenced by no build target) — task 2.1 physically relocates them together with `test_test_cli.c`/`test_xcetest_process_entry.c` + - Observable: a harness-scoped build produces an `xce_test` binary; a controller-scoped build run immediately after this single task (no later tasks applied) still succeeds, `controller_unit_test`'s reported test count has dropped by exactly the 11 removed entries, and none of the four standalone binaries are defined anymore + - _Requirements: 1.1, 1.2, 1.3, 2.1, 2.2, 2.4_ +- [ ] 1.2 Promote the shared ctrlstatus_fmt.h header and repoint its current consumers + - Move `ctrlstatus_fmt.h` to `src/include/xce/`; update the include path in harness's `test.c` and `joblist_display.c` (already relocated in 1.1) and in controller's `test_ctrlstatus.c` + - `joblist_display.c`'s own test file, `test_joblist_display.c`, still lives in `src/controller/testsuite/` at this point and is intentionally left untouched here — its include-path fix happens in task 2.2, when that file relocates + - Observable: a repo-wide search finds no remaining relative `#include "ctrlstatus_fmt.h"`; all three current consumers include it from `xce/ctrlstatus_fmt.h` + - _Requirements: 1.4_ +- [ ] 1.3 Write the harness component guide + - New `src/harness/CLAUDE.md` describing the component's purpose, build command, and structure, matching the style of existing component guides (e.g. `src/controller/CLAUDE.md`) + - Observable: `src/harness/CLAUDE.md` exists and documents the build command and file layout + - _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 + - 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`) + - Observable: `meson test --suite harness` runs `harness_unit_test` with the same pass count these 13 files reported before the move; `meson test --suite controller` run immediately after this same task still runs `controller_unit_test` successfully, with its test count reduced by exactly these final 2 entries (11 were already reflected in task 1.1's count drop) + - _Requirements: 3.1, 3.2, 3.3, 3.4_ +- [ ] 2.2 Relocate the four already-standalone harness test binaries + - Physically relocate the `test_resval_typeresolve.c`, `test_joblist_display.c`, `test_join_worker_args.c`, and `test_listrow_json.c` files into `src/harness/testsuite/` (left on disk at their old path since task 1.1 removed their controller-side `executable()`/`test()` blocks); re-create each of the four `executable()`/`test()` blocks, unchanged in shape, in `src/harness/meson.build` under suite `harness`, referencing their production files already relocated to `src/harness/` in task 1.1 + - Update `test_joblist_display.c`'s `ctrlstatus_fmt.h` include path to `xce/ctrlstatus_fmt.h` as part of this same move + - Observable: `meson test --suite harness` lists all four binaries alongside `harness_unit_test`, each reporting the same pass count as before the move + - _Requirements: 1.4, 3.1, 3.2, 3.4_ + +- [ ] 3. Integration: decouple build orchestration, downstream workflows, and documentation +- [ ] 3.1 Add the harness build entry and decouple it from the controller build + - In `src/tools/xce-build.sh`, drop `controller/xce_test` from the `controller)` case's compile target list; add a new `harness)` case following the identical three-part `setup_build` / `meson compile` / conditional `meson test --suite harness` template already used by `scheduler)`/`worker)` + - Observable: `/xce-build harness` on a fresh build dir (no prior controller build) produces the `xce_test` binary and runs the `harness` suite; `/xce-build controller` alone does not produce `xce_test` + - _Requirements: 2.1, 2.2, 2.3, 2.4_ +- [ ] 3.2 (P) Update the HA soak workflow's harness prerequisite + - Update `.claude/skills/xce-test-ha/run-ha-soak.sh` and `SKILL.md` so the soak ensures the harness is built (or fails with a clear message) instead of assuming a controller build produced it + - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message instead of a raw spawn/connect failure + - _Requirements: 5.1, 5.4_ + - _Boundary: xce-test-ha skill_ + - _Depends: 3.1_ +- [ ] 3.3 (P) Extend the local HA deploy workflow's soft-skip handling to the harness + - Update `.claude/skills/xce-deploy-ha/SKILL.md` to cover the harness binary under its existing worker/scheduler/xceadmin soft-skip pattern + - Observable: deploying from a build dir without the harness built reports the same clear soft-skip style already used for a missing worker/scheduler/xceadmin binary + - _Requirements: 5.2, 5.4_ + - _Boundary: xce-deploy-ha skill_ + - _Depends: 3.1_ +- [ ] 3.4 (P) Update the multi-host verification workflow's harness prerequisite + - Update `.claude/skills/xce-test-multihost/run-multihost-verify.sh` and `SKILL.md` + - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message + - _Requirements: 5.3, 5.4_ + - _Boundary: xce-test-multihost skill_ + - _Depends: 3.1_ +- [ ] 3.5 (P) Update the resource-value verification workflow's harness prerequisite + - Update `.claude/skills/xce-test-resources/run-resource-verify.sh` and `SKILL.md` + - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message + - _Requirements: 5.3, 5.4_ + - _Boundary: xce-test-resources skill_ + - _Depends: 3.1_ +- [ ] 3.6 (P) Update root guide and build/deployment steering for the harness component + - Add `harness` to root `CLAUDE.md`'s `/xce-build` component list and Subdirectory Guidelines; remove `build-deployment.md`'s "controller always builds xce_test" claim and add harness to its `/xce-build` command table and Meson Structure tree + - Observable: `CLAUDE.md` lists `harness` among buildable components; `build-deployment.md` no longer claims the controller build produces `xce_test` + - _Requirements: 6.1, 6.2_ + - _Boundary: CLAUDE.md, build-deployment.md_ + - _Depends: 3.1_ +- [ ] 3.7 (P) Update structure steering to list the harness component + - Add `harness` to `structure.md`'s component table and to its flat-root Exceptions list alongside controller + - Observable: `structure.md`'s component tree and Exceptions section both mention harness + - _Requirements: 6.3_ + - _Boundary: structure.md_ + - _Depends: 1.1_ + +- [ ] 4. Validation: prove behavioral parity and end-to-end correctness +- [ ] 4.1 Verify xce_test's behavioral parity after relocation + - Rerun the existing `xce-test-cli-ux` / `xce-test-e2e-primitives` / `xce-test-rc-file` test coverage against the harness-built `xce_test` binary and diff its command/flag/output behavior against a pre-move capture + - Observable: no behavioral difference is found; every previously-passing scenario in that coverage still passes unchanged + - _Requirements: 4.1, 4.2, 4.3_ + - _Depends: 3.1_ +- [ ] 4.2 Verify the harness_unit_test build-order dependency actually holds + - Deliberately remove the built `xce_test` binary, run `harness_unit_test`, and confirm `test_test_cli.c`/`test_xcetest_process_entry.c` fail loudly rather than silently pass; rebuild normally and confirm the suite passes + - Observable: the deliberate-removal run reports a clear failure from the two pty-spawning tests; the normal run reports the full suite green + - _Requirements: 3.1, 3.2_ + - _Depends: 2.1, 3.1_ +- [ ] 4.3 Verify independent and combined build behavior + - Run `/xce-build harness` alone on a clean build dir and confirm `xce_test` is produced; run `/xce-build controller` alone and confirm `xce_test` is absent; run `/xce-build all` and confirm both exist + - Observable: each of the three build invocations produces exactly the binaries described above + - _Requirements: 2.1, 2.2, 2.4_ + - _Depends: 3.1_ +- [ ] 4.4 Verify every downstream workflow from a clean build state + - Run the HA soak, local HA deploy, multi-host verification, and resource-value verification workflows starting from a build directory where the harness has never been built + - Observable: none of the four workflows fails with a raw/unrelated error; each either succeeds after building the harness itself or reports the expected clear, actionable message + - _Requirements: 5.1, 5.2, 5.3, 5.4_ + - _Depends: 3.2, 3.3, 3.4, 3.5_ + +## Implementation Notes + +- **Task 1.1**: discovered a real gap in the discovery/design audit — `test_config_repl.c`, `test_ha_core_safety.c`, `test_upgrade_snapshot_apply.c`, `test_bug857_regression.c` (controller's own domain tests, out of this spec's boundary) call functions directly from `waldump.c`/`walcompare.c`/`snapverify.c`/`snapdiff.c`. Fixed by having `controller_unit_test_src` recompile those four files a second time from `../harness/`. This is a permanent, sanctioned controller→harness dependency, not a migration artifact — recorded in design.md's Allowed Dependencies. Later tasks: do not assume harness has zero inbound dependency from controller; only assume harness has zero *outbound* dependency on controller (Requirement 1.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. diff --git a/src/controller/meson.build b/src/controller/meson.build index 2ce15933..b4db549f 100644 --- a/src/controller/meson.build +++ b/src/controller/meson.build @@ -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']) diff --git a/src/controller/testsuite/controller_test.c b/src/controller/testsuite/controller_test.c index ed83bb03..37eb3250 100644 --- a/src/controller/testsuite/controller_test.c +++ b/src/controller/testsuite/controller_test.c @@ -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); diff --git a/src/controller/testsuite/configcompare.c b/src/harness/configcompare.c similarity index 100% rename from src/controller/testsuite/configcompare.c rename to src/harness/configcompare.c diff --git a/src/controller/testsuite/confirm.c b/src/harness/confirm.c similarity index 100% rename from src/controller/testsuite/confirm.c rename to src/harness/confirm.c diff --git a/src/controller/testsuite/flagspec.c b/src/harness/flagspec.c similarity index 100% rename from src/controller/testsuite/flagspec.c rename to src/harness/flagspec.c diff --git a/src/controller/testsuite/configcompare.h b/src/harness/include/configcompare.h similarity index 100% rename from src/controller/testsuite/configcompare.h rename to src/harness/include/configcompare.h diff --git a/src/controller/testsuite/confirm.h b/src/harness/include/confirm.h similarity index 100% rename from src/controller/testsuite/confirm.h rename to src/harness/include/confirm.h diff --git a/src/controller/testsuite/flagspec.h b/src/harness/include/flagspec.h similarity index 100% rename from src/controller/testsuite/flagspec.h rename to src/harness/include/flagspec.h diff --git a/src/controller/testsuite/interruptcall.h b/src/harness/include/interruptcall.h similarity index 100% rename from src/controller/testsuite/interruptcall.h rename to src/harness/include/interruptcall.h diff --git a/src/controller/testsuite/joblist_display.h b/src/harness/include/joblist_display.h similarity index 100% rename from src/controller/testsuite/joblist_display.h rename to src/harness/include/joblist_display.h diff --git a/src/controller/testsuite/join_worker_args.h b/src/harness/include/join_worker_args.h similarity index 100% rename from src/controller/testsuite/join_worker_args.h rename to src/harness/include/join_worker_args.h diff --git a/src/controller/testsuite/listfmt_json.h b/src/harness/include/listfmt_json.h similarity index 100% rename from src/controller/testsuite/listfmt_json.h rename to src/harness/include/listfmt_json.h diff --git a/src/controller/testsuite/listrow_json.h b/src/harness/include/listrow_json.h similarity index 100% rename from src/controller/testsuite/listrow_json.h rename to src/harness/include/listrow_json.h diff --git a/src/controller/testsuite/rcfile.h b/src/harness/include/rcfile.h similarity index 100% rename from src/controller/testsuite/rcfile.h rename to src/harness/include/rcfile.h diff --git a/src/controller/testsuite/resval_typeresolve.h b/src/harness/include/resval_typeresolve.h similarity index 100% rename from src/controller/testsuite/resval_typeresolve.h rename to src/harness/include/resval_typeresolve.h diff --git a/src/controller/testsuite/snapdiff.h b/src/harness/include/snapdiff.h similarity index 100% rename from src/controller/testsuite/snapdiff.h rename to src/harness/include/snapdiff.h diff --git a/src/controller/testsuite/snapverify.h b/src/harness/include/snapverify.h similarity index 100% rename from src/controller/testsuite/snapverify.h rename to src/harness/include/snapverify.h diff --git a/src/controller/testsuite/walcompare.h b/src/harness/include/walcompare.h similarity index 100% rename from src/controller/testsuite/walcompare.h rename to src/harness/include/walcompare.h diff --git a/src/controller/testsuite/waldump.h b/src/harness/include/waldump.h similarity index 100% rename from src/controller/testsuite/waldump.h rename to src/harness/include/waldump.h diff --git a/src/controller/testsuite/xcetest_interrupt.h b/src/harness/include/xcetest_interrupt.h similarity index 100% rename from src/controller/testsuite/xcetest_interrupt.h rename to src/harness/include/xcetest_interrupt.h diff --git a/src/controller/testsuite/interruptcall.c b/src/harness/interruptcall.c similarity index 100% rename from src/controller/testsuite/interruptcall.c rename to src/harness/interruptcall.c diff --git a/src/controller/testsuite/joblist_display.c b/src/harness/joblist_display.c similarity index 100% rename from src/controller/testsuite/joblist_display.c rename to src/harness/joblist_display.c diff --git a/src/controller/testsuite/join_worker_args.c b/src/harness/join_worker_args.c similarity index 100% rename from src/controller/testsuite/join_worker_args.c rename to src/harness/join_worker_args.c diff --git a/src/controller/testsuite/listfmt_json.c b/src/harness/listfmt_json.c similarity index 100% rename from src/controller/testsuite/listfmt_json.c rename to src/harness/listfmt_json.c diff --git a/src/controller/testsuite/listrow_json.c b/src/harness/listrow_json.c similarity index 100% rename from src/controller/testsuite/listrow_json.c rename to src/harness/listrow_json.c diff --git a/src/harness/meson.build b/src/harness/meson.build new file mode 100644 index 00000000..de02a13c --- /dev/null +++ b/src/harness/meson.build @@ -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) diff --git a/src/controller/testsuite/rcfile.c b/src/harness/rcfile.c similarity index 100% rename from src/controller/testsuite/rcfile.c rename to src/harness/rcfile.c diff --git a/src/controller/testsuite/resval_typeresolve.c b/src/harness/resval_typeresolve.c similarity index 100% rename from src/controller/testsuite/resval_typeresolve.c rename to src/harness/resval_typeresolve.c diff --git a/src/controller/testsuite/snapdiff.c b/src/harness/snapdiff.c similarity index 100% rename from src/controller/testsuite/snapdiff.c rename to src/harness/snapdiff.c diff --git a/src/controller/testsuite/snapverify.c b/src/harness/snapverify.c similarity index 100% rename from src/controller/testsuite/snapverify.c rename to src/harness/snapverify.c diff --git a/src/controller/testsuite/test.c b/src/harness/test.c similarity index 100% rename from src/controller/testsuite/test.c rename to src/harness/test.c diff --git a/src/controller/testsuite/walcompare.c b/src/harness/walcompare.c similarity index 100% rename from src/controller/testsuite/walcompare.c rename to src/harness/walcompare.c diff --git a/src/controller/testsuite/waldump.c b/src/harness/waldump.c similarity index 100% rename from src/controller/testsuite/waldump.c rename to src/harness/waldump.c diff --git a/src/meson.build b/src/meson.build index cca71ad8..f271f9cd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -64,6 +64,7 @@ if component != 'xcecli' ] subdir('lib') + subdir('harness') subdir('controller') subdir('worker') subdir('autoscaler')