Files
Sylpheed/crates/sylpheed-formats/tests/slb_leading_segment_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

282 lines
11 KiB
Rust

//! The `.slb` leading segment — recovered, and scoped.
//!
//! `VOICE_D_453` used to decode to 0.14 s because its line lives in a headerless
//! packet stream *before* the first `RIFF`, and the decoder started at the
//! `RIFF`. The banks that looked fine were the ones whose leading segment is
//! silence. One rule, two outcomes.
use std::path::Path;
use sylpheed_formats::{slb, PakArchive};
mod common;
use common::skip_without_disc;
fn bank(root: &Path, n: u32) -> Vec<u8> {
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let path = format!("eng\\etc\\VOICE_D_{n}.slb");
let entry = snd.find_by_name(&path).expect("bank present");
snd.read(entry).expect("read")
}
/// The boundary is arithmetic and has no tunable: the first `RIFF` sits at
/// exactly `HEADERLESS_DATA_OFFSET + n*XMA1_PACKET` in every resupply bank.
#[test]
fn leading_segment_is_a_whole_number_of_packets() {
skip_without_disc!(root);
for (n, packets) in [(450u32, 8usize), (451, 1), (452, 7), (453, 22), (454, 29)] {
let b = bank(&root, n);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
assert!(ri > slb::HEADERLESS_DATA_OFFSET, "VOICE_D_{n}");
let lead = ri - slb::HEADERLESS_DATA_OFFSET;
assert_eq!(
lead % slb::XMA1_PACKET,
0,
"VOICE_D_{n} not a whole packet count"
);
assert_eq!(lead / slb::XMA1_PACKET, packets, "VOICE_D_{n} packet count");
}
}
/// The two banks whose line lives in the leading segment now yield it.
#[test]
fn broken_banks_recover_their_line() {
skip_without_disc!(root);
// (bank, sub-waves expected, payload of the leading one)
for (n, waves, lead_len) in [(453u32, 2usize, 45116usize), (454, 2, 59452)] {
let riffs = slb::to_xma_riffs(&bank(&root, n));
assert_eq!(riffs.len(), waves, "VOICE_D_{n} sub-wave count");
assert_eq!(riffs[0].len(), lead_len, "VOICE_D_{n} leading segment");
// It must be the LARGER part: that is the whole point.
assert!(
riffs[0].len() > riffs[1].len() * 5,
"VOICE_D_{n}: leading segment should dominate"
);
}
}
/// `VOICE_D_451`'s leading region is all zeros — the guard must skip it, so the
/// rule cannot prepend silence to a bank that does not need it.
#[test]
fn all_zero_leading_region_is_skipped() {
skip_without_disc!(root);
let b = bank(&root, 451);
let ri = b.windows(4).position(|w| w == b"RIFF").unwrap();
assert!(
b[slb::HEADERLESS_DATA_OFFSET..ri].iter().all(|x| *x == 0),
"expected an all-zero leading region"
);
// Two sub-waves, both from the RIFF section — no synthesised third.
assert_eq!(slb::to_xma_riffs(&b).len(), 2);
}
fn bank_named(root: &Path, path: &str) -> Vec<u8> {
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let entry = snd
.find_by_name(path)
.unwrap_or_else(|| panic!("{path} present"));
snd.read(entry).expect("read")
}
/// `HEADERLESS_DATA_OFFSET` is the `<lang>\etc\` case, not the format.
///
/// The leading stream is a whole number of packets ending at the first `RIFF`,
/// so its start is `first_riff % XMA1_PACKET`. Disc-wide that takes four values
/// and only 1392 matches the old constant — assuming it elsewhere starts the
/// decode mid-packet. See docs/re/structures/slb-data-offset.md.
#[test]
fn leading_data_offset_is_derived_not_assumed() {
skip_without_disc!(root);
// (bank, expected derived offset). The `etc` banks must still land on the
// old constant — that is the no-regression half of the test.
for (path, want) in [
("eng\\etc\\VOICE_D_452.slb", 1392usize),
("eng\\etc\\VOICE_D_453.slb", 1392),
("eng\\Voice\\VOICE_TCAF_592.slb", 1468),
("jpn\\Voice\\VOICE_TCAF_592.slb", 1728),
("jpn\\etc\\VOICE_D_452.slb", 1600),
] {
let b = bank_named(&root, path);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
let got = slb::leading_data_offset(ri);
assert_eq!(got, want, "{path}: derived offset");
assert_eq!(
(ri - got) % slb::XMA1_PACKET,
0,
"{path}: leading stream is not a whole packet count"
);
assert!(
got == slb::HEADERLESS_DATA_OFFSET || got > slb::HEADERLESS_DATA_OFFSET,
"{path}: offsets below the old constant are unexplained"
);
}
}
/// The banks the old constant mis-decoded now carry a leading sub-wave, and the
/// ones it decoded correctly are untouched.
#[test]
fn derived_offset_recovers_voice_banks_without_regressing_etc() {
skip_without_disc!(root);
for path in [
"eng\\Voice\\VOICE_TCAF_592.slb",
"jpn\\Voice\\VOICE_TCAF_592.slb",
] {
let b = bank_named(&root, path);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
// Under the old constant this leading region was not a whole packet
// count, so `to_xma_riffs` emitted no leading sub-wave at all.
assert_ne!(
(ri - slb::HEADERLESS_DATA_OFFSET) % slb::XMA1_PACKET,
0,
"{path}: expected the OLD constant to mis-align here"
);
let riffs = slb::to_xma_riffs(&b);
assert!(
riffs.len() >= 2,
"{path}: expected a leading sub-wave plus at least one RIFF, got {}",
riffs.len()
);
}
// Control: an `etc` bank still produces what it did before.
let b = bank_named(&root, "eng\\etc\\VOICE_D_452.slb");
assert_eq!(
slb::leading_data_offset(b.windows(4).position(|w| w == b"RIFF").unwrap()),
slb::HEADERLESS_DATA_OFFSET
);
}
/// The scan agrees with the truth wherever the truth is knowable.
///
/// A bank carrying a `RIFF` has its offset *forced* to `first_riff % 2048`, so
/// those banks are a labelled set for a rule meant to serve the ones without a
/// `RIFF`. Over the whole labelled set the scan is right 99.6 % of the time and
/// its only failures are ties. This test walks a slice of it.
#[test]
fn scan_data_offset_agrees_with_the_riff_derived_answer() {
skip_without_disc!(root);
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let mut checked = 0usize;
let mut agreed = 0usize;
for lang in ["eng", "jpn"] {
for (dir, lo, hi) in [("Voice", 1u32, 120u32), ("etc", 1, 120)] {
for n in lo..hi {
let path = format!("{lang}\\{dir}\\VOICE_TCAF_{n:03}.slb");
let Some(entry) = snd.find_by_name(&path) else {
continue;
};
let Ok(b) = snd.read(entry) else { continue };
let Some(ri) = b.windows(4).position(|w| w == b"RIFF") else {
continue;
};
if ri <= slb::HEADERLESS_DATA_OFFSET {
continue;
}
if !b[slb::HEADERLESS_DATA_OFFSET..ri].iter().any(|v| *v != 0) {
continue;
}
checked += 1;
if slb::scan_data_offset(&b) == slb::leading_data_offset(ri) {
agreed += 1;
}
}
}
}
assert!(
checked >= 20,
"expected a usable labelled set, got {checked}"
);
// The whole-disc rate is 99.62%; allow a little slack for a small slice.
let rate = agreed as f64 / checked as f64;
assert!(
rate >= 0.95,
"scan agreed on {agreed}/{checked} ({:.1}%), expected >=95%",
rate * 100.0
);
}
/// Every offset the scan can return is one of the four seen on disc.
#[test]
fn scan_only_returns_known_offsets() {
skip_without_disc!(root);
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let mut seen = 0usize;
for n in 1u32..200 {
for path in [
format!("eng\\Voice\\VOICE_ADAN_{n:03}.slb"),
format!("jpn\\Voice\\VOICE_ADAN_{n:03}.slb"),
] {
let Some(entry) = snd.find_by_name(&path) else {
continue;
};
let Ok(b) = snd.read(entry) else { continue };
let got = slb::scan_data_offset(&b);
assert!(
slb::DATA_OFFSET_CANDIDATES.contains(&got),
"{path}: scan returned {got}, not a known offset"
);
seen += 1;
}
}
assert!(seen >= 20, "expected banks to test, saw {seen}");
}
/// A wave's boundary is exact: `seek` magic sits at `data_at + declared_size`.
///
/// Established 2026-08-26 (docs/re/structures/slb-data-offset.md). Every
/// `RIFF`-bearing entry on the disc satisfies it — **7 620/7 620** in the full
/// sweep — and the `seek` chunk's little-endian packet count at `+12` times
/// 2048 equals the declared size. This is the decoder-independent boundary, and
/// it is what proves the declared sizes honest rather than over-stated.
///
/// The test walks a bounded slice so it stays fast; the identity is disc-wide.
#[test]
fn a_waves_declared_size_is_confirmed_by_the_next_seek() {
skip_without_disc!(root);
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let mut checked = 0usize;
for n in 1u32..400 {
for path in [
format!("eng\\etc\\VOICE_D_{n}.slb"),
format!("eng\\Voice\\VOICE_TCAF_{n:03}.slb"),
format!("jpn\\Voice\\VOICE_ADAN_{n:03}.slb"),
] {
let Some(entry) = snd.find_by_name(&path) else {
continue;
};
let Ok(b) = snd.read(entry) else { continue };
let Some(ri) = b.windows(4).position(|w| w == b"RIFF") else {
continue;
};
let Some(rel) = b[ri..].windows(4).position(|w| w == b"data") else {
continue;
};
let di = ri + rel;
let Some(sz) = b.get(di + 4..di + 8) else {
continue;
};
let declared = u32::from_le_bytes(sz.try_into().unwrap()) as usize;
// The boundary lies outside this entry's own TOC window whenever the
// declared size overruns it, which is the common case — so read from
// the archive's flat data rather than from the entry slice.
let probe = entry.offset as usize + di + 8 + declared;
let Some(tag) = snd.data_at(probe, 16) else {
continue;
};
assert_eq!(
&tag[0..4],
b"seek",
"{path}: expected `seek` at data_at+declared ({probe})"
);
let packets = u32::from_le_bytes(tag[12..16].try_into().unwrap()) as usize;
assert_eq!(
packets * slb::XMA1_PACKET,
declared,
"{path}: seek packet count x 2048 != declared data size"
);
checked += 1;
}
}
assert!(checked >= 30, "expected banks to check, got {checked}");
eprintln!("wave-boundary identity held for {checked} banks");
}