Conformance Policy¶
Reader validation has two levels: strict normalized-output checks and external reference comparisons.
Strict Checks¶
These fields are compared exactly in golden-summary tests:
detected format and reader;
axis unit, kind and order;
signal names, roles, units and signal types;
dimensions;
metadata keys in the typed subset;
provenance source hashes;
warnings and quality flags.
Current goldens live under crates/nirs4all-formats/tests/goldens/ and are checked
by cargo test --workspace. They intentionally summarize arrays instead of
storing full arrays: axis length/first/last, value length/first/last and rounded
value sums. Full-array reference comparisons are added per format when a
trusted external reader is wired in.
To intentionally accept a changed normalized output:
NIRS4ALL_FORMATS_ACCEPT_GOLDENS=1 cargo test -p nirs4all-formats --test goldens
Only use acceptance after reviewing the reader change and updating the relevant format notes.
Floating point summaries use six-decimal rounding. Full reference comparisons use explicit tolerances per format and per reference reader. Defaults are strict and must be relaxed only with a documented reason.
Reference Readers (M2, 2026-05-23)¶
The conformance harness lives under tests/conformance/. Run it with:
pytest -m conformance tests/conformance/
Reference readers wired in M2:
Format |
Reference reader |
Licence |
How it’s invoked |
|---|---|---|---|
Bruker OPUS |
|
MIT |
Direct Python import. |
Galactic SPC |
|
MIT |
Direct Python import. |
JCAMP-DX |
|
MIT |
Direct Python import. |
Spectral Evolution |
|
GPL-3 |
|
SVC/GER |
|
GPL-3 |
|
Allotrope ASM |
canonical ASM JSON schema |
n/a |
|
Generic HDF5 |
|
BSD |
Direct Python import. |
GPL-licensed readers (spectrolab, opusreader2) stay isolated through
subprocess boundaries and are never imported into the MIT runtime library
path. Missing reference readers (R + spectrolab not installed, etc.)
make the matching tests skip rather than fail.
Per-format tolerances¶
The comparison uses abs(a − b) ≤ max(abs_tol, rel_tol × max(|a|, |b|)).
Tolerances live in tests/conformance/tolerances.toml:
Format |
Axis abs |
Axis rel |
Values abs |
Values rel |
Rationale |
|---|---|---|---|---|---|
OPUS |
0 |
1e-12 |
1e-7 |
1e-6 |
Linear axis, CSF scaling matches |
SPC |
1e-6 |
1e-7 |
1e-6 |
1e-6 |
Explicit-X float32 round-trip. |
JCAMP |
1e-1 |
5e-4 |
0 |
1e-9 |
|
SED |
0 |
1e-6 |
0 |
1e-6 |
ASCII text shared by both readers; harness auto-detects the |
SIG |
0 |
1e-6 |
0 |
1e-6 |
Same as SED. |
ASM |
0 |
1e-12 |
0 |
1e-12 |
Bit-identical against the JSON arrays. |
HDF5 |
0 |
0 |
0 |
0 |
Bit-identical against |
Known skips¶
tests/conformance/known_skips.toml documents fixtures whose reference
reader has structural limitations (e.g. jcamp_readfile concatenates
top-level multi-block JCAMP files into a single axis, breaking the
length-pairing logic). Each skip carries a one-line rationale.
Current coverage (2026-05-23)¶
Initial M2 run on the committed corpus: 67 passed, 16 skipped, 0 failed across 7 format harnesses. Skip reasons cluster around:
non-spectral fixtures the reader refuses (vlen strings, peak assignments) — expected refusal path;
empty-axis JCAMP fixtures (TESTFID, TESTSPEC) —
jcamp_readfiledoes not handle those layouts;ambiguous block primacy in two OPUS ICR fixtures — covered separately by golden summaries.
No Reference Case¶
If no reference loader exists, the format can reach Done only after:
fixtures cover controlled variations;
reverse-engineering notes document each decoded field;
adversarial tests cover truncation and corruption;
at least one independent review note is committed.