Repository navigation
ci: wire test-floor identity ratcheting into report-results job - #221
Conversation
Adds test-floor ratcheting to the Consolidated Test & Benchmark Report job so every test ID that passed on the base ref must pass on head. This catches runtime-visible test erosion (dropped/skipped tests) that static AST counts cannot see. Changes: - Commit baseline JUnit XML from local test run as reports/junit.xml (258 tests, all passing on PHP 8.5.8 Linux x64 NTS) - Add full checkout step in report-results to read the baseline from git - Add merge step combining all platform-specific junit artifacts into a single reports/junit-head.xml for the head report - Add discipline test-floor step with test_report pointing to the merged file; runs only on non-fork PRs (same guard as the existing EnricoMi publish-unit-test-result-action step) CI time impact: <30s total (merge script ~2s + discipline XML parsing). allow-gate-weakening: ci-integrity test-floor identity ratcheting intentionally narrowed to pull_request events only — on push-to-main the merge base equals head so no meaningful ratchet is possible; the step mirrors the existing EnricoMi publish-unit-test-result-action guard which also skips pushes and fork PRs
discipline: passedBase Findings lifted by an override:
|
… git objects The sparse-checkout input makes actions/checkout fetch with --filter=blob:none (partial clone). discipline embeds libgit2, which cannot fetch promisor blobs on demand, so every git-mode gate (assertion-reduction, test-floor, ...) dies in report-results with: gate `assertion-reduction` could not run: object not found - cannot read header for (<sha>); class=Odb (9); code=NotFound (-3) Drop the sparse checkout and use a plain fetch-depth: 0 checkout, matching the discipline sentinel job which already does exactly this and passes. The baseline (reports/junit.xml) and baselines/ are both part of the full tree, so nothing else in the job loses access. Verified locally against origin/main: test-floor git mode passes with an empty base (this PR adds the baseline; the ratchet engages on the next PR once reports/junit.xml exists on main).
Test Results
Benchmark Results
Run Status
Memory (Linux, PHP 8.4)Peak RSS of a child process building one structure, n = 500,000, minus an empty-process floor. Judy's memory advantage is type- and scale-dependent — see BENCHMARK.md for the curve across sizes on a dedicated host.
API Speedups (Linux, PHP 8.4)Speedup = PHP time / Judy time. Bold = Judy faster.
Core Types (Linux, PHP 8.4)Speedup = PHP time / Judy time. Bold = Judy faster.
All types detailCore Types (Linux, PHP 8.4) — AllSpeedup = PHP time / Judy time. Bold = Judy faster.
Release Comparison (2.6.0 → 2.7.1)
Summary: 0 regressions, 97 unchanged, 3 not evaluated (unstable)
3 benchmarks not evaluated (within-arm scatter exceeded the effect being claimed)
Memory ComparisonMemory: 0 regressions, 10 unchanged
Full benchmark outputLinux — PHP 8.4 |
The report-results job still carried a second "Checkout full repo for test-floor baseline" step from the first wiring attempt. actions/checkout runs `git clean -ffdx` by default, so the second checkout deleted the just-downloaded artifacts/ directory (and any untracked file including the merged reports/junit-head.xml) before the merge step ran. Observed in CI: "Merged 0 suites -> reports/junit-head.xml" while the download step had just pulled junit-linux-8.4 (258 tests). The empty head report made the ratchet a silent no-op and gutted the EnricoMi check and the consolidated report too. The single full checkout (fetch-depth: 0, no sparse-checkout) already provides reports/junit.xml, so the second checkout is redundant. Verified the merge script against the real runner layout (artifacts/junit-*/): "Merged 1 suites" -> 258 testcases, and test-floor reports 258 examined with "not present in base ref; identity ratchet will be enforced on subsequent changes".
The ratchet wired in #221 never actually enforced anything. Two bugs: 1. Path mismatch. test_report pointed at reports/junit-head.xml, a filename the merge step generates but that is never committed. In git mode discipline reads the BASE report from the base ref at the same path as test_report, so the lookup always missed and the step passed trivially with "not present in base ref; identity ratchet will be enforced on subsequent changes" -- a promise that could never come true. 2. Serialization mismatch (found on the first live run of the fix). Having pointed test_report at reports/junit.xml, the ratchet then flagged four tests as "dropped" for no reason. Cause: the merge step built the head with xml.dom.minidom.toprettyxml(), which normalizes attribute quoting and HTML-entity escaping (name="...offsetSet's..." instead of name='...offsetSet's...'). The committed baseline is raw run-tests.php output, so every test name containing an apostrophe disagreed with the head and test-floor read it as a drop. Discipline compares the name strings as written, not XML-decoded. Fix both: point test_report at the committed reports/junit.xml baseline (git mode then finds base from the base ref and head from disk at the same path), and merge the head by concatenating the raw <testsuite> blocks from each artifact verbatim, preserving the escaping the baseline uses. Verified with the local v0.18.0 CLI (the same engine CI runs): - fixed wiring, head == baseline: "verified 258 passed base test identity/identities against head report", PASS - minidom-re-serialized head: 4 false "Test Dropped From Suite" errors (the exact names CI flagged) -- reproduced, then fixed by raw concat - one testcase genuinely dropped: caught as Test Dropped From Suite - full local discipline check on the diff: PASS (27 gates)
The ratchet wired in #221 never actually enforced anything. Two bugs: 1. Path mismatch. test_report pointed at reports/junit-head.xml, a filename the merge step generates but that is never committed. In git mode discipline reads the BASE report from the base ref at the same path as test_report, so the lookup always missed and the step passed trivially with "not present in base ref; identity ratchet will be enforced on subsequent changes" -- a promise that could never come true. 2. Serialization mismatch (found on the first live run of the fix). Having pointed test_report at reports/junit.xml, the ratchet then flagged four tests as "dropped" for no reason. Cause: the merge step built the head with xml.dom.minidom.toprettyxml(), which normalizes attribute quoting and HTML-entity escaping (name="...offsetSet's..." instead of name='...offsetSet's...'). The committed baseline is raw run-tests.php output, so every test name containing an apostrophe disagreed with the head and test-floor read it as a drop. Discipline compares the name strings as written, not XML-decoded. Fix both: point test_report at the committed reports/junit.xml baseline (git mode then finds base from the base ref and head from disk at the same path), and merge the head by concatenating the raw <testsuite> blocks from each artifact verbatim, preserving the escaping the baseline uses. Verified with the local v0.18.0 CLI (the same engine CI runs): - fixed wiring, head == baseline: "verified 258 passed base test identity/identities against head report", PASS - minidom-re-serialized head: 4 false "Test Dropped From Suite" errors (the exact names CI flagged) -- reproduced, then fixed by raw concat - one testcase genuinely dropped: caught as Test Dropped From Suite - full local discipline check on the diff: PASS (27 gates)
…#224) The ratchet wired in #221 never actually enforced anything. Two bugs: 1. Path mismatch. test_report pointed at reports/junit-head.xml, a filename the merge step generates but that is never committed. In git mode discipline reads the BASE report from the base ref at the same path as test_report, so the lookup always missed and the step passed trivially with "not present in base ref; identity ratchet will be enforced on subsequent changes" -- a promise that could never come true. 2. Serialization mismatch (found on the first live run of the fix). Having pointed test_report at reports/junit.xml, the ratchet then flagged four tests as "dropped" for no reason. Cause: the merge step built the head with xml.dom.minidom.toprettyxml(), which normalizes attribute quoting and HTML-entity escaping (name="...offsetSet's..." instead of name='...offsetSet's...'). The committed baseline is raw run-tests.php output, so every test name containing an apostrophe disagreed with the head and test-floor read it as a drop. Discipline compares the name strings as written, not XML-decoded. Fix both: point test_report at the committed reports/junit.xml baseline (git mode then finds base from the base ref and head from disk at the same path), and merge the head by concatenating the raw <testsuite> blocks from each artifact verbatim, preserving the escaping the baseline uses. Verified with the local v0.18.0 CLI (the same engine CI runs): - fixed wiring, head == baseline: "verified 258 passed base test identity/identities against head report", PASS - minidom-re-serialized head: 4 false "Test Dropped From Suite" errors (the exact names CI flagged) -- reproduced, then fixed by raw concat - one testcase genuinely dropped: caught as Test Dropped From Suite - full local discipline check on the diff: PASS (27 gates)
What
Adds
test-flooridentity ratcheting to the Consolidated Test & Benchmark Report job (.github/workflows/ci.yml). Every test ID that passed on the base ref must pass on head — a test that was passing on base and becomes missing, skipped, or failed on head is reported asTest Dropped From Suite.Why
The existing static AST count (language pack) can't catch runtime-visible test erosion:
#[cfg(...)],@pytest.mark.skipif)Configuring
test_reportcloses these gaps by ratcheting the set of executed test identities across base and head from JUnit XML.Changes
New baseline:
reports/junit.xmlreport-results job additions:
reports/junit.xmlfrom git (base report); replaces sparse checkoutjunit-*artifacts intoreports/junit-head.xml(head report)discipline check --enable test-floor --test-report reports/junit-head.xml; only on non-fork PRs (same guard as the existing EnricoMi action step)Validation
discipline check --base origin/main: PASS (27 gates, 0 errors, 0 warnings; 1 override for intentional narrowing)CI time impact
<30s total: merge script ~2s + discipline XML parsing.
Gate notes
The
ci-integrity/verification-step-narrowedfinding is lifted by theallow-gate-weakening:directive — test-floor ratcheting intentionally only runs on PRs (on push-to-main the merge base equals head so no meaningful ratchet is possible). This mirrors the existing EnricoMi publish-unit-test-result-action guard which also skips pushes and fork PRs.