fix(scripts): preserve test-harness counters on re-source - #3271
Conversation
Add a TEST_HARNESS_SOURCED guard so a second source cannot zero recorded pass/fail. Extend the self-test to pin EXIT-trap interaction, verbatim format specifiers, subshell summary-once, accumulation, the empty suite, and re-source idempotency. Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Claude finished @kyle-sexton's task in 40s —— View job Security review complete
No security issues found. This PR touches only Per the skip gate in
|
|
Last security-reviewed head: |
|
Claude finished @kyle-sexton's task in 2m 9s —— View job Code review complete
Scope: FindingsNone. No correctness or maintainability issues that would block this PR. Notes from the review (not blocking):
Unverified by me (author-claimed only, not independently re-run in this review since this lane's tools don't include arbitrary script execution): the |
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
Closes #3283 ## Summary `scripts/lib/test-harness.test.sh` sources the library it tests, so a break in `test_harness::report`'s return path or in `fail()` counter arithmetic is detected by the suite's assertions and then cannot be reported to CI. The process exit status is produced by the code under test. Two mutations against a throwaway copy of the harness demonstrate it: deleting `return 1` from report, and removing the increment from `fail()`. Both print `FAIL:` lines and still exited 0. Checking `_test_harness_fail` after report catches the first mutation but not the second. ## Fix - Parent assertions go through `self_ok`/`self_fail`, which keep an independent pass/fail tally the harness functions do not write. - After `test_harness::report`, a non-zero independent tally forces exit 1 even if the harness counters or return were sabotaged. - The last-line discipline still requires every other sourcer to end on `test_harness::report`; this self-test is the one file allowed to follow the call. - A throwaway-copy mutation regression runs the self-test against both defects and asserts it still exits non-zero. ## Verification - `bash scripts/lib/test-harness.test.sh` — PASS=18 FAIL=0 (includes both mutation cases) - `bash scripts/check-silent-revert.test.sh` — PASS=130 FAIL=0 (installs an EXIT trap and sources the harness) - `bash scripts/check-skill-portability.test.sh` — PASS=92 FAIL=0 (installs an EXIT trap and sources the harness) - `shellcheck --rcfile=.shellcheckrc -x` and `shfmt -d` clean on `scripts/lib/test-harness.test.sh` - `bash -n` clean on `scripts/lib/test-harness.test.sh` ## Related - Refs #3268 — EXIT-trap and re-source gaps that cannot report a sabotaged report/counter - Refs #3271 — those cases landed; they detect both mutations and still cannot report them - Refs #3248 — the shared harness this self-test holds to its exit contract Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Closes #3268
Summary
scripts/lib/test-harness.showns the pass/fail exit contract for thescripts/*.test.shsuites. Its self-test did not cover EXIT-trap interaction, which is why the report is called rather than installed as a trap, and re-sourcing silently zeroed the counters so a recorded failure could exit 0.Fix
TEST_HARNESS_SOURCEDidempotency guard so a second source (direct or transitive) returns without resetting_test_harness_pass/_test_harness_fail.scripts/lib/test-harness.test.shto pin: pre-source and post-source cleanup traps still run and cannot turn a recorded failure into exit 0; a failing cleanup does not turn a clean suite red; a succeeding cleanup does not mask a failure; the summary prints before the caller's EXIT trap; printf format specifiers (%s,%d,%q) print verbatim; the summary prints once despite subshells and subshells do not disturb status; counters accumulate across repeated calls; an empty suite exits 0; re-sourcing preserves a recorded failure.Verification
bash scripts/lib/test-harness.test.sh— PASS=16 FAIL=0bash scripts/check-silent-revert.test.sh— PASS=130 FAIL=0 (installs an EXIT trap and sources the harness)bash scripts/check-skill-portability.test.sh— PASS=92 FAIL=0 (installs an EXIT trap and sources the harness)shellcheck --rcfile=.shellcheckrc -xandshfmt -dclean on both filesbash -nclean on both filesRelated