103 lines
3.9 KiB
Rust
103 lines
3.9 KiB
Rust
//! Determinism fence: the encoder is contractually bit-exact on the
|
|
//! same input, and the decoder is contractually bit-exact on the same
|
|
//! bitstream. This file exists to catch a future change that would
|
|
//! quietly break either property — a parallel order search with a
|
|
//! race, a `HashMap` iteration order leaking into state, a
|
|
//! non-deterministic tie-break. All three would pass the round-trip
|
|
//! tests but fail here.
|
|
//!
|
|
//! Tests drive small deterministic inputs (LFSR noise, sine) so they
|
|
//! don't depend on corpus presence and run in milliseconds.
|
|
|
|
use lac::{decode_frame, encode_frame};
|
|
|
|
// ── Deterministic inputs ────────────────────────────────────────────────────
|
|
|
|
/// 32-bit Galois LFSR, mirrored from `tests/synthetic.rs`. Seeded
|
|
/// deterministically so repeated calls with the same `seed` produce
|
|
/// bit-identical outputs, which is the property we rely on below.
|
|
fn lfsr_noise(n: usize, bit_depth: u8, seed: u32) -> Vec<i32> {
|
|
assert!((1..=24).contains(&bit_depth));
|
|
let mut state = if seed == 0 { 0xACE1_ACE1 } else { seed };
|
|
let shift = 32 - bit_depth as u32;
|
|
let max: i32 = (1i32 << (bit_depth - 1)) - 1;
|
|
(0..n)
|
|
.map(|_| {
|
|
let lsb = state & 1;
|
|
state >>= 1;
|
|
if lsb != 0 {
|
|
state ^= 0x8020_0003;
|
|
}
|
|
((state as i32) >> shift).max(-max)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
// ── Tests ───────────────────────────────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn encode_byte_equal_on_same_input_silence() {
|
|
let samples = vec![0i32; 4096];
|
|
let a = encode_frame(&samples);
|
|
let b = encode_frame(&samples);
|
|
assert_eq!(a, b, "encoder produced different bytes for identical input");
|
|
}
|
|
|
|
#[test]
|
|
fn encode_byte_equal_on_same_input_noise() {
|
|
// 16-bit noise — exercises the non-trivial LPC/Rice search path
|
|
// where any latent non-determinism (tie-break, parallelism, hash
|
|
// ordering) would be most likely to surface.
|
|
let samples = lfsr_noise(4096, 16, 0xDEAD);
|
|
let a = encode_frame(&samples);
|
|
let b = encode_frame(&samples);
|
|
assert_eq!(
|
|
a,
|
|
b,
|
|
"encoder produced different bytes on a noisy input (first {} bytes)",
|
|
a.len().min(16)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_byte_equal_on_same_input_24bit_full_scale() {
|
|
// Full-scale content stresses the autocorrelation accumulator
|
|
// width; any non-determinism in coefficient quantization would
|
|
// show up differently from the silence and noise cases.
|
|
let samples = vec![(1 << 23) - 1; 2048];
|
|
let a = encode_frame(&samples);
|
|
let b = encode_frame(&samples);
|
|
assert_eq!(a, b);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_byte_equal_across_many_repeats() {
|
|
// Ten encodes of the same input — catches an intermittent race
|
|
// that a two-shot comparison might miss by luck. LFSR seed chosen
|
|
// independently from the other tests to reduce correlation
|
|
// between failure modes.
|
|
let samples = lfsr_noise(2048, 20, 0xBEEF);
|
|
let reference = encode_frame(&samples);
|
|
for i in 1..10 {
|
|
let current = encode_frame(&samples);
|
|
assert_eq!(
|
|
current, reference,
|
|
"encode #{} differs from the reference encode",
|
|
i
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn decode_byte_equal_on_same_input() {
|
|
// Decoder determinism: same bitstream, same samples, every time.
|
|
let samples = lfsr_noise(2048, 16, 0xF00D);
|
|
let bytes = encode_frame(&samples);
|
|
let a = decode_frame(&bytes).expect("decode a");
|
|
let b = decode_frame(&bytes).expect("decode b");
|
|
assert_eq!(
|
|
a, b,
|
|
"decoder produced different samples for identical bytes"
|
|
);
|
|
assert_eq!(a, samples, "decoder output doesn't match original samples");
|
|
}
|