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:
2026-06-02 16:33:55 -04:00
co-authored by Claude Opus 4.8
parent 2c17310adc
commit 9d278ded5a
+89
View File
@@ -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());
}
} }