From 9f1b276f3682e7d7e72ce336e2ea02be60b29ec3 Mon Sep 17 00:00:00 2001 From: Mollusk Date: Fri, 5 Jun 2026 18:55:42 -0400 Subject: [PATCH] test(jitter): cover grown-target re-prime and overflow clean_run reset Two edge tests for the adaptive playout-delay controller: - grown_target_requires_deeper_reprime: a disruption-grown target actually gates the next re-prime (3 frames no longer enough once target is 4). - overflow_resync_resets_clean_run: the MAX_BUFFERED overflow resync path restarts the clean run. Gemini-authored (junior), senior-reviewed against the real diff and independently re-verified (cargo test --lib + clippy clean). Driven via the headless agy --print --sandbox loop (resumed with --continue past the orientation-tax timeout). Co-Authored-By: Claude Opus 4.8 --- src/core/jitter.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/core/jitter.rs b/src/core/jitter.rs index 9345daa..cc8724e 100644 --- a/src/core/jitter.rs +++ b/src/core/jitter.rs @@ -567,5 +567,72 @@ mod tests { } assert_eq!(polls, PRIME_TIMEOUT_TICKS); } + + #[test] + fn grown_target_requires_deeper_reprime() { + let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut jb = JitterBuffer::new().unwrap(); + + assert_eq!(jb.target_delay(), DEFAULT_DELAY_FRAMES); + + // 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 -> head now at seq 2 + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert_eq!(jb.next_seq, Some(2)); + assert_eq!(jb.target_delay(), DEFAULT_DELAY_FRAMES); + + // A late packet for an already-played sequence (0) arrives: grows delay to 4 + jb.insert(0, vec![0u8]); + assert_eq!(jb.target_delay(), 4); + + // Drain: plays seq 2, then underruns (goes idle) + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_none()); + assert!(jb.is_idle()); + + // Insert three fresh contiguous frames (seq 100, 101, 102) + jb.insert(100, frame(&mut enc, 1000)); + jb.insert(101, frame(&mut enc, 1000)); + jb.insert(102, frame(&mut enc, 1000)); + + // Playout must NOT prime yet (3 < grown target of 4) + assert!(jb.pop_frame().is_none()); + assert!(!jb.is_idle()); + + // Insert a fourth frame (seq 103) -> primes and plays seq 100 + jb.insert(103, frame(&mut enc, 1000)); + assert!(jb.pop_frame().is_some()); + } + + #[test] + fn overflow_resync_resets_clean_run() { + 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)); + + // Play a few in-order real frames so clean_run > 0 + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert!(jb.clean_run > 0); + + // Insert a contiguous run long enough to exceed MAX_BUFFERED_FRAMES + // packets currently contains seq 2 (length 1). + // Inserting seq 3..=34 (32 frames) makes total length 33, exceeding MAX_BUFFERED_FRAMES (32) + for seq in 3..=34 { + jb.insert(seq, frame(&mut enc, 1000)); + } + + // Assert overflow occurred and reset clean_run + assert_eq!(jb.clean_run, 0); + } }