use audio_core_bsd::{AudioFrame, AudioNode, PortDescriptor, ProcessContext, SampleFormat};
use audio_graph_bsd::{FlushError, Graph, GraphConfig, RingSink};
use proptest::prelude::*;
struct SourceNode {
out: [PortDescriptor; 1],
}
impl SourceNode {
fn new() -> Self {
Self {
out: [PortDescriptor::output(1, SampleFormat::F32)],
}
}
}
impl AudioNode for SourceNode {
fn inputs(&self) -> &[PortDescriptor] {
&[]
}
fn outputs(&self) -> &[PortDescriptor] {
&self.out
}
fn process(&mut self, _: &mut ProcessContext, _: &[AudioFrame], _: &mut [AudioFrame]) {}
}
proptest! {
#[test]
fn flush_roundtrip_preserves_arbitrary_signal(
values in prop::collection::vec(-1.0f32..1.0, 1..256)
) {
let n = values.len();
let (producer, mut consumer) = rtrb::RingBuffer::<AudioFrame>::new(4);
let mut g = Graph::new();
let src = g.add_node(Box::new(SourceNode::new()));
let sink = g.add_sink(Box::new(RingSink::new(producer, 1, 48_000, n)));
g.link((src, 0), (sink, 0)).unwrap();
g.compile(GraphConfig::new(n, 48_000, 1)).unwrap();
g.feed(src, 0, &AudioFrame::from_planar(1, 48_000, values.clone()));
let mut ctx = ProcessContext::new(n, 0, 48_000);
g.process_cycle(&mut ctx).unwrap();
let (count, err) = g.flush_sinks();
prop_assert_eq!(count, 1);
prop_assert!(err.is_none());
let frame = consumer.pop().expect("flushed frame must be available");
prop_assert_eq!(frame.samples, values, "flush must preserve the signal bit-exactly");
}
#[test]
fn flush_into_full_ring_reports_ringfull_no_panic(capacity in 1usize..8) {
let (producer, _consumer) = rtrb::RingBuffer::<AudioFrame>::new(capacity);
let mut g = Graph::new();
let src = g.add_node(Box::new(SourceNode::new()));
let sink = g.add_sink(Box::new(RingSink::new(producer, 1, 48_000, 4)));
g.link((src, 0), (sink, 0)).unwrap();
g.compile(GraphConfig::new(4, 48_000, 1)).unwrap();
g.feed(src, 0, &AudioFrame::from_planar(1, 48_000, vec![0.1; 4]));
let mut ctx = ProcessContext::new(4, 0, 48_000);
let mut saw_full = false;
for _ in 0..(capacity + 3) {
g.process_cycle(&mut ctx).unwrap();
let (_, err) = g.flush_sinks();
if matches!(err, Some(FlushError::RingFull(_))) {
saw_full = true;
}
}
prop_assert!(
saw_full,
"expected at least one RingFull after flushing past capacity {capacity}"
);
}
#[test]
fn flush_sink_plain_node_always_not_flushable(plain_count in 0usize..10) {
let mut g = Graph::new();
let mut last = 0;
for _ in 0..plain_count {
last = g.add_node(Box::new(SourceNode::new()));
}
g.compile(GraphConfig::new(4, 48_000, 1)).unwrap();
if plain_count > 0 {
prop_assert_eq!(g.flush_sink(last), Err(FlushError::NotFlushable(last)));
}
}
}