test(jitter): state-transition edge cases for JitterBuffer
Covers the remaining state-transition edges: re-prime after an underrun goes idle (must re-accumulate TARGET_DELAY_FRAMES, not resume on one packet), duplicate-insert overwrite (no buffer growth), is_idle across fresh/buffering/ underrun, and overflow-resync when next_seq is already Some (playout head snaps to the new front). Jitter tests 6 -> 10; test-only, no prod change. Implemented by Gemini per next-task.md; left uncommitted per the operating- agreement default, reviewed against the real diff and re-verified (build + clippy --all-targets + test all green) by the senior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -222,5 +222,94 @@ mod tests {
|
|||||||
assert!(!jb.packets.contains_key(&0));
|
assert!(!jb.packets.contains_key(&0));
|
||||||
assert_eq!(jb.next_seq, Some(1));
|
assert_eq!(jb.next_seq, Some(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reprimes_after_underrun_idle() {
|
||||||
|
let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap();
|
||||||
|
let mut jb = JitterBuffer::new().unwrap();
|
||||||
|
|
||||||
|
// Prime with seq 0, 1, 2
|
||||||
|
jb.insert(0, frame(&mut enc, 1000));
|
||||||
|
jb.insert(1, frame(&mut enc, 1000));
|
||||||
|
jb.insert(2, frame(&mut enc, 1000));
|
||||||
|
|
||||||
|
// pop_frame() 3x -> 3 Some
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
|
||||||
|
// A 4th pop_frame() -> None, and jb.is_idle() is true
|
||||||
|
assert!(jb.pop_frame().is_none());
|
||||||
|
assert!(jb.is_idle());
|
||||||
|
|
||||||
|
// Insert ONE new frame (seq 3): pop_frame() must still be None (must re-accumulate TARGET_DELAY_FRAMES)
|
||||||
|
jb.insert(3, frame(&mut enc, 1000));
|
||||||
|
assert!(jb.pop_frame().is_none());
|
||||||
|
assert!(!jb.is_idle());
|
||||||
|
|
||||||
|
// Insert seq 4 and 5 (now 3 buffered) -> pop_frame() yields Some (re-primed)
|
||||||
|
jb.insert(4, frame(&mut enc, 1000));
|
||||||
|
jb.insert(5, frame(&mut enc, 1000));
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicate_insert_does_not_grow_buffer() {
|
||||||
|
let mut jb = JitterBuffer::new().unwrap();
|
||||||
|
jb.insert(0, vec![0u8]);
|
||||||
|
jb.insert(0, vec![1u8]);
|
||||||
|
assert_eq!(jb.packets.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_idle_reflects_buffer_state() {
|
||||||
|
let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap();
|
||||||
|
let mut jb = JitterBuffer::new().unwrap();
|
||||||
|
|
||||||
|
// Fresh buffer
|
||||||
|
assert!(jb.is_idle());
|
||||||
|
|
||||||
|
// After a single insert
|
||||||
|
jb.insert(0, frame(&mut enc, 1000));
|
||||||
|
assert!(!jb.is_idle());
|
||||||
|
|
||||||
|
// Prime (3 frames)
|
||||||
|
jb.insert(1, frame(&mut enc, 1000));
|
||||||
|
jb.insert(2, frame(&mut enc, 1000));
|
||||||
|
|
||||||
|
// Drain past the end so it underruns
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_none());
|
||||||
|
|
||||||
|
assert!(jb.is_idle());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overflow_resync_while_playing() {
|
||||||
|
let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap();
|
||||||
|
let mut jb = JitterBuffer::new().unwrap();
|
||||||
|
|
||||||
|
// Prime with seq 0, 1, 2
|
||||||
|
jb.insert(0, frame(&mut enc, 1000));
|
||||||
|
jb.insert(1, frame(&mut enc, 1000));
|
||||||
|
jb.insert(2, frame(&mut enc, 1000));
|
||||||
|
|
||||||
|
// pop_frame() twice (now next_seq == Some(2))
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert!(jb.pop_frame().is_some());
|
||||||
|
assert_eq!(jb.next_seq, Some(2));
|
||||||
|
|
||||||
|
// Insert a contiguous run of higher sequences to exceed MAX_BUFFERED_FRAMES
|
||||||
|
let start = 3;
|
||||||
|
let end = 3 + MAX_BUFFERED_FRAMES + 2;
|
||||||
|
for seq in start..end {
|
||||||
|
jb.insert(seq as u32, frame(&mut enc, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(jb.packets.len(), MAX_BUFFERED_FRAMES);
|
||||||
|
assert_eq!(jb.next_seq, jb.packets.keys().next().copied());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user