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>
182 lines
6.4 KiB
Rust
182 lines
6.4 KiB
Rust
//! Is there a FIELD that says "this bundle is a screen", or only a shape?
|
||
//!
|
||
//! `is_composable` admits 1 786 more bundles than there are screens, most of
|
||
//! them 2–5-element button+glow fragments, and the backlog asks what separates
|
||
//! the two. The obvious place to look is the 32-byte bundle header: six words
|
||
//! besides the magic and the entry count, none of them read by anything.
|
||
//!
|
||
//! This sweeps every composable bundle on the disc and asks two questions the
|
||
//! same way the `opt `/focus sweeps did — by counting, not by looking at one
|
||
//! example:
|
||
//!
|
||
//! 1. do those six header words ever vary at all?
|
||
//! 2. what does the population actually look like — element counts, and how
|
||
//! many carry a full-screen (1280×720) element?
|
||
//!
|
||
//! A negative on (1) is a real answer: it would mean the file does not label a
|
||
//! screen, and a port has to decide by shape or by who references the bundle.
|
||
|
||
use std::collections::HashMap;
|
||
use std::path::{Path, PathBuf};
|
||
|
||
use sylpheed_formats::{pak::PakArchive, ratc, ui_layout};
|
||
|
||
mod common;
|
||
use common::disc_root;
|
||
|
||
fn for_each_build(root: &Path, mut f: impl FnMut(&str, &[u8])) {
|
||
let mut paks: Vec<PathBuf> = std::fs::read_dir(root.join("dat"))
|
||
.expect("dat/")
|
||
.flatten()
|
||
.map(|e| e.path())
|
||
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak"))
|
||
.collect();
|
||
paks.sort();
|
||
for p in &paks {
|
||
let name = p.file_name().unwrap().to_string_lossy().to_string();
|
||
let Ok(arc) = PakArchive::open(p) else {
|
||
continue;
|
||
};
|
||
for e in arc.entries() {
|
||
let Ok(bytes) = arc.read(e) else { continue };
|
||
if ratc::is_ratc(&bytes) {
|
||
f(&name, &bytes);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn be32(b: &[u8], at: usize) -> u32 {
|
||
u32::from_be_bytes([b[at], b[at + 1], b[at + 2], b[at + 3]])
|
||
}
|
||
|
||
#[test]
|
||
fn the_bundle_header_does_not_label_a_screen() {
|
||
let Some(root) = disc_root() else {
|
||
eprintln!("SKIP: extracted disc not found (set SYLPHEED_DISC to enable)");
|
||
return;
|
||
};
|
||
|
||
// header word offset -> value -> how many bundles
|
||
let mut header: HashMap<usize, HashMap<u32, usize>> = HashMap::new();
|
||
let mut bundles = 0usize;
|
||
let mut counts: Vec<usize> = Vec::new();
|
||
let mut with_fullscreen = 0usize;
|
||
// Cross-tabulate the one header word that looks like flags against the two
|
||
// shape signals a "screen" would have: a full-screen element, and size.
|
||
let mut flag_bits: HashMap<u32, (usize, usize, usize)> = HashMap::new(); // bit -> (set, set&fullscreen, set&big)
|
||
|
||
for_each_build(&root, |_pak, bytes| {
|
||
if bytes.len() < 0x20 {
|
||
return;
|
||
}
|
||
let Some(build) = ui_layout::parse_build(bytes) else {
|
||
return;
|
||
};
|
||
if build.from_fallback {
|
||
return;
|
||
}
|
||
bundles += 1;
|
||
counts.push(build.elements.len());
|
||
if build
|
||
.elements
|
||
.iter()
|
||
.any(|e| (e.pivot_x as u64 * 2, e.pivot_y as u64 * 2) == (1280, 720))
|
||
{
|
||
with_fullscreen += 1;
|
||
}
|
||
let fullscreen = build
|
||
.elements
|
||
.iter()
|
||
.any(|e| (e.pivot_x as u64 * 2, e.pivot_y as u64 * 2) == (1280, 720));
|
||
let big = build.elements.len() >= 10;
|
||
let flags = be32(bytes, 0x10);
|
||
for bit in 0..32u32 {
|
||
if flags & (1 << bit) != 0 {
|
||
let e = flag_bits.entry(bit).or_default();
|
||
e.0 += 1;
|
||
if fullscreen {
|
||
e.1 += 1;
|
||
}
|
||
if big {
|
||
e.2 += 1;
|
||
}
|
||
}
|
||
}
|
||
for off in [0x04, 0x08, 0x0c, 0x10, 0x18, 0x1c] {
|
||
*header
|
||
.entry(off)
|
||
.or_default()
|
||
.entry(be32(bytes, off))
|
||
.or_default() += 1;
|
||
}
|
||
});
|
||
|
||
counts.sort_unstable();
|
||
let pct = |p: f64| counts[((counts.len() as f64 - 1.0) * p) as usize];
|
||
eprintln!("composable bundles with a real declaration table: {bundles}");
|
||
eprintln!(
|
||
"element counts: min {} p25 {} median {} p75 {} p95 {} max {}",
|
||
counts[0],
|
||
pct(0.25),
|
||
pct(0.50),
|
||
pct(0.75),
|
||
pct(0.95),
|
||
counts[counts.len() - 1]
|
||
);
|
||
eprintln!("bundles carrying a full-screen (1280x720) element: {with_fullscreen}");
|
||
let mut offs: Vec<_> = header.keys().copied().collect();
|
||
offs.sort();
|
||
for off in offs {
|
||
let vals = &header[&off];
|
||
let mut v: Vec<_> = vals.iter().collect();
|
||
v.sort_by_key(|(_, n)| std::cmp::Reverse(**n));
|
||
eprintln!(
|
||
" header +{off:#04x}: {} distinct value(s), commonest {:?}",
|
||
vals.len(),
|
||
&v[..v.len().min(4)]
|
||
);
|
||
}
|
||
|
||
let mut bits: Vec<_> = flag_bits.iter().collect();
|
||
bits.sort();
|
||
eprintln!(
|
||
" +0x10 bits: bit -> (bundles with it set, of those full-screen, of those >=10 elements)"
|
||
);
|
||
for (bit, (n, fs, big)) in bits {
|
||
eprintln!(" bit {bit:2}: {n:5} full-screen {fs:5} big {big:5}");
|
||
}
|
||
eprintln!(
|
||
" for reference: {bundles} bundles, {with_fullscreen} full-screen, {} with >=10 elements",
|
||
counts.iter().filter(|&&c| c >= 10).count()
|
||
);
|
||
|
||
assert!(bundles > 0, "no composable bundles — the sweep is broken");
|
||
|
||
// MEASURED 2026-08-24. The question was "does a field say this bundle is a
|
||
// screen"; the answer is no, and the numbers that make it no are asserted.
|
||
//
|
||
// The best any bit of the flags word manages is bit 13, set on 403 bundles
|
||
// of which 179 carry a full-screen element — a 44 % hit rate against a
|
||
// 12.8 % base. Enrichment, not a marker. Bit 15 is set on 91 % of ALL
|
||
// bundles, which is the opposite failure.
|
||
let base = with_fullscreen as f64 / bundles as f64;
|
||
for (bit, (n, fs, _big)) in &flag_bits {
|
||
let rate = *fs as f64 / *n as f64;
|
||
assert!(
|
||
rate < 0.95 || *n < 50,
|
||
"bit {bit} looks like a screen marker after all: {fs}/{n} full-screen \
|
||
against a {base:.3} base — re-open the question"
|
||
);
|
||
}
|
||
// The header is NOT dead space, which is the other half of the result.
|
||
assert!(
|
||
header[&0x18].get(&1280).copied().unwrap_or(0) > bundles * 9 / 10,
|
||
"+0x18 is not the design width after all"
|
||
);
|
||
assert!(
|
||
header[&0x1c].get(&720).copied().unwrap_or(0) > bundles * 9 / 10,
|
||
"+0x1c is not the design height after all"
|
||
);
|
||
}
|