test: salvage the orphaned labels/disc triage, and extract build_labels

Thirteen agents triaging src/labels and src/disc died on a saturated
machine, leaving 5,836 insertions across 28 files uncommitted in a
worktree. Recovered by 3-way apply onto twelve commits of drift; zero
conflicts. The diff was archived to freemkv-private first, because a
worktree is not a backup and this one had already nearly been lost.

One production change, and it is the right one: mpls_universal::parse
read every playlist off the disc AND converted the entries to labels in
a single function, so the conversion — stream-type mapping, dedup key,
the dense global counters — could only be reached through a synthetic
UDF image. Extracted to build_labels(&[Playlist]), which unit tests can
drive from already-parsed values. Behaviour-preserving: same iteration
order, same skip-on-error.

Two collisions resolved by hand:

A second mod pass_progress_tests, written independently against the
same survivors as the one committed in c610285. Kept mine — it covers
the distinct-counters case and the Progress blanket impl, which theirs
does not — but theirs had three clamp tests mine lacked: good_pct,
bad_pct and pending_pct also clamp an overshoot, and I had only tested
that for work_pct. Merged those in as one test and proved each of the
three clamps load-bearing by removing them individually.

An unused_parens warning in a new fixture.

Method note, recorded because it cost real time: git apply --3way
STAGES its result, so `git diff` reads empty and the tree looks
untouched. I nearly concluded the patch had silently failed. Worse, the
first attempt piped through `head -20`, so `echo exit=$?` reported
head's status rather than git's — the same mistake this audit has
already documented once. Check the real exit status, and check
--cached, not just the working tree.
This commit is contained in:
Matthew Jackson
2026-07-30 16:36:13 -07:00
parent 8b8bcff106
commit 5360f8d309
28 changed files with 5717 additions and 75 deletions
+149
View File
@@ -324,6 +324,68 @@ mod tests {
assert_eq!(labels[0].qualifier, LabelQualifier::None);
}
/// Spec: `menu_base.prop` lines are skipped when `is_empty() ||
/// starts_with('#')` — either alone is sufficient. A commented-out
/// key=value line must never be parsed into an entry.
/// Mutation: `||` -> `&&` requires both, which a non-empty comment
/// line can't satisfy, so it falls through to `line.find('=')` and
/// gets parsed as a real property.
#[test]
fn menu_base_comment_line_with_equals_is_still_skipped() {
let labels = parse_props(
"#audio_1.class=AudioButton\n\
#audio_1.streamNumber=9\n\
#audio_1.name=Should Not Appear\n\
audio_2.class=AudioButton\n\
audio_2.streamNumber=1\n\
audio_2.name=Real Track\n",
);
assert_eq!(labels.len(), 1, "commented-out entry must not be parsed");
assert_eq!(labels[0].name, "Real Track");
}
/// Spec: `menu_base.prop` streamNumber (or audioStream/subtitleStream)
/// must be strictly positive — `0` means "no STN entry" and must be
/// skipped, matching the `n > 0` guard on the language_streams side.
/// Mutation: `n > 0` -> `n >= 0` (or the guard deleted) would let a
/// stream_num of 0 through, emitting a dead label apply_labels can
/// never match (its counter starts at 1).
#[test]
fn menu_base_zero_stream_number_skipped() {
let labels = parse_props(
"audio_1.class=AudioButton\n\
audio_1.streamNumber=0\n\
audio_1.name=Disabled Slot\n",
);
assert!(
labels.is_empty(),
"streamNumber=0 must be skipped, got {labels:?}"
);
}
/// Spec: `is_subtitle` is `class.contains("SubtitleButton") ||
/// prefix.starts_with("subtitle_")` — EITHER signal alone is
/// sufficient to classify (and keep) a subtitle entry whose prefix
/// doesn't follow the `subtitle_` naming convention.
/// Mutation: `||` -> `&&` would require BOTH signals; an entry whose
/// class says SubtitleButton but whose prefix is something else
/// (e.g. a vendor-specific button id) would then satisfy neither
/// `is_audio` nor `is_subtitle` and get dropped entirely.
#[test]
fn menu_base_subtitle_class_alone_is_sufficient() {
let labels = parse_props(
"menuBtn7.class=SubtitleButton\n\
menuBtn7.streamNumber=1\n\
menuBtn7.name=English SDH\n",
);
assert_eq!(
labels.len(),
1,
"class=SubtitleButton alone must classify as subtitle, not be dropped"
);
assert_eq!(labels[0].stream_type, StreamLabelType::Subtitle);
}
#[test]
fn prefix_commentary_segment_match_not_substring() {
// Genuine commentary group segments match.
@@ -350,6 +412,39 @@ mod tests {
}
}
/// Spec: `merge`'s `mb.iter().find(...)` must match an mb entry by
/// (stream_type AND stream_number) TOGETHER — either alone is not a
/// unique key (there can be an audio #1 and a subtitle #1, or two
/// different audio streams).
/// Mutation: `&&` -> `||` inside the closure would match on type OR
/// number alone, so `.find` (which returns the FIRST match) can pick
/// an mb entry with the right type but the WRONG stream number.
#[test]
fn merge_matches_mb_entry_by_type_and_number_together() {
// ls wants audio #2 (empty name, so it will borrow from mb).
let ls = vec![lbl(StreamLabelType::Audio, 2, "")];
// mb's FIRST audio entry is #1 (wrong number); its #2 entry (the
// real match) comes second.
let mb = vec![
lbl(StreamLabelType::Audio, 1, "Wrong Number Match"),
lbl(StreamLabelType::Audio, 2, "Correct Match"),
];
let merged = merge(ls, mb);
assert_eq!(
merged.len(),
2,
"mb's own audio #1 must also survive as its own entry"
);
let a2 = merged
.iter()
.find(|l| l.stream_type == StreamLabelType::Audio && l.stream_number == 2)
.unwrap();
assert_eq!(
a2.name, "Correct Match",
"must match mb by (type AND number), not type or number alone"
);
}
#[test]
fn merge_preserves_menu_base_only_streams() {
// language_streams covers audio 1; menu_base has audio 1 (name)
@@ -506,6 +601,60 @@ mod tests {
assert_eq!(labels[0].language, "eng");
}
/// Spec: the skip test is `is_empty() || starts_with('#')` — EITHER
/// condition alone must skip the line. A commented-out line that
/// happens to look like valid CSV (a real authoring pattern for
/// disabling a stream entry) must never produce a label.
/// Mutation: `||` -> `&&` requires BOTH conditions, which a non-empty
/// comment line can never satisfy, so it would fall through to the
/// CSV parser and (since it has >= 4 comma fields) emit a spurious
/// label instead of being skipped.
#[test]
fn ls_comment_line_with_csv_shape_is_still_skipped() {
let labels =
parse_language_streams_text("#id,audio_production,1,eng\nid2,audio_production,2,fra\n");
assert_eq!(
labels.len(),
1,
"the commented-out CSV-shaped line must not parse"
);
assert_eq!(labels[0].language, "fra");
}
/// Spec: `subtitle_dual` is a recognized subtitle type (Normal/no
/// qualifier). Mutation: delete this match arm → falls to the
/// catch-all `_ => continue`, silently dropping the stream.
#[test]
fn ls_subtitle_dual_parsed() {
let labels = parse_language_streams_text("id,subtitle_dual,1,eng\n");
assert_eq!(labels.len(), 1, "subtitle_dual must produce a label");
assert_eq!(labels[0].stream_type, StreamLabelType::Subtitle);
assert_eq!(labels[0].purpose, LabelPurpose::Normal);
assert_eq!(labels[0].qualifier, LabelQualifier::None);
}
/// Spec: `subtitle_bonus` is a recognized subtitle type (Normal/no
/// qualifier). Mutation: delete this match arm → dropped as unknown.
#[test]
fn ls_subtitle_bonus_parsed() {
let labels = parse_language_streams_text("id,subtitle_bonus,2,eng\n");
assert_eq!(labels.len(), 1, "subtitle_bonus must produce a label");
assert_eq!(labels[0].stream_type, StreamLabelType::Subtitle);
assert_eq!(labels[0].purpose, LabelPurpose::Normal);
}
/// Spec: `subtitle_ime` maps to Subtitle/Ime (no Forced qualifier,
/// unlike `subtitle_ime_narrative`).
/// Mutation: delete this match arm → dropped as unknown.
#[test]
fn ls_subtitle_ime_parsed() {
let labels = parse_language_streams_text("id,subtitle_ime,3,jpn\n");
assert_eq!(labels.len(), 1, "subtitle_ime must produce a label");
assert_eq!(labels[0].stream_type, StreamLabelType::Subtitle);
assert_eq!(labels[0].purpose, LabelPurpose::Ime);
assert_eq!(labels[0].qualifier, LabelQualifier::None);
}
/// Spec: multiple valid lines produce multiple labels.
/// Mutation: stop after first label → only 1 label returned.
#[test]