ship: pin the e106 ground truth as a multiset, not a set of names
`static_assembly_matches_runtime_capture` walks the capture's parts and looks each up in ours, so an extra static placement was originally invisible to it — that is how `e303_wep_01` sat here decoded 100x too large. `a21aa5f` closed that with a set-of-names check; BACKLOG.md still says otherwise, and it is wrong. One direction stayed open, though: a set cannot fail on a resource placed *twice* when the capture lists it once. That is not hypothetical — a duplicated instance is what a bad node walk emits, and the two legitimate duplicates here (`e106_eng_01`, `e303_wep_01`) are why the test had to special-case counts at all. So pin the whole multiset — 9 resources, 11 placements, against the capture's 8 dedup'd parts — which subsumes both hand-written counts and fails on an extra, a missing, or a duplicated placement. Refuted before believing: perturbing the expected table (declaring `e106_bdy_01` twice) fails with the real multiset on the left, so the assertion runs against the disc rather than skipping for want of SYLPHEED_ISO. The remaining `for want in &cap.parts` loop guards the table itself: editing it cannot quietly drop a part the runtime actually draws.
This commit is contained in:
@@ -615,31 +615,51 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ── Extras: the direction this test could not previously fail in. ──
|
||||
// ── Extras: the direction this test could not originally fail in. ──
|
||||
// The loop above walks the CAPTURE's parts and looks each up in ours, so
|
||||
// a static placement with no counterpart was invisible to it — which is
|
||||
// a static placement with no counterpart is invisible to it — which is
|
||||
// how a resource decoded 100x too large (`e303_wep_01`, 2026-08-12) sat
|
||||
// here unnoticed. Pin the set instead: the capture legitimately misses
|
||||
// repeated instances of a shared resource (vbase dedup), so `e303_wep_01`
|
||||
// is expected; anything else appearing only in the static assembly is a
|
||||
// regression.
|
||||
let captured: std::collections::BTreeSet<&str> =
|
||||
cap.parts.iter().map(|p| p.part.as_str()).collect();
|
||||
let extra: std::collections::BTreeSet<&str> = placed
|
||||
.iter()
|
||||
.map(|p| p.resource.as_str())
|
||||
.filter(|r| !captured.contains(r))
|
||||
.collect();
|
||||
let allowed: std::collections::BTreeSet<&str> = ["e303_wep_01"].into_iter().collect();
|
||||
// here unnoticed. Pin the whole **multiset** rather than the set of
|
||||
// names: a set cannot fail on a *duplicated* instance of a resource the
|
||||
// capture already lists, and duplication is exactly what a bad node walk
|
||||
// produces. This subsumes the per-resource counts that used to be
|
||||
// asserted separately below.
|
||||
//
|
||||
// Two deviations from the capture are expected, and only these two:
|
||||
// * `e303_wep_01` x2 — a turret shared with `e101`, cross-mounted here.
|
||||
// Absent from the capture because the F10 dump dedups by vertex base
|
||||
// and both instances draw the one buffer.
|
||||
// * `e106_eng_01` x2 — the mirrored engine nacelles, same reason.
|
||||
let mut counts: std::collections::BTreeMap<&str, usize> = Default::default();
|
||||
for p in &placed {
|
||||
*counts.entry(p.resource.as_str()).or_default() += 1;
|
||||
}
|
||||
let expected: std::collections::BTreeMap<&str, usize> = [
|
||||
("e106_bdy_01", 1),
|
||||
("e106_bdy_02", 1),
|
||||
("e106_bdy_03", 1),
|
||||
("e106_bdy_04", 1),
|
||||
("e106_brg_01", 1),
|
||||
("e106_eng_01", 2),
|
||||
("e106_eng_02", 1),
|
||||
("e106_wep_02_01", 1),
|
||||
("e303_wep_01", 2),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
extra, allowed,
|
||||
"static placements with no counterpart in the runtime capture"
|
||||
counts, expected,
|
||||
"the static assembly's placement multiset drifted from the e106 ground truth"
|
||||
);
|
||||
|
||||
// Multi-instance coverage the capture couldn't see (vbase dedup).
|
||||
let count = |res: &str| placed.iter().filter(|p| p.resource == res).count();
|
||||
assert_eq!(count("e106_eng_01"), 2, "both engine nacelles placed");
|
||||
assert_eq!(count("e303_wep_01"), 2, "both shared turrets placed");
|
||||
// …and every name the capture lists must be in that table, so editing the
|
||||
// table cannot quietly drop a part the runtime actually draws.
|
||||
for want in &cap.parts {
|
||||
assert!(
|
||||
expected.contains_key(want.part.as_str()),
|
||||
"{} is in the runtime capture but not in the expected multiset",
|
||||
want.part
|
||||
);
|
||||
}
|
||||
// NEITHER hull reflects: the twins' geometry is mirrored on the disc,
|
||||
// so both placements are proper rotations. This flipped on 2026-08-12 —
|
||||
// while both twins decoded to one buffer, `apply_twin_mirrors` had to
|
||||
|
||||
Reference in New Issue
Block a user