From 7b118272b0842b4a2035dcfc11cacfb166819656 Mon Sep 17 00:00:00 2001 From: Mollusk Date: Fri, 21 Aug 2026 19:55:41 -0400 Subject: [PATCH] test(host): close Row 9 fanout partition --- src/host/fanout.rs | 103 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/src/host/fanout.rs b/src/host/fanout.rs index 0ec4da4..27537a2 100644 --- a/src/host/fanout.rs +++ b/src/host/fanout.rs @@ -1038,8 +1038,15 @@ mod tests { )); } - #[test] - fn eligible_late_arrival_is_positively_captured() { + #[derive(Clone, Copy)] + enum Row9Scenario { + MusicOnly, + DifferentDeviceMicrophone, + SameDeviceMicrophone, + TaintedMonitor, + } + + fn assert_row_9_late_arrival(scenario: Row9Scenario, expected_reason: Option<&str>) { let mut graph = Graph::new(); let sink = graph.native_virtual_node("pixelpass_capture_test", MediaRole::Sink, 43); graph.port_on_channel(sink, PortDirection::In, false, Some("FL")); @@ -1049,17 +1056,95 @@ mod tests { drive(&mut controller, &graph, &mut links); assert!(links.held.is_empty()); - let late = graph.app_node("late", MediaRole::StreamOutput, 44); - graph.port_on_channel(late, PortDirection::Out, false, Some("FL")); - graph.port_on_channel(late, PortDirection::Out, false, Some("FR")); + let firefox_pid = 11_114; + let firefox = match scenario { + Row9Scenario::MusicOnly => { + graph.app_node("firefox-music", MediaRole::StreamOutput, firefox_pid) + } + Row9Scenario::DifferentDeviceMicrophone => { + let speakers = graph.device_node_on("speakers", MediaRole::Sink, GlobalId(700)); + let microphone = + graph.device_node_on("usb-microphone", MediaRole::Source, GlobalId(701)); + let call = graph.peerspeak_node("call", 7); + let firefox_in = + graph.app_node("firefox-microphone", MediaRole::StreamInput, firefox_pid); + graph.link(call, speakers); + graph.link(microphone, firefox_in); + graph.app_node("firefox-audio", MediaRole::StreamOutput, firefox_pid) + } + Row9Scenario::SameDeviceMicrophone => { + let device = GlobalId(700); + let speakers = graph.device_node_on("headset-output", MediaRole::Sink, device); + let microphone = + graph.device_node_on("headset-microphone", MediaRole::Source, device); + let call = graph.peerspeak_node("call", 7); + let firefox_in = + graph.app_node("firefox-microphone", MediaRole::StreamInput, firefox_pid); + graph.link(call, speakers); + graph.link(microphone, firefox_in); + graph.app_node("firefox-audio", MediaRole::StreamOutput, firefox_pid) + } + Row9Scenario::TaintedMonitor => { + let speakers = graph.device_node("speakers", MediaRole::Sink); + let call = graph.peerspeak_node("call", 7); + let firefox_in = + graph.app_node("firefox-monitor", MediaRole::StreamInput, firefox_pid); + graph.link(call, speakers); + graph.link(speakers, firefox_in); + graph.app_node("firefox-audio", MediaRole::StreamOutput, firefox_pid) + } + }; + graph.port_on_channel(firefox, PortDirection::Out, false, Some("FL")); + graph.port_on_channel(firefox, PortDirection::Out, false, Some("FR")); drive(&mut controller, &graph, &mut links); - assert_eq!(links.creates, 2); - assert_eq!( - controller.states()[&late.serial], - StreamCaptureState::Captured + + match expected_reason { + None => { + assert_eq!(links.creates, 2, "both stereo links must be created"); + assert_eq!(links.held.len(), 2); + assert_eq!( + controller.states()[&firefox.serial], + StreamCaptureState::Captured + ); + } + Some(expected_reason) => { + assert_eq!(links.creates, 0, "an excluded stream must never be linked"); + assert!(links.held.is_empty()); + let StreamCaptureState::Excluded { reason } = controller.states()[&firefox.serial] + else { + panic!("the late Firefox stream must be excluded"); + }; + assert_eq!(reason.code(), expected_reason); + } + } + + drive(&mut controller, &graph, &mut links); + assert_eq!(links.creates, if expected_reason.is_none() { 2 } else { 0 }); + } + + #[test] + fn row_9a_music_only_late_arrival_is_positively_captured() { + assert_row_9_late_arrival(Row9Scenario::MusicOnly, None); + } + + #[test] + fn row_9b_different_device_microphone_late_arrival_is_positively_captured() { + assert_row_9_late_arrival(Row9Scenario::DifferentDeviceMicrophone, None); + } + + #[test] + fn row_9c_same_device_microphone_late_arrival_is_never_linked() { + assert_row_9_late_arrival( + Row9Scenario::SameDeviceMicrophone, + Some("tainted-owner-bridge"), ); } + #[test] + fn row_9d_tainted_monitor_late_arrival_is_never_linked() { + assert_row_9_late_arrival(Row9Scenario::TaintedMonitor, Some("tainted-owner-bridge")); + } + #[test] fn configured_but_unvalidated_aec_gates_every_mutation() { let (graph, app, sink, _) = stereo_graph();