test(host/audio): prove a reopen recovered with a live uplink, not one frame
`reopens_after_push_death` failed about one run in nine, and widening its timeout did not help — the earlier commit blamed the backoff and was wrong. The pump drops whatever queued while it was down: audio from before the device came back is stale, so a fresh instance drains the channel right after opening. The harness counts `opens` from the START of the open, so the moment the test sees the counter move, the pump has not reached that drain yet. The single frame it then sent landed inside the drain window and was discarded exactly as designed, leaving the test waiting for audio that was never going to arrive. So the test now keeps feeding, which is what a real uplink does and what the drain assumes. The sequence advances each time or the de-jitter reads the repeats as duplicates and drops them for a second, correct reason. Production behaviour is unchanged: this was the test asserting something the pump never promised. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -496,9 +496,6 @@ mod pump_tests {
|
||||
start_tuned(fail_first, false, Duration::ZERO)
|
||||
}
|
||||
|
||||
/// The budget has to clear `backoff_start` (2 s) with room to spare: a reopen test that
|
||||
/// waits exactly as long as the backoff it is waiting for fails whenever the machine is
|
||||
/// busy or the binary is cold, which is precisely what CI is.
|
||||
fn wait_until(what: &str, mut cond: impl FnMut() -> bool) {
|
||||
for _ in 0..600 {
|
||||
if cond() {
|
||||
@@ -509,6 +506,27 @@ mod pump_tests {
|
||||
panic!("timed out waiting for: {what}");
|
||||
}
|
||||
|
||||
/// Keep a live uplink running until audio reaches the backend.
|
||||
///
|
||||
/// A single frame is NOT enough to prove a reopen recovered: a freshly opened backend
|
||||
/// deliberately drops whatever queued while it was down (the `try_recv` drain after the
|
||||
/// open — that audio predates the device), and `opens` counts from the START of the open,
|
||||
/// so a frame sent the moment the counter moves lands inside that drain and is discarded
|
||||
/// by design. A real uplink keeps sending, so the test does too. The sequence has to keep
|
||||
/// advancing or the de-jitter reads the repeats as duplicates and drops them.
|
||||
fn wait_until_pushed(what: &str, h: &Harness, from_seq: u32) {
|
||||
let mut seq = from_seq;
|
||||
for _ in 0..600 {
|
||||
let _ = h.tx.try_send(mic_frame(seq));
|
||||
seq = seq.wrapping_add(1);
|
||||
if h.pushed.load(Ordering::SeqCst) > 0 {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
panic!("timed out waiting for: {what}");
|
||||
}
|
||||
|
||||
fn opus_frame() -> Vec<u8> {
|
||||
let mut enc = opus::Encoder::new(48_000, opus::Channels::Stereo, opus::Application::Voip)
|
||||
.expect("opus encoder");
|
||||
@@ -582,8 +600,7 @@ mod pump_tests {
|
||||
.store(false, Ordering::Release);
|
||||
h.tx.send(mic_frame(0)).unwrap(); // push sees death → reopen
|
||||
wait_until("reopen", || h.opens.load(Ordering::SeqCst) >= 2);
|
||||
h.tx.send(mic_frame(1)).unwrap();
|
||||
wait_until("pcm after reopen", || h.pushed.load(Ordering::SeqCst) > 0);
|
||||
wait_until_pushed("pcm after reopen", &h, 1);
|
||||
drop(h.tx);
|
||||
h.join.join().unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user