93 lines
3.0 KiB
Rust
93 lines
3.0 KiB
Rust
//! Integration coverage for server audio capture/sink plumbing.
|
|
//!
|
|
//! Scope: compile `server/src/audio.rs` as a module and exercise public audio
|
|
//! constructors/helpers across deterministic error and smoke paths.
|
|
//! Targets: `server/src/audio.rs`.
|
|
//! Why: audio pipeline setup is branchy and should stay stable without requiring
|
|
//! physical ALSA/UAC hardware in CI.
|
|
|
|
#[path = "../../server/src/audio.rs"]
|
|
#[allow(warnings)]
|
|
mod server_audio_contract;
|
|
|
|
mod tests {
|
|
use super::server_audio_contract::{ClipTap, Voice, ear};
|
|
#[cfg(coverage)]
|
|
use futures_util::StreamExt;
|
|
use lesavka_common::lesavka::AudioPacket;
|
|
use serial_test::serial;
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn ear_rejects_malformed_pipeline_device_string() {
|
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
|
let result = rt.block_on(ear("hw:UAC2Gadget,0\" ! broken-pipe", 0));
|
|
assert!(result.is_err(), "malformed device string should fail parse");
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn ear_missing_device_path_is_stable() {
|
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
|
let result = rt.block_on(ear("hw:DefinitelyMissingDevice,0", 0));
|
|
if let Err(err) = result {
|
|
assert!(!err.to_string().trim().is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn ear_existing_non_audio_node_reaches_runtime_paths() {
|
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
|
let result = rt.block_on(async {
|
|
tokio::time::timeout(std::time::Duration::from_millis(250), ear("/dev/null", 0)).await
|
|
});
|
|
match result {
|
|
Ok(Ok(stream)) => drop(stream),
|
|
Ok(Err(_)) | Err(_) => {}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn clip_tap_feed_flush_and_drop_are_stable() {
|
|
let mut tap = ClipTap::new("audio-contract", std::time::Duration::from_millis(1));
|
|
tap.feed(&[1, 2, 3, 4, 5]);
|
|
tap.feed(&vec![9u8; 300_000]);
|
|
tap.flush();
|
|
tap.flush(); // empty flush should be a no-op
|
|
drop(tap);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn voice_constructor_and_push_finish_are_stable() {
|
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
|
let result = rt.block_on(Voice::new("hw:DefinitelyMissingDevice,0"));
|
|
match result {
|
|
Ok(mut voice) => {
|
|
voice.push(&AudioPacket {
|
|
id: 0,
|
|
pts: 77,
|
|
data: vec![0xFF, 0xF1, 0x50, 0x80, 0x00, 0x1F, 0xFC],
|
|
});
|
|
voice.finish();
|
|
}
|
|
Err(err) => {
|
|
assert!(!err.to_string().trim().is_empty());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(coverage)]
|
|
#[test]
|
|
#[serial]
|
|
fn audio_stream_poll_next_is_stable_when_channel_is_closed() {
|
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
|
let polled = rt.block_on(async {
|
|
let mut stream = ear("/dev/null", 0).await.expect("coverage ear stream");
|
|
stream.next().await
|
|
});
|
|
assert!(polled.is_none(), "closed stream should yield None");
|
|
}
|
|
}
|