Skip to content

Stop corpus-gated tests passing without assertions - #2

Merged
dekrom merged 3 commits into
dekrom:mainfrom
terbin:fix-silent-test-skips
Aug 30, 2026
Merged

dekrom merged 3 commits into
dekrom:mainfrom
terbin:fix-silent-test-skips

Conversation

@terbin

@terbin terbin commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Depends on #1 (the NBT recursion-cap fix): this branch carries that fix as its first commit, so merge #1 first and this diff reduces to the harness alone.

Corpus-gated tests returned early and still reported success when the sample data was absent, so the suite looked green while asserting nothing. The corpus resolver moves into a shared support crate that fails loudly when a run declares the data required, the corpus-only tests are #[ignore]d so they read as skipped rather than passed, and property tests cover the decoder without a corpus. Enforced in CI: tests run under nextest, coverage is published without a gate, a guard fails the build if a silent skip reappears, and releases require a passing test job. Minimum Rust is set to 1.89.

@dekrom

dekrom commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Went through this locally: the corpus tests pass on the real sample data and correctly report as ignored without it, and the no-silent-skips guard fires on the old else { return } pattern. Solid.

One small thing before I approve, for consistency:

optional_corpus_decodes_to_exact_eof in tests/legacy.rs reads XAERO_LEGACY_CORPUS with a bare .expect(...), while everything else now goes through resolve_corpus/corpus_root. Because it's #[ignore]d, cargo test --workspace -- --ignored hard-panics unless XAERO_LEGACY_CORPUS is also set — and that one points at a private archive that isn't part of the public corpus, so a normal --ignored run with just the sample data blows up on it.

Can you route the legacy var through the same resolver + a XAERO_LEGACY_CORPUS_REQUIRED gate you built for XAERO_CORPUS? That keeps the fail-loud guarantee for CI when the data is declared required, gives the same clean error message as the rest, and lets --ignored run against the public corpus without tripping over the private one. Everything else is good to merge.

terbin added 3 commits August 28, 2026 09:57
Tests keyed on a sample corpus that CI never has returned early and still reported success, so the suite looked green while asserting nothing. Move the corpus resolver into a shared support crate that fails loudly when a run declares the data required, mark the corpus-only tests ignored so they read as skipped rather than passed, and add decoder property tests that need no corpus. Enforce it: run under nextest, publish coverage without gating on a number, fail the build if a silent skip reappears, and require a green suite before release. Set the minimum Rust to 1.89, the true floor for the current sources and dependencies.
`optional_corpus_decodes_to_exact_eof` read XAERO_LEGACY_CORPUS with a bare env::var().expect(), so it was the one corpus test that did not go through resolve_corpus. Give resolve_corpus the variable name, add legacy_corpus_root() beside corpus_root(), and point the legacy sweep at it, so required mode names the variable the caller actually reads instead of always naming XAERO_CORPUS.

The sweep still stops rather than skipping. It reads a private archive that no CI runner can reach, so nothing can ever declare it required, and a skip there would report a pass while asserting nothing, which is the shape this branch exists to remove. The panic now names the flag that excludes this one test, so running the ignored set against the public corpus does not trip over the private one.

Also correct four module docs and the developer setup guide, which all still described the pre-#[ignore] silent-skip behaviour, and ignore lcov.info so the documented coverage command leaves no untracked file behind.
The test read two hardcoded region paths and continued past either one it could not open. With both absent the loop body never ran, so the test reported a pass having asserted nothing. That is the defect this branch exists to remove: the harness closed the missing-variable case and left the missing-file case open.

Found by pointing XAERO_CORPUS at an empty directory and running the ignored set. Of the 17 corpus tests, this was the only one that passed. It now names the missing file and stops. The no-silent-skips guard cannot catch this shape, because it looks for a missing environment variable rather than a missing file.

Also record that the legacy sweep's test name is quoted in the developer setup guide and in its own panic message, so a rename updates both, and document the nextest form of the ignored-test command alongside the cargo test form.
@terbin
terbin force-pushed the fix-silent-test-skips branch from d8944f4 to f428e0e Compare August 28, 2026 09:02
@terbin

