Testing and release gates¶
GFFBase separates quick developer feedback from compatibility, large-corpus,
and publication work. A bare pytest intentionally excludes the latter tiers.
Test tiers¶
Tier |
Command |
Purpose |
|---|---|---|
Default |
|
Deterministic unit, integration, regression, and bounded property tests |
B-tree |
|
Run the same behavior through the spatial fallback |
Parity |
|
Compare the public compatibility surface with the pinned gffutils oracle |
Slow |
|
Longer local cases excluded from normal iteration |
Corpus |
|
Full real-annotation ingestion and structural validation |
Rust |
|
Native parser and Rust-level edge cases |
Docs |
|
API imports, links, generated content, and navigation |
Use an explicit marker expression when combining tiers, for example
pytest -m "slow or corpus". Corpus tests require the verified files in
benchmarks/data/ and should use a scratch directory on a volume with enough
space.
Parser and property testing¶
The native and fallback parsers are independent implementations of one public contract. Generated cases compare records, warnings, metadata, and failure classes across file, gzip, and byte entry points. Round-trip properties cover Unicode, quoting, reserved characters, and percent encoding. Test generation is bounded in normal CI; a longer profile is available for scheduled or manual hardening runs. Every minimized failure becomes a deterministic regression.
Database oracles¶
Small generated DAGs provide an implementation-independent reference for parents, children, closure depth, and mutation. Tests compare:
closure results with recursive traversal of direct edges;
scalar and batched APIs;
R-tree and B-tree region results;
state before and after update, delete, and relationship mutation;
default conflict rejection with explicit multipart and unique-creation policies.
Mutation tests also corrupt internal tables deliberately. The validator must detect missing, extra, and wrong-depth closure rows, orphan attributes, and incoherent synthetic parents.
Installation modes¶
Release confidence requires more than an editable checkout:
Build a clean wheel outside the source package directory.
Install it into an empty environment.
Verify the imported package and native-extension paths, versions, and wheel checksum.
Run parser sentinels and a small database round trip.
Separately exercise editable native and forced fallback installations.
A Python/native version mismatch is a hard failure. Do not repair it by adding
the repository’s python/ directory to PYTHONPATH; rebuild the extension or
install the intended wheel.
CI and release acceptance¶
CI covers supported operating systems and Python versions, R-tree/B-tree routing, minimum and current dependencies, a clean-wheel smoke test, the pinned parity oracle, Rust lint/tests, typing, formatting, and strict docs. Coverage may not fall below the platform gate, but coverage alone is not evidence of correctness: every new decision branch needs an assertion about its output or failure.
Large-corpus and cluster benchmark runs are release evidence rather than normal PR jobs. A tag-ready candidate requires their recorded input hashes, successful full validation, matching correctness signatures, and a clean tracked tree.
Three checks most suites do not have¶
Differential parity against a pinned oracle. The parity tier compares
gffbase against a git-pinned gffutils build rather than against recorded
expectations. Every deliberate difference is declared in a register, and the
suite fails both on an undeclared gap and on a declaration that has gone stale,
so the compatibility claim cannot drift in either direction.
The documentation’s own code runs. Every runnable snippet in docs/,
README.md and MIGRATION.md is extracted and executed against the
vendored fixtures, so a documented example cannot rot unnoticed. Skipping is
opt-out and must state a reason, and the number of exemptions is a ratchet that
may fall but never rise – which keeps “just skip it” from becoming the easy fix
for a failing example.
Release invariants. Version agreement across every file that states a version, packaging manifests resolving to real files, workflow YAML that GitHub will actually load, documentation links that resolve to pages which exist, and every published benchmark table matching the committed measurements it claims to come from. These run in the ordinary suite, not only at release time, because a release-day failure is the most expensive place to learn about them.