From f41dc3d63b296355dd5b3795b54206211ef4e3b3 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 27 Oct 2026 07:59:19 +0100 Subject: [PATCH] Tests: every gate stage asserts how many cases it ran old-reader-refusal.sh pins the expected case count of each stage as a constant (C# stages from the TRX the run writes, by class; Rust, Python and TypeScript from their own output) and fails naming '-count' on any other number, so a probe renamed out of its filter or a skipped leaf test no longer passes with nothing run. Co-Authored-By: Claude Sonnet 5.5 --- .../old-reader-refusal.sh | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/tests/vectors/document-variants-c2a/old-reader-refusal.sh b/tests/vectors/document-variants-c2a/old-reader-refusal.sh index 82d8d1d0..6b22dba1 100755 --- a/tests/vectors/document-variants-c2a/old-reader-refusal.sh +++ b/tests/vectors/document-variants-c2a/old-reader-refusal.sh @@ -17,8 +17,14 @@ # old-reader-refusal.sh gate csharp|typescript|python|rust # Run in a CultLib clone at the revision under test: stage, then probe. For csharp, C2aGateWriter first writes the live # v3 and v5 stores into $GATE_OUT (default /tmp/gate) and the 2.0.1 probes read them beside the committed vectors; the -# 1.0.60 writer then writes a ref-list store there, 2.0.1 and the tree under test both open it, and their REFLIST-SUMMARY -# lines must be identical. +# 1.0.60 writer then writes a ref-list store there, 2.0.1 and the tree under test both open it, write the same documents +# back and reread the file, and their REFLIST-SUMMARY lines (records by schema id, the list's items in order, the Items +# slot's catalog entry, warnings, header) must be identical. +# +# Every stage asserts how many cases it ran, so a probe that was renamed out of its filter or a leaf test that was skipped +# fails the gate by name ('-count') instead of passing with nothing run. The expected counts are the constants below; +# each is confirmed against the probe's source: C#, the TRX the run wrote (cases by class); Rust, the sum of 'N passed'; +# Python, 'Ran N tests'; TypeScript, node's 'pass N'. set -euo pipefail here="tests/vectors/document-variants-c2a" @@ -27,6 +33,15 @@ OLD_READER_REV_CSHARP="${OLD_READER_REV_CSHARP:-b8fc5eb0}" # cultlib-unity-v2. OLD_READER_REV_LEAF="${OLD_READER_REV_LEAF:-02cf04b5}" # cultcache-rs-v0.3.1 OLD_WRITER_REV="${OLD_WRITER_REV:-45c2f400}" # cultlib-unity-v1.0.60 +EXPECT_C2A_WRITER=2 # C2aGateWriter: C2aStamps..., TheCommittedVectorsAreWhatC2aWrites +EXPECT_READER_GATE=12 # OldReaderGateProbe: five TestCaseSource tests over the committed and live stores +EXPECT_READER_REFLIST=1 # OldReaderRefListProbe +EXPECT_OLD_WRITER=1 # OldWriterRefListProbe +EXPECT_HEAD_REFLIST=1 # RefListDriftTests.A_1_0_60_Written_Store_Opens_Like_2_0_1 +EXPECT_RUST=3 # tests/old_reader_v3.rs +EXPECT_PYTHON=4 # test_old_reader_v3.py +EXPECT_TYPESCRIPT=3 # old-reader-v3.test.ts + usage() { echo "usage: $0 stage|probe|gate csharp|typescript|python|rust (writer for stage and probe)" >&2; exit 2; } revision_for() { @@ -73,8 +88,8 @@ stage() { probe() { case "$1" in - csharp) CULTLIB_ROOT="$PWD" dotnet test tests/GameCult.Caching.Tests --filter "FullyQualifiedName~OldReaderGateProbe|FullyQualifiedName~OldReaderRefListProbe" --logger "console;verbosity=detailed" ;; - writer) dotnet test tests/GameCult.Caching.Tests --filter FullyQualifiedName~OldWriterRefListProbe --logger "console;verbosity=detailed" ;; + csharp) CULTLIB_ROOT="$PWD" dotnet test tests/GameCult.Caching.Tests --filter "FullyQualifiedName~OldReaderGateProbe|FullyQualifiedName~OldReaderRefListProbe" --logger "console;verbosity=detailed" --logger "trx;LogFileName=reader.trx" --results-directory "${GATE_TRX_DIR:-TestResults}" ;; + writer) dotnet test tests/GameCult.Caching.Tests --filter FullyQualifiedName~OldWriterRefListProbe --logger "console;verbosity=detailed" --logger "trx;LogFileName=writer.trx" --results-directory "${GATE_TRX_DIR:-TestResults}" ;; typescript) npm install --no-audit --no-fund >/dev/null 2>&1 cd packages/cultcache-ts @@ -100,18 +115,38 @@ probe_staged() { summary_of() { grep -o 'REFLIST-SUMMARY .*' "$1" | sort -u; } +# Cases a TRX records for test classes whose name ends in $2 (every class when $2 is empty); 0 when the run wrote no TRX. +trx_cases() { if [ -f "$1" ]; then grep -o "]*className=\"[^\"]*$2\"" "$1" | wc -l; else echo 0; fi; } + +# Cases a leaf runtime's own output says it ran. +leaf_cases() { + case "$1" in + rust) awk '/^test result:/ { n += $4 } END { print n + 0 }' "$2" ;; + python) awk '/^Ran [0-9]+ test/ { n = $2 } END { print n + 0 }' "$2" ;; + typescript) awk '/^[^[:alnum:]]*pass [0-9]+$/ { n = $NF } END { print n + 0 }' "$2" ;; + esac +} + # Every csharp stage runs, so a writer that stamps the wrong header is also caught by the 2.0.1 reader opening what it wrote; # the gate fails if any stage failed and names them. gate() { + local failures="" + # A stage that ran a number of cases other than the one pinned fails as '-count'. + expect() { if [ "$2" != "$3" ]; then echo "COUNT $1: expected $2 case(s), ran $3"; failures+=" $1-count"; fi; } case "$1" in csharp) - local out="${GATE_OUT:-/tmp/gate}" failures="" + local out="${GATE_OUT:-/tmp/gate}" trx rm -rf "$out"; mkdir -p "$out" - export CULTLIB_GATE_OUT="$out" - dotnet test tests/GameCult.Caching.Tests --filter FullyQualifiedName~C2aGateWriter --logger "console;verbosity=detailed" || failures+=" c2a-writer" + export CULTLIB_GATE_OUT="$out" GATE_TRX_DIR="$out/trx" + trx="$GATE_TRX_DIR" + dotnet test tests/GameCult.Caching.Tests --filter FullyQualifiedName~C2aGateWriter --logger "console;verbosity=detailed" --logger "trx;LogFileName=c2a-writer.trx" --results-directory "$trx" || failures+=" c2a-writer" + expect c2a-writer "$EXPECT_C2A_WRITER" "$(trx_cases "$trx/c2a-writer.trx" C2aGateWriter)" probe_staged writer || failures+=" 1.0.60-writer" + expect 1.0.60-writer "$EXPECT_OLD_WRITER" "$(trx_cases "$trx/writer.trx" OldWriterRefListProbe)" probe_staged csharp 2>&1 | tee "$out/reader.log" || failures+=" 2.0.1-reader" - CULTLIB_GATE_OLD_STORE="$out/v1-0-60-ref-list.cc" dotnet test tests/GameCult.Caching.Tests --filter "FullyQualifiedName~RefListDriftTests.A_1_0_60_Written_Store_Opens_Like_2_0_1" --logger "console;verbosity=detailed" 2>&1 | tee "$out/head.log" || failures+=" head-ref-list" + expect 2.0.1-reader "$EXPECT_READER_GATE+$EXPECT_READER_REFLIST" "$(trx_cases "$trx/reader.trx" OldReaderGateProbe)+$(trx_cases "$trx/reader.trx" OldReaderRefListProbe)" + CULTLIB_GATE_OLD_STORE="$out/v1-0-60-ref-list.cc" dotnet test tests/GameCult.Caching.Tests --filter "FullyQualifiedName~RefListDriftTests.A_1_0_60_Written_Store_Opens_Like_2_0_1" --logger "console;verbosity=detailed" --logger "trx;LogFileName=head.trx" --results-directory "$trx" 2>&1 | tee "$out/head.log" || failures+=" head-ref-list" + expect head-ref-list "$EXPECT_HEAD_REFLIST" "$(trx_cases "$trx/head.trx" RefListDriftTests)" summary_of "$out/reader.log" > "$out/reader.summary" summary_of "$out/head.log" > "$out/head.summary" cat "$out/reader.summary" "$out/head.summary" @@ -122,7 +157,14 @@ gate() { fi if [ -n "$failures" ]; then echo "GATE csharp FAILED:$failures"; return 1; fi echo "GATE csharp ok" ;; - typescript|python|rust) probe_staged "$1" ;; + typescript|python|rust) + local log expected + log="$(mktemp)" + case "$1" in rust) expected="$EXPECT_RUST" ;; python) expected="$EXPECT_PYTHON" ;; typescript) expected="$EXPECT_TYPESCRIPT" ;; esac + probe_staged "$1" 2>&1 | tee "$log" || failures+=" $1" + expect "$1" "$expected" "$(leaf_cases "$1" "$log")" + if [ -n "$failures" ]; then echo "GATE $1 FAILED:$failures"; return 1; fi + echo "GATE $1 ok" ;; *) usage ;; esac }