terbin commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Routed it through the resolver. resolve_corpus takes the variable name now, legacy_corpus_root() sits beside corpus_root(), and required mode reports XAERO_LEGACY_CORPUS_REQUIRED=1 but XAERO_LEGACY_CORPUS is unset instead of naming the wrong variable. Unit test on it.

I left the sweep failing rather than skipping, though. The gate only changes one branch, variable unset and not required, and there the resolver yields nothing, so the test has to return early and report a pass having asserted nothing. That's the shape this branch removes. The gate meant to prevent it can't be turned on either: the archive isn't reachable from a GitHub-hosted runner, so nothing can set XAERO_LEGACY_CORPUS_REQUIRED=1. Same is already true of XAERO_CORPUS_REQUIRED, which appears only inside corpus.rs.

Your --ignored problem is real, so I fixed it with a filter instead:

XAERO_CORPUS=/path/to/sample-data cargo test --workspace -- --ignored \
  --skip optional_corpus_decodes_to_exact_eof

That and the nextest equivalent are in START-HERE, and the panic message names the flag. I don't have the corpus here, so I've verified the filtering but not a full green run against real data.

Also fixed: four module docs and the START-HERE dev section still described the old silent-skip behaviour, and lcov.info wasn't gitignored.

One question. test-support/src/synth.rs and trees.rs have no callers anywhere (66 lines, plus the tempfile dep only trees.rs uses). They're pub in a lib crate, so nothing lints them. Scaffolding you have plans for, or should they come out?

Rebased onto main, so the diff is the harness alone. All 7 workflow jobs green on a scratch PR in my fork.

@terbin

terbin commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Two things I hit while in here, both separate from your review.

truncation_never_panics reads two hardcoded region paths and continues past either one it can't open. Point XAERO_CORPUS at an empty directory and it passes having decoded nothing. I ran all 17 ignored corpus tests that way and it was the only one that passed. Fixed in this branch: it now names the missing file and stops. Note the no-silent-skips guard can't catch that shape, since it looks for a missing variable rather than a missing file.

The coverage summary reads 43.30% because the runner has no corpus and skips 18 tests. xaero-merge shows 0.00%, its only test being corpus-gated. Worth excluding corpus-only crates from the published figure or annotating the summary, otherwise it reads as a regression at a glance.

@dekrom dekrom left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally: builds clean, fmt/clippy green, full suite passes against the real sample corpus, and the previously silent corpus tests now fail loudly when the data is absent (confirmed by pointing XAERO_CORPUS at an empty dir). Both the legacy-variable routing and the truncation_never_panics fix are correct. Production changes are test-module only — no runtime behaviour changes. Good to go.

@dekrom
dekrom merged commit 6a08f54 into dekrom:main Aug 30, 2026
7 checks passed
@terbin
terbin deleted the fix-silent-test-skips branch August 30, 2026 22:15
dekrom added a commit that referenced this pull request Sep 4, 2026
PR #2 landed while this branch was being written, and the two efforts met in
the same places:

- The NBT recursion cap is the same fix found twice. Upstream's version is
  kept, along with its nesting tests; the only thing carried over from this
  side is a case for deeply nested lists, which recurse through a different
  arm and cost an attacker five bytes a level.
- The shared test-support crate replaces the per-file corpus helpers this
  branch had grown, including its own XAERO_REQUIRE_CORPUS gate. Upstream's
  <VAR>_REQUIRED convention is more general and is already the contract the
  other tests use, so every corpus test here now resolves through it.
- Three merge-fixture tests and one ingest test still skipped silently when
  the corpus was absent, which is exactly what PR #2 set out to remove. They
  fail loudly now like the rest.

Kept from this branch: the sparse-cut assertion in the corpus round-trip,
which checks that every truncated prefix decodes to a prefix of the whole
rather than assuming a cut always reports itself.

Verified with the corpus present: round-trip still byte-identical, all
seventeen corpus-driven tests pass, fmt, clippy and the wasm check clean.
dekrom added a commit that referenced this pull request Sep 4, 2026
The zvcr world-download importer, a hardening pass over the codec and
ingest paths, and the fail-loud test harness from PR #2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants