use super::*;
#[test]
fn poll_event_yields_chunk_dropped() {
let backend = Box::new(MockBackend::new(48_000, 2, 440.0));
let config = StreamConfig {
ring_capacity_chunks: 1,
..Default::default()
};
let mut stream = Stream::open(config, backend).expect("open");
stream.start().expect("start");
let got_drop = wait_until(
|| {
while let Some(ev) = stream.poll_event() {
if matches!(ev, Event::ChunkDropped { .. }) {
return true;
}
}
false
},
Duration::from_secs(3),
);
stream.stop();
assert!(
got_drop,
"expected to retrieve ChunkDropped through poll_event"
);
}
#[test]
fn poll_event_is_none_when_empty() {
let backend = Box::new(MockBackend::new(48_000, 2, 440.0));
let mut stream = Stream::open(StreamConfig::default(), backend).expect("open");
assert!(stream.poll_event().is_none());
}
#[test]
fn recording_clock_is_zero_based() {
let backend = Box::new(MockBackend::new(48_000, 2, 440.0));
let mut stream = Stream::open(StreamConfig::default(), backend).expect("open");
stream.start().expect("start");
let mut first: Option<AudioChunk> = None;
let got = wait_until(
|| match stream.poll_chunk() {
Some(c) => {
first = Some(c);
true
}
None => false,
},
Duration::from_secs(2),
);
stream.stop();
assert!(got, "expected the first chunk to arrive");
let first = first.unwrap();
assert_eq!(
first.pts_ns, 0,
"the first delivered chunk should start at recording time zero (pts_ns == 0): {}",
first.pts_ns
);
}
#[test]
fn dual_output_delivers_primary_and_secondary() {
let config = StreamConfig {
secondary_output: Some(OutputFormat {
sample_rate: 16_000,
channels: 1,
}),
ring_capacity_chunks: 200,
..Default::default()
};
let backend = Box::new(MockBackend::new(48_000, 2, 440.0));
let mut stream = Stream::open(config, backend).expect("open");
stream.start().expect("start");
let mut primary: Vec<AudioChunk> = Vec::new();
let mut secondary: Vec<SecondaryChunk> = Vec::new();
let deadline = Instant::now() + Duration::from_millis(500);
while Instant::now() < deadline {
while let Some(c) = stream.poll_chunk() {
primary.push(c);
}
while let Some(c) = stream.poll_secondary() {
secondary.push(c);
}
thread::sleep(Duration::from_millis(5));
}
stream.stop();
while let Some(c) = stream.poll_chunk() {
primary.push(c);
}
while let Some(c) = stream.poll_secondary() {
secondary.push(c);
}
assert!(!primary.is_empty(), "expected primary chunks");
assert!(!secondary.is_empty(), "expected secondary chunks");
for c in &primary {
assert_eq!(
c.data.len(),
960 * 2,
"primary is 48k/stereo = 1920 samples"
);
}
for c in &secondary {
assert_eq!(c.samples.len(), 320, "secondary is 16k/mono = 320 samples");
}
assert_eq!(primary[0].pts_ns, 0, "first primary chunk starts at zero");
for w in secondary.windows(2) {
assert!(
w[1].pts_ns >= w[0].pts_ns,
"secondary PTS should not decrease"
);
}
assert!(
secondary[0].pts_ns >= 0,
"secondary PTS should be non-negative (based on primary epoch)"
);
for pair in primary.windows(2) {
assert_eq!(pair[1].frame_index, pair[0].frame_index + 960);
}
for pair in secondary.windows(2) {
assert_eq!(pair[1].frame_index, pair[0].frame_index + 960);
}
assert_eq!(primary[0].frame_index, 0);
assert_eq!(secondary[0].frame_index, 0);
assert_eq!(secondary[0].seq, 0);
for w in secondary.windows(2) {
assert_eq!(
w[1].seq,
w[0].seq + 1,
"secondary seq should increase consecutively"
);
}
}
#[test]
fn denoise_enabled_still_delivers_both_taps() {
fn drain_taps(stream: &mut Stream, primary: &mut usize, secondary: &mut usize) {
while stream.poll_chunk().is_some() {
*primary += 1;
}
while let Some(chunk) = stream.poll_secondary() {
assert_eq!(
chunk.samples.len(),
320,
"secondary is 16k/mono = 320 samples"
);
*secondary += 1;
}
}
let config = StreamConfig {
secondary_output: Some(OutputFormat {
sample_rate: 16_000,
channels: 1,
}),
..Default::default()
};
let backend = Box::new(MockBackend::new(48_000, 2, 440.0));
let mut stream = Stream::open(config, backend).expect("open");
stream.set_denoise(true);
stream.start().expect("start");
let mut primary = 0usize;
let mut secondary = 0usize;
let primary_ok = wait_until(
|| {
drain_taps(&mut stream, &mut primary, &mut secondary);
primary > 0
},
DENOISE_TAP_TIMEOUT,
);
let secondary_ok = wait_until(
|| {
drain_taps(&mut stream, &mut primary, &mut secondary);
secondary > 0
},
DENOISE_TAP_TIMEOUT,
);
let last_sample_ns = stream.shared.last_sample_ns.load(Ordering::SeqCst);
stream.stop();
assert!(
primary_ok,
"no primary delivery with denoise within {DENOISE_TAP_TIMEOUT:?}: \
primary={primary}, secondary={secondary}, last_sample_ns={last_sample_ns}"
);
assert!(
secondary_ok,
"no secondary delivery with denoise within {DENOISE_TAP_TIMEOUT:?}: \
primary={primary}, secondary={secondary}, last_sample_ns={last_sample_ns}"
);
}