format: equivalence harness only accepts the known whole-file fallbacks

The harness counted ContiguousStorageRequired from any parser as an
allowed difference, so a converted module that wrongly fell back to the
whole file would still pass. It now accepts the error only from the
three sites that are not converted yet (dense attribute storage, a SOHM
B-tree index, huge fractal-heap objects, all found through a v2 B-tree)
and only in the checks that can reach them; anything else fails with
the check and the site named.

Checked by making LocalHeap::parse_in return the error first: the
fixture and h5py runs fail ("local heap fell back to the whole file").

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-26 14:22:42 -05:00
co-authored by Claude Opus 5.5
parent 052098bf36
commit e5359354b7
@@ -11,9 +11,11 @@
//! be identical, value for value and error for error. //! be identical, value for value and error for error.
//! //!
//! The one allowed difference is [`FormatError::ContiguousStorageRequired`] //! The one allowed difference is [`FormatError::ContiguousStorageRequired`]
//! from the storage path: the structures still indexed by a v2 B-tree (dense //! from the storage path, and only from the structures still indexed by a v2
//! attributes, a SOHM B-tree index, huge fractal-heap objects), which fail //! B-tree (dense attributes, a SOHM B-tree index, huge fractal-heap objects;
//! cleanly instead of reading the whole file. Those are counted. //! see `CONTIGUOUS_REQUIRED`), which fail cleanly instead of reading the
//! whole file. Those are counted; the error from any other site or check
//! fails the harness.
//! //!
//! Milestones M2/M3 extend `check_object` with the raw-data and group //! Milestones M2/M3 extend `check_object` with the raw-data and group
//! parsers as they are converted. //! parsers as they are converted.
@@ -71,6 +73,38 @@ use clawhdf5_format::symbol_table::{SymbolTableMessage, SymbolTableNode};
const MAX_OBJECTS: usize = 1500; const MAX_OBJECTS: usize = 1500;
const MAX_HEAP_IDS: usize = 200; const MAX_HEAP_IDS: usize = 200;
/// The structures that still need the whole file in memory, because they
/// are found through a version-2 B-tree (not converted yet), and the checks
/// that can reach each of them. Anything else answering
/// [`FormatError::ContiguousStorageRequired`] is a converted parser falling
/// back to the whole file, and fails the harness.
const CONTIGUOUS_REQUIRED: &[(&str, &[&str])] = &[
(
"dense attribute storage (a v2 B-tree)",
&["attributes", "attributes (tolerant)"],
),
(
"a shared-message B-tree index",
&[
"SOHM B-tree",
"shared message",
"fill value",
"attributes",
"attributes (tolerant)",
],
),
(
"a huge fractal-heap object's B-tree",
&["heap object", "attributes", "attributes (tolerant)"],
),
];
fn may_require_contiguous(check: &str, site: &str) -> bool {
CONTIGUOUS_REQUIRED
.iter()
.any(|(s, checks)| *s == site && checks.contains(&check))
}
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct Tally { struct Tally {
files: usize, files: usize,
@@ -98,7 +132,13 @@ impl Walk<'_> {
got: &Result<T, FormatError>, got: &Result<T, FormatError>,
) { ) {
self.tally.checks += 1; self.tally.checks += 1;
if let Err(FormatError::ContiguousStorageRequired(_)) = got { if let Err(FormatError::ContiguousStorageRequired(site)) = got {
assert!(
may_require_contiguous(what, site),
"{}: {what} fell back to the whole file ({site}), which only the \
v2-B-tree-indexed structures may do",
self.name
);
self.tally.contiguous_required += 1; self.tally.contiguous_required += 1;
return; return;
} }