test(audio): unit tests for parse_pw_nodes device parser
Covers the pure pw-cli parser seam: multi-node parse sorted by description (non-audio dropped), Source=>input / Sink=>output, description-falls-back- to-name, empty/non-audio inputs yield nothing, EOF-flush of the final block, and incomplete blocks (no media.class) dropped. pw_cli tests 0 -> 6. Implemented by Gemini per next-task.md; left uncommitted per the operating- agreement default, reviewed against the real diff and re-verified (build + clippy --all-targets + test all green) by the senior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -70,3 +70,104 @@ fn parse_pw_nodes(text: &str) -> Vec<AudioDevice> {
|
||||
devices.sort_by(|a, b| a.description.cmp(&b.description));
|
||||
devices
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE_NODES: &str = r#"
|
||||
id 33, type PipeWire:Interface:Node/3
|
||||
node.name = "alsa_output.builtin"
|
||||
node.description = "Built-in Speakers"
|
||||
media.class = "Audio/Sink"
|
||||
id 34, type PipeWire:Interface:Node/3
|
||||
node.name = "alsa_input.builtin"
|
||||
node.description = "Built-in Mic"
|
||||
media.class = "Audio/Source"
|
||||
id 35, type PipeWire:Interface:Node/3
|
||||
node.name = "v4l2.cam"
|
||||
media.class = "Video/Source"
|
||||
id 36, type PipeWire:Interface:Node/3
|
||||
node.name = "bare.sink"
|
||||
media.class = "Audio/Sink"
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn parses_audio_nodes_sorted_by_description() {
|
||||
let devices = parse_pw_nodes(SAMPLE_NODES);
|
||||
|
||||
// exactly 3 devices (the Video/Source node is dropped)
|
||||
assert_eq!(devices.len(), 3);
|
||||
|
||||
// order is by description: ["Built-in Mic", "Built-in Speakers", "bare.sink"]
|
||||
// (uppercase "B" sorts before lowercase "b")
|
||||
assert_eq!(devices[0].description, "Built-in Mic");
|
||||
assert_eq!(devices[1].description, "Built-in Speakers");
|
||||
assert_eq!(devices[2].description, "bare.sink");
|
||||
|
||||
// the mic device equals:
|
||||
assert_eq!(
|
||||
devices[0],
|
||||
AudioDevice {
|
||||
name: "alsa_input.builtin".into(),
|
||||
description: "Built-in Mic".into(),
|
||||
is_input: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_is_input_sink_is_output() {
|
||||
let devices = parse_pw_nodes(SAMPLE_NODES);
|
||||
// Find devices by name or description to verify is_input
|
||||
let mic = devices.iter().find(|d| d.name == "alsa_input.builtin").unwrap();
|
||||
let speakers = devices.iter().find(|d| d.name == "alsa_output.builtin").unwrap();
|
||||
let bare = devices.iter().find(|d| d.name == "bare.sink").unwrap();
|
||||
|
||||
assert!(mic.is_input);
|
||||
assert!(!speakers.is_input);
|
||||
assert!(!bare.is_input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn description_falls_back_to_node_name() {
|
||||
let devices = parse_pw_nodes(SAMPLE_NODES);
|
||||
let bare = devices.iter().find(|d| d.name == "bare.sink").unwrap();
|
||||
assert_eq!(bare.description, "bare.sink");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_audio_and_empty_inputs_yield_nothing() {
|
||||
assert!(parse_pw_nodes("").is_empty());
|
||||
|
||||
let video_sample = r#"
|
||||
id 35, type PipeWire:Interface:Node/3
|
||||
node.name = "v4l2.cam"
|
||||
media.class = "Video/Source"
|
||||
"#;
|
||||
assert!(parse_pw_nodes(video_sample).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_block_is_emitted_at_eof() {
|
||||
let single_sample = r#"
|
||||
id 99, type PipeWire:Interface:Node/3
|
||||
node.name = "single.device"
|
||||
media.class = "Audio/Sink"
|
||||
"#;
|
||||
let devices = parse_pw_nodes(single_sample);
|
||||
assert_eq!(devices.len(), 1);
|
||||
assert_eq!(devices[0].name, "single.device");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incomplete_block_without_class_is_dropped() {
|
||||
let incomplete_sample = r#"
|
||||
id 100, type PipeWire:Interface:Node/3
|
||||
node.name = "incomplete.device"
|
||||
node.description = "Incomplete Device"
|
||||
"#;
|
||||
let devices = parse_pw_nodes(incomplete_sample);
|
||||
assert!(devices.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user