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:
@@ -120,3 +120,105 @@ pub fn mask_bytes(data: &[u8]) -> Vec<u8> {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! Privacy-masking + capture-orchestration tests.
|
||||
//!
|
||||
//! `mask_string` / `mask_bytes` redact identifying characters before
|
||||
//! a drive capture leaves the machine: every ASCII letter → 'A',
|
||||
//! every ASCII digit → '0', everything else (punctuation, spaces,
|
||||
//! control bytes, non-ASCII) is preserved verbatim so structural
|
||||
//! framing (offsets, separators) survives for diffing.
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mask_string_letters_become_a_digits_become_zero() {
|
||||
// Mixed case letters all collapse to 'A'; digits to '0'.
|
||||
assert_eq!(mask_string("HL-DT-ST"), "AA-AA-AA");
|
||||
assert_eq!(mask_string("BU40N"), "AA00A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_string_preserves_non_alnum_punctuation_and_space() {
|
||||
// Separators and spaces must be preserved so the masked output
|
||||
// keeps the same shape as the original (the whole point of a
|
||||
// structure-preserving redaction).
|
||||
assert_eq!(mask_string("1.04"), "0.00");
|
||||
assert_eq!(mask_string("a b-c.d_e"), "A A-A.A_A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_string_preserves_non_ascii_chars() {
|
||||
// is_ascii_alphabetic/is_ascii_digit are false for non-ASCII, so
|
||||
// multibyte chars pass through unchanged (no mojibake, no panic).
|
||||
// 'c','a','f' are ASCII letters → 'A'; 'é' is non-ASCII →
|
||||
// preserved; '9' → '0'.
|
||||
assert_eq!(mask_string("café9"), "AAAé0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_string_empty_is_empty() {
|
||||
assert_eq!(mask_string(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_bytes_matches_string_masking_for_ascii() {
|
||||
// mask_bytes is the byte-wise analogue: letters→b'A', digits→b'0'.
|
||||
assert_eq!(mask_bytes(b"HL-DT-ST"), b"AA-AA-AA".to_vec());
|
||||
assert_eq!(mask_bytes(b"1.04"), b"0.00".to_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_bytes_preserves_non_alnum_and_high_bytes() {
|
||||
// Control bytes (0x00), high bytes (0xFF), and punctuation are
|
||||
// not ASCII alnum and must survive verbatim — INQUIRY payloads
|
||||
// are space-padded binary and the framing must be diffable.
|
||||
let input = [0x00u8, b'A', 0x20, b'7', 0xFF, b'-'];
|
||||
assert_eq!(mask_bytes(&input), vec![0x00, b'A', 0x20, b'0', 0xFF, b'-']);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_bytes_length_preserved() {
|
||||
// Masking is 1:1 — output length always equals input length so
|
||||
// fixed-offset fields stay aligned.
|
||||
let input = vec![0u8; 96];
|
||||
assert_eq!(mask_bytes(&input).len(), 96);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_bytes_empty_is_empty() {
|
||||
assert!(mask_bytes(&[]).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn feature_table_has_no_duplicate_codes() {
|
||||
// capture_drive_data iterates FEATURES once per code; a duplicate
|
||||
// code would silently capture the same feature twice (and bloat
|
||||
// the report). Each MMC-6 feature code must be unique.
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
for &(code, _name) in FEATURES {
|
||||
assert!(seen.insert(code), "duplicate feature code {code:#06x}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn feature_table_includes_aacs_010d() {
|
||||
// AACS (0x010D) is the feature that gates UHD decryption capture;
|
||||
// it must be in the table or AACS drives capture incompletely.
|
||||
assert!(
|
||||
FEATURES.iter().any(|&(c, _)| c == 0x010D),
|
||||
"AACS feature 0x010D must be captured"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn feature_table_codes_are_sorted_ascending() {
|
||||
// The table is maintained in ascending MMC-6 code order; a code
|
||||
// inserted out of order is a maintenance smell that this pins.
|
||||
let codes: Vec<u16> = FEATURES.iter().map(|&(c, _)| c).collect();
|
||||
let mut sorted = codes.clone();
|
||||
sorted.sort_unstable();
|
||||
assert_eq!(codes, sorted, "FEATURES must stay in ascending code order");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user