Files
Sylpheed/crates/sylpheed-formats/tests/mesh_consistency_disc.rs
Fabian Hamm 15d51b30ac test(formats): make $SYLPHEED_DISC an actual control (#16 remedy 3)
Before this commit, `unset SYLPHEED_DISC` did not disable the disc-backed
suites on the machine that has the disc: every `disc_root()` fell back to a
hardcoded absolute path that exists on this box. The env var looked like a
control and was not one. Same for $SYLPHEED_RES3D and $SYLPHEED_ISO.

Replace the duplicated resolvers with one `tests/common/mod.rs`:

  - 17 local `disc_root()` definitions -> 1
  - 7 copies of the skip macro -> 1 (`skip_without_disc!` and siblings)
  - 16 hardcoded absolute paths -> 0 executable ones
    (3 of those were inline in `mesh_disc.rs`, in no resolver at all,
     and 2 were in `examples/`)
  - `corpus_report.rs` now reports on the SAME resolver the suites use,
    instead of a second copy of the logic its own comments flagged as a
    drift risk.

The 17 copies had already drifted into FIVE variants, and they were not all
the same function. `movie_manifest_disc`, `movie_subtitle_disc` and `slb_disc`
honoured $SYLPHEED_DISC and nothing else, while the other 14 fell back. So one
name already meant two things -- a third instance of the shape #16 is about.
The shared helper adopts the env-only behaviour those three already had, rather
than inventing a sixth variant.

Two module docs still described the fallback after it was deleted, which is the
same defect in prose: `texture_disc` claimed "or the default dev path exists"
and `pak_idxd_disc` said "or drop it at the default dev path below". Both now
say what the code does.

Verified both ways on the machine that HAS the corpus, which is the only place
this refactor can be falsified:

  A  env unset  -> "ABSENT -- $SYLPHEED_DISC unset; its suites self-skip"
                   suites=31 passed=209 failed=0 ignored=14, slowest 0.12s
  B  env set    -> "PRESENT via $SYLPHEED_DISC" (all three corpora)
                   suites=31 passed=209 failed=0 ignored=14,
                   slowest 1235.53s (mesh_consistency_disc)

Identical tallies, opposite corpus states, ~10000x apart in wall clock. (A) is
new behaviour -- it was previously unreachable here. (B) proves nothing broke.

`just test-disc` sources `.env` (already gitignored) for the set case. Note the
quoting trap documented there: the corpus paths contain spaces, and an unquoted
`VAR=a b c` parses as "run command `b`", failing silently into ABSENT -- which
looks exactly like a working skip.

Remedy (1) (`#[ignore]` + `--ignored`) is deliberately NOT done here: (3) already
moves the mode from the filesystem into the environment, and `#[ignore]` already
carries three meanings in this directory (corpus-absent, known-failing, bare).
Overloading it a fourth time would re-create the defect.

`cargo fmt --all --check` clean; no new compiler warnings.

Refs #16

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 20:47:23 +02:00

149 lines
5.5 KiB
Rust

//! Cross-container consistency for XBG7 geometry.
//!
//! A geometry resource shared by several stage containers must decode to the
//! same bounds in each. This needs no ground truth, and on 2026-08-11 it found
//! **125 of 681** shared resources decoding to different bounds while reporting
//! identical vertex and triangle counts — the anchor scan locating a different
//! buffer of the same size (see `docs/re/structures/xbg7-mesh.md`).
//!
//! The test is `#[ignore]`d because the decoder does not satisfy it yet. It is
//! written as the *target* state so that fixing the anchor scan makes it pass,
//! rather than as a snapshot of the bug.
use std::collections::BTreeMap;
use std::path::PathBuf;
use sylpheed_formats::mesh::Xbg7Model;
mod common;
use common::disc_root;
/// Rounded (w, h, d) of a model's own geometry.
fn span(m: &Xbg7Model) -> Option<[i64; 3]> {
let (mut lo, mut hi) = ([f32::MAX; 3], [f32::MIN; 3]);
for s in &m.meshes {
for q in &s.positions {
for k in 0..3 {
lo[k] = lo[k].min(q[k]);
hi[k] = hi[k].max(q[k]);
}
}
}
if lo[0] == f32::MAX {
return None;
}
Some([
(hi[0] - lo[0]).round() as i64,
(hi[1] - lo[1]).round() as i64,
(hi[2] - lo[2]).round() as i64,
])
}
#[test]
#[ignore = "known-failing: 62 of 714 shared resources decode inconsistently (was 125 of 681; distinct anchor assignment + the 0.42 connectivity cap fixed the rest). Note this metric is the WEAKER witness — a systematic mis-anchor is consistent — see docs/re/structures/xbg7-mesh.md"]
fn shared_resources_decode_identically_in_every_container() {
let Some(root) = disc_root() else {
eprintln!("SKIP: extracted disc not found (set SYLPHEED_DISC to enable)");
return;
};
let dir = root.join("hidden/resource3d");
let mut files: Vec<PathBuf> = std::fs::read_dir(&dir)
.expect("resource3d/")
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr"))
.collect();
files.sort();
// name -> (verts, tris) -> set of spans seen
let mut seen: BTreeMap<String, Vec<([i64; 3], usize, usize, String)>> = BTreeMap::new();
for f in &files {
let Ok(bytes) = std::fs::read(f) else {
continue;
};
let where_ = f.file_name().unwrap().to_string_lossy().to_string();
for m in Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false) {
let Some(sp) = span(&m) else { continue };
let v: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
let t: usize = m.meshes.iter().map(|s| s.indices.len() / 3).sum();
seen.entry(m.name.clone())
.or_default()
.push((sp, v, t, where_.clone()));
}
}
let mut bad: Vec<String> = Vec::new();
for (name, list) in &seen {
if list.len() < 2 {
continue;
}
// Only compare decodes that agree on how much geometry they found;
// a differing vertex/triangle count is a different question.
if !list.iter().all(|e| e.1 == list[0].1 && e.2 == list[0].2) {
continue;
}
let spans: std::collections::BTreeSet<[i64; 3]> = list.iter().map(|e| e.0).collect();
if spans.len() > 1 && bad.len() < 10 {
bad.push(format!("{name}: {spans:?}"));
}
}
assert!(
bad.is_empty(),
"{} shared resources decode to different bounds; first: {bad:?}",
bad.len()
);
}
/// Port/starboard twins must not decode to the *same* buffer.
///
/// A runtime capture showed the container stores both halves of the `e106` hull
/// as separate X-reflected buffers, so a `…_01`/`…_02` pair of equal vertex
/// count should come out mirrored (or related by another axis / vertex order) —
/// never identical, which is the collapse distinct assignment fixes. Since that
/// fix reached grouped pools too, this holds for **every** twin pair on the disc
/// — including `n206`, which was the last exception.
#[test]
fn twin_pairs_do_not_share_a_buffer() {
let Some(root) = disc_root() else {
eprintln!("SKIP: extracted disc not found (set SYLPHEED_DISC to enable)");
return;
};
let mut files: Vec<PathBuf> = std::fs::read_dir(root.join("hidden/resource3d"))
.expect("resource3d/")
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr"))
.collect();
files.sort();
let mut collapsed: Vec<String> = Vec::new();
for f in &files {
let Ok(bytes) = std::fs::read(f) else {
continue;
};
let models = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false);
let by_name: BTreeMap<&str, &Xbg7Model> =
models.iter().map(|m| (m.name.as_str(), m)).collect();
for m in &models {
let Some(stem) = m.name.strip_suffix("_01") else {
continue;
};
let Some(t) = by_name.get(format!("{stem}_02").as_str()) else {
continue;
};
let (a, b) = (m.meshes[0].vbuf_offset, t.meshes[0].vbuf_offset);
if a.is_some() && a == b {
collapsed.push(format!(
"{}/{stem}_02 in {}",
m.name,
f.file_name().unwrap().to_string_lossy()
));
}
}
}
assert!(
collapsed.is_empty(),
"twin pairs sharing one buffer: {collapsed:?}"
);
}