libfreemkv 0.31.2: comprehensive spec-grounded test suite (~950 tests)

Test-hardening release, no runtime changes. Adds spec-grounded unit tests
across the silent-corruption surfaces — UDF/MPLS/CLPI/IFO parsing, BD/DVD
title + extent assembly, AACS/CSS key handling, TS/PS demux + codec parsers,
MKV/EBML container output, the mux pipeline, sector prefetch + decrypt
decorator, drive/SCSI sense decoding, label extraction, and core I/O. Each
test is grounded in the format spec or real on-disc behavior and verified to
fail under a targeted source mutation. No behavior changed.
This commit is contained in:
Matthew Jackson
2026-06-07 22:28:29 -07:00
parent 2a55bab3ed
commit 8000bae177
85 changed files with 22998 additions and 1 deletions
+152
View File
@@ -318,4 +318,156 @@ mod tests {
assert_eq!(m, 1);
assert_eq!(d, 1);
}
// ── Additional hardening tests ─────────────────────────────────────────
/// Spec: coding_type mismatch with matching language → Divergent (not Match).
/// The spec doc says both coding_type AND language must agree for Match.
/// Mutation: only check language for match → coding_type mismatch silently classified as Match.
#[test]
fn class_divergent_on_coding_type_mismatch_same_lang() {
let r = ClpiVsMplsRow {
pid: 0x1100,
clpi_coding_type: Some(0x83), // TrueHD
clpi_language: Some("eng".into()),
mpls_coding_type: Some(0x86), // DTS-HD MA
mpls_language: Some("eng".into()),
};
assert_eq!(r.class(), ClpiVsMplsClass::Divergent);
}
/// Spec: empty rows → class_counts returns (0,0,0,0). Never panics on empty audit.
/// Mutation: access rows[0] unconditionally → panic on empty audit.
#[test]
fn class_counts_empty_audit() {
let audit = ClpiVsMplsAudit { rows: Vec::new() };
let (co, mo, m, d) = audit.class_counts();
assert_eq!((co, mo, m, d), (0, 0, 0, 0));
}
/// Spec: (false, false) branch — both coding_types absent, equal language → Match.
/// The spec comment says "compare language fields; Divergent if they differ, else Match".
/// Mutation: return Divergent for any (false, false) case → this test goes red.
#[test]
fn class_both_coding_absent_equal_none_lang_is_match() {
let r = ClpiVsMplsRow {
pid: 0x1100,
clpi_coding_type: None,
clpi_language: None,
mpls_coding_type: None,
mpls_language: None,
};
// Both languages are None == None → Match.
assert_eq!(r.class(), ClpiVsMplsClass::Match);
}
/// Spec: all four classes form an exhaustive disjoint cover.
/// This test verifies the discriminant logic using boundary coding_type values.
/// Mutation: swap the ClpiOnly/MplsOnly branches → wrong classification.
#[test]
fn class_boundary_coding_types_all_four_classes_reachable() {
let clpi_only = ClpiVsMplsRow {
pid: 1,
clpi_coding_type: Some(1),
clpi_language: None,
mpls_coding_type: None,
mpls_language: None,
};
let mpls_only = ClpiVsMplsRow {
pid: 2,
clpi_coding_type: None,
clpi_language: None,
mpls_coding_type: Some(1),
mpls_language: None,
};
let match_ = ClpiVsMplsRow {
pid: 3,
clpi_coding_type: Some(0x83),
clpi_language: Some("eng".into()),
mpls_coding_type: Some(0x83),
mpls_language: Some("eng".into()),
};
let divergent = ClpiVsMplsRow {
pid: 4,
clpi_coding_type: Some(0x83),
clpi_language: Some("eng".into()),
mpls_coding_type: Some(0x83),
mpls_language: Some("fra".into()),
};
assert_eq!(clpi_only.class(), ClpiVsMplsClass::ClpiOnly);
assert_eq!(mpls_only.class(), ClpiVsMplsClass::MplsOnly);
assert_eq!(match_.class(), ClpiVsMplsClass::Match);
assert_eq!(divergent.class(), ClpiVsMplsClass::Divergent);
}
/// Spec: class_counts tuple order is (clpi_only, mpls_only, matches, divergent).
/// Verifies each counter increments the RIGHT slot.
/// Mutation: swap any two counters → wrong slot increments.
#[test]
fn class_counts_each_counter_in_correct_slot() {
// One of each class — verify tuple slots separately.
let audit = ClpiVsMplsAudit {
rows: vec![
// 2 ClpiOnly
ClpiVsMplsRow {
pid: 1,
clpi_coding_type: Some(0x83),
clpi_language: None,
mpls_coding_type: None,
mpls_language: None,
},
ClpiVsMplsRow {
pid: 2,
clpi_coding_type: Some(0x82),
clpi_language: None,
mpls_coding_type: None,
mpls_language: None,
},
// 1 MplsOnly
ClpiVsMplsRow {
pid: 3,
clpi_coding_type: None,
clpi_language: None,
mpls_coding_type: Some(0x90),
mpls_language: None,
},
// 3 Match
ClpiVsMplsRow {
pid: 4,
clpi_coding_type: Some(0x83),
clpi_language: Some("eng".into()),
mpls_coding_type: Some(0x83),
mpls_language: Some("eng".into()),
},
ClpiVsMplsRow {
pid: 5,
clpi_coding_type: Some(0x82),
clpi_language: Some("fra".into()),
mpls_coding_type: Some(0x82),
mpls_language: Some("fra".into()),
},
ClpiVsMplsRow {
pid: 6,
clpi_coding_type: Some(0x86),
clpi_language: Some("deu".into()),
mpls_coding_type: Some(0x86),
mpls_language: Some("deu".into()),
},
// 1 Divergent
ClpiVsMplsRow {
pid: 7,
clpi_coding_type: Some(0x83),
clpi_language: Some("eng".into()),
mpls_coding_type: Some(0x83),
mpls_language: Some("spa".into()),
},
],
};
let (co, mo, m, d) = audit.class_counts();
assert_eq!(co, 2, "clpi_only slot");
assert_eq!(mo, 1, "mpls_only slot");
assert_eq!(m, 3, "matches slot");
assert_eq!(d, 1, "divergent slot");
assert_eq!(co + mo + m + d, audit.rows.len(), "all rows accounted for");
}
}