use oxideav_opus::{
FrameDecodeStatus, OpusDecoder, OpusPacket, OpusTocByte, RangeDecoder, SilkHeaderBits,
MAX_FRAMES_PER_PACKET, MAX_FRAME_BYTES,
};
#[test]
fn r1_empty_packet_rejected() {
let err = OpusPacket::parse(&[]).expect_err("R1");
assert!(format!("{err}").contains("empty"), "R1: {err}");
}
#[test]
fn r2_implicit_frame_length_capped_at_1275() {
let mut p = vec![0u8; 1 + 1276];
p[0] = 0; assert!(OpusPacket::parse(&p).is_err(), "R2 code 0: 1276 B frame");
let mut p = vec![0u8; 1 + 1275];
p[0] = 0;
let parsed = OpusPacket::parse(&p).expect("R2 code 0 boundary");
assert_eq!(parsed.frames().len(), 1);
assert_eq!(parsed.frames()[0].len(), 1275);
let mut p = vec![0u8; 1 + 2552];
p[0] = 1 << 0; assert!(OpusPacket::parse(&p).is_err(), "R2 code 1: 1276 B each");
let mut p = vec![0u8; 1 + 2550];
p[0] = 1;
let parsed = OpusPacket::parse(&p).expect("R2 code 1 boundary");
assert_eq!(parsed.frames().len(), 2);
assert!(parsed.frames().iter().all(|f| f.len() == 1275));
}
#[test]
fn r3_code1_requires_even_body_length() {
for body_len in 0usize..=8 {
let mut p = vec![0u8; 1 + body_len];
p[0] = 1; let result = OpusPacket::parse(&p);
if body_len % 2 == 0 {
let parsed =
result.unwrap_or_else(|_| panic!("R3: even body_len={body_len} should accept"));
assert_eq!(parsed.frames().len(), 2);
assert_eq!(parsed.frames()[0].len(), body_len / 2);
} else {
assert!(result.is_err(), "R3: odd body_len={body_len} should reject");
}
}
}
#[test]
fn r4_code2_length_byte_truncations_rejected() {
let p = [2u8]; assert!(OpusPacket::parse(&p).is_err(), "R4: missing length byte");
for first in 252u8..=255 {
let p = [2u8, first];
assert!(
OpusPacket::parse(&p).is_err(),
"R4: missing 2nd length byte after first={first}"
);
}
let mut p = vec![2u8, 10];
p.extend_from_slice(&[0u8; 3]);
assert!(
OpusPacket::parse(&p).is_err(),
"R4: declared length 10 > remaining 3"
);
let mut p = vec![2u8, 5];
p.extend_from_slice(&[0u8; 5]);
let parsed = OpusPacket::parse(&p).expect("R4 boundary accept");
assert_eq!(parsed.frames().len(), 2);
assert_eq!(parsed.frames()[0].len(), 5);
assert_eq!(parsed.frames()[1].len(), 0);
}
#[test]
fn r5_code3_frame_count_zero_rejected() {
let p = [3u8, 0x00];
assert!(OpusPacket::parse(&p).is_err(), "R5: M=0");
for m in 1u8..=MAX_FRAMES_PER_PACKET {
let p = [3u8, m << 2]; let parsed = OpusPacket::parse(&p)
.unwrap_or_else(|e| panic!("R5: M={m} should be accepted (got {e})"));
assert_eq!(parsed.frames().len(), m as usize);
for f in parsed.frames() {
assert_eq!(f.len(), 0, "R=0 ⇒ each frame is 0 bytes");
}
}
}
#[test]
fn r6_code3_cbr_r_not_multiple_of_m_rejected() {
let mut p = vec![3u8, 0x0C];
p.extend_from_slice(&[0u8; 7]);
assert!(OpusPacket::parse(&p).is_err(), "R6: R=7, M=3, 7%3=1");
let mut p = vec![3u8, 0x0C];
p.extend_from_slice(&[0u8; 6]);
let parsed = OpusPacket::parse(&p).expect("R6 boundary accept");
assert_eq!(parsed.frames().len(), 3);
for f in parsed.frames() {
assert_eq!(f.len(), 2);
}
}
#[test]
fn r7_code3_vbr_declared_length_overruns_remaining() {
let mut p = vec![3u8, 0x09, 50u8];
p.extend_from_slice(&[0u8; 10]);
assert!(
OpusPacket::parse(&p).is_err(),
"R7: declared 50, only 10 bytes remain after length sequence"
);
let mut p = vec![3u8, 0x09, 5u8];
p.extend_from_slice(&[0u8; 15]);
let parsed = OpusPacket::parse(&p).expect("R7 boundary accept");
assert_eq!(parsed.frames().len(), 2);
assert_eq!(parsed.frames()[0].len(), 5);
assert_eq!(parsed.frames()[1].len(), 10);
}
#[test]
fn code3_padding_chain_truncation_rejected() {
let p = [3u8, 0x06];
assert!(
OpusPacket::parse(&p).is_err(),
"padding length byte missing"
);
let mut p = vec![3u8, 0x06, 200u8];
p.extend_from_slice(&[0u8; 5]);
assert!(OpusPacket::parse(&p).is_err(), "padding > remaining");
}
#[test]
fn code3_padding_chain_255_unterminated_rejected() {
let p = [3u8, 0x06, 255, 255, 255];
assert!(OpusPacket::parse(&p).is_err(), "255-chain runs off end");
}
#[test]
fn toc_byte_total_function_self_consistency() {
for byte in 0u8..=255 {
let toc = OpusTocByte::from_byte(byte);
let config = byte >> 3;
let s = (byte >> 2) & 0x01;
let c = byte & 0x03;
assert_eq!(toc.config, config, "byte=0x{byte:02X} config");
let expected_channels = if s == 0 {
oxideav_opus::ChannelMapping::Mono
} else {
oxideav_opus::ChannelMapping::Stereo
};
assert_eq!(toc.channels, expected_channels, "byte=0x{byte:02X} s");
let expected_code = match c {
0 => oxideav_opus::FrameCountCode::One,
1 => oxideav_opus::FrameCountCode::TwoEqual,
2 => oxideav_opus::FrameCountCode::TwoUnequal,
3 => oxideav_opus::FrameCountCode::Arbitrary,
_ => unreachable!(),
};
assert_eq!(toc.frame_count_code, expected_code, "byte=0x{byte:02X} c");
assert!(
matches!(toc.frame_size_tenths_ms, 25 | 50 | 100 | 200 | 400 | 600),
"byte=0x{byte:02X} frame_size_tenths_ms={}",
toc.frame_size_tenths_ms
);
}
}
#[test]
fn silk_header_decode_truncation_never_panics() {
let full = [0x5Au8; 32];
for stereo in [false, true] {
for &n in &[1u8, 2, 3] {
for take in 1usize..=full.len() {
let buf = &full[..take];
let mut rd = RangeDecoder::new(buf);
let h = SilkHeaderBits::decode(&mut rd, n, stereo).unwrap_or_else(|e| {
panic!("panic-equivalent error n={n} stereo={stereo} take={take}: {e}")
});
assert_eq!(h.num_silk_frames, n);
assert_eq!(h.side.is_some(), stereo);
assert!(
h.mid.vad_flags >> n == 0,
"n={n} take={take} mid VAD high bits set: {:08b}",
h.mid.vad_flags
);
if let Some(s) = h.side {
assert!(
s.vad_flags >> n == 0,
"n={n} take={take} side VAD high bits set: {:08b}",
s.vad_flags
);
}
assert!(
h.per_frame_lbrr.mid >> n == 0,
"n={n} take={take} mid LBRR high bits set: {:08b}",
h.per_frame_lbrr.mid
);
assert!(
h.per_frame_lbrr.side >> n == 0,
"n={n} take={take} side LBRR high bits set: {:08b}",
h.per_frame_lbrr.side
);
}
}
}
}
#[test]
fn silk_header_mono_never_emits_side_state() {
for byte0 in 0u8..=255 {
for &n in &[1u8, 2, 3] {
let buf = [byte0, 0xA3, 0x5C, 0xC5, 0x3A, 0xFF, 0x00, 0x77];
let mut rd = RangeDecoder::new(&buf);
let h = SilkHeaderBits::decode(&mut rd, n, false).expect("decode");
assert!(
h.side.is_none(),
"byte0=0x{byte0:02X} n={n}: mono produced side state"
);
assert_eq!(
h.per_frame_lbrr.side, 0,
"byte0=0x{byte0:02X} n={n}: mono produced side LBRR"
);
}
}
}
#[test]
fn silk_header_40ms_lbrr_bitmap_never_zero_when_global_set() {
for tail in [0u8, 0x11, 0x55, 0xAA, 0x33, 0xC0, 0x3F] {
let buf = [0xFFu8, tail, 0x99, 0x66, 0x42, 0x18, 0x9A, 0x65];
let mut rd = RangeDecoder::new(&buf);
let h = SilkHeaderBits::decode(&mut rd, 2, false).expect("decode");
assert!(
h.mid.lbrr_flag,
"tail=0x{tail:02X}: expected global LBRR=1 with 0xFF seed"
);
assert!(
(1..=3).contains(&h.per_frame_lbrr.mid),
"tail=0x{tail:02X}: 40ms LBRR bitmap {} out of 1..=3",
h.per_frame_lbrr.mid
);
}
}
#[test]
fn silk_header_60ms_lbrr_bitmap_never_zero_when_global_set() {
for tail in [0u8, 0x11, 0x55, 0xAA, 0x33, 0xC0, 0x3F] {
let buf = [0xFFu8, tail, 0x99, 0x66, 0x42, 0x18, 0x9A, 0x65, 0xCD, 0x32];
let mut rd = RangeDecoder::new(&buf);
let h = SilkHeaderBits::decode(&mut rd, 3, false).expect("decode");
assert!(
h.mid.lbrr_flag,
"tail=0x{tail:02X}: expected global LBRR=1 with 0xFF seed"
);
assert!(
(1..=7).contains(&h.per_frame_lbrr.mid),
"tail=0x{tail:02X}: 60ms LBRR bitmap {} out of 1..=7",
h.per_frame_lbrr.mid
);
}
}
#[test]
fn code3_minimal_two_byte_packet_sweep_no_panic() {
for fc in 0u8..=255 {
let p = [3u8, fc];
let v = fc & 0x01 != 0;
let pp = fc & 0x02 != 0;
let m = fc >> 2;
let result = OpusPacket::parse(&p);
let must_accept = (1..=MAX_FRAMES_PER_PACKET).contains(&m) && !pp && (!v || m == 1);
if must_accept {
let parsed = result.unwrap_or_else(|e| {
panic!("fc=0x{fc:02X} v={v} p={pp} M={m}: expected accept ({e})")
});
assert_eq!(parsed.frames().len(), m as usize);
for f in parsed.frames() {
assert_eq!(f.len(), 0);
}
} else {
assert!(
result.is_err(),
"fc=0x{fc:02X} v={v} p={pp} M={m}: expected reject"
);
}
}
}
#[test]
fn r2_code3_vbr_declared_length_capped_at_max_frame_bytes() {
let mut p = vec![3u8, 0x09, 255u8, 255u8];
p.extend_from_slice(&[0u8; 1275 + 5]); let parsed = OpusPacket::parse(&p).expect("R2 boundary code 3 VBR");
assert_eq!(parsed.frames().len(), 2);
assert_eq!(parsed.frames()[0].len(), 1275);
assert_eq!(parsed.frames()[1].len(), 5);
}
#[test]
fn parse_short_packet_sweep_never_panics() {
for c in 0u8..4 {
for stereo in [0u8, 1] {
let toc = (c & 0x03) | (stereo << 2);
for body_len in 0usize..=12 {
let mut p = vec![0u8; 1 + body_len];
p[0] = toc;
let _ = OpusPacket::parse(&p);
}
for pattern in [0xFFu8, 0xFC, 0x00, 0x80, 0x55] {
let mut p = vec![pattern; 1 + 8];
p[0] = toc;
let _ = OpusPacket::parse(&p);
}
}
}
}
#[test]
fn parsed_frames_borrow_inside_packet_bounds() {
let mut p = vec![3u8, (3 << 2) | 0x01, 4u8, 5u8];
p.extend_from_slice(&[0xAAu8; 4]); p.extend_from_slice(&[0xBBu8; 5]); p.extend_from_slice(&[0xCCu8; 6]); let parsed = OpusPacket::parse(&p).expect("parse");
assert_eq!(parsed.frames().len(), 3);
assert!(parsed.frames()[0].iter().all(|&b| b == 0xAA));
assert!(parsed.frames()[1].iter().all(|&b| b == 0xBB));
assert!(parsed.frames()[2].iter().all(|&b| b == 0xCC));
let base = p.as_ptr() as usize;
let end = base + p.len();
for f in parsed.frames() {
let start = f.as_ptr() as usize;
assert!(start >= base && start + f.len() <= end);
}
}
#[test]
fn max_frame_bytes_constant_matches_section_3_2_1() {
assert_eq!(MAX_FRAME_BYTES, 1275);
}
#[test]
fn max_frames_per_packet_constant_matches_r5() {
assert_eq!(MAX_FRAMES_PER_PACKET, 48);
}
#[test]
fn celt_only_decode_truncation_never_panics() {
let toc = 19u8 << 3; let mut body = Vec::with_capacity(64);
let mut x: u32 = 0x1234_5678;
for _ in 0..64 {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
body.push((x & 0xff) as u8);
}
for trunc in 1..=body.len() {
let mut pkt = Vec::with_capacity(1 + trunc);
pkt.push(toc);
pkt.extend_from_slice(&body[..trunc]);
let mut dec = OpusDecoder::new();
let out = dec
.decode_packet(&pkt)
.expect("a well-framed CELT packet decodes (silence on truncation)");
assert_eq!(out.channels, 1, "len {trunc}");
assert_eq!(out.frame_outcomes.len(), 1, "len {trunc}");
assert_eq!(out.pcm.len(), 960, "len {trunc}");
assert_eq!(out.samples_per_channel(), 960, "len {trunc}");
assert!(
matches!(
out.frame_outcomes[0].status,
FrameDecodeStatus::CeltSilence
| FrameDecodeStatus::CeltCoarseEnergyDecoded
| FrameDecodeStatus::CeltAllocationDecoded
| FrameDecodeStatus::CeltDecodeError
),
"len {trunc}: got {:?}",
out.frame_outcomes[0].status
);
}
}
#[test]
fn fuzz_crash_regression_lpc_recurrence_overflow() {
let data: [u8; 18] = [
0x00, 0x08, 0x08, 0x31, 0xa1, 0x5e, 0xa1, 0x31, 0xfd, 0x67, 0x52, 0x26, 0xff, 0x6c, 0x25,
0xff, 0xa1, 0x00,
];
let body = &data[1..];
let mut dec = OpusDecoder::new();
let _ = dec.decode_packet(body);
let _ = dec.decode_self_delimited_packet(body);
}