2026-04-22 08:07:09 -03:00
|
|
|
//! Include-based coverage for microphone source-name routing.
|
|
|
|
|
//!
|
|
|
|
|
//! Scope: include `client/src/input/microphone.rs` and cover selected-source
|
|
|
|
|
//! routing heuristics for launcher catalog names.
|
|
|
|
|
//! Targets: `client/src/input/microphone.rs`.
|
|
|
|
|
//! Why: launcher-selected Pulse source names must not regress to PipeWire
|
|
|
|
|
//! negotiation when the catalog already provides a concrete Pulse device name.
|
|
|
|
|
|
2026-04-25 16:48:20 -03:00
|
|
|
#[allow(warnings)]
|
|
|
|
|
mod live_capture_clock {
|
|
|
|
|
include!("support/live_capture_clock_shim.rs");
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 08:07:09 -03:00
|
|
|
#[allow(warnings)]
|
|
|
|
|
mod microphone_source_contract {
|
|
|
|
|
include!(env!("LESAVKA_CLIENT_MICROPHONE_SRC"));
|
|
|
|
|
|
|
|
|
|
use serial_test::serial;
|
|
|
|
|
use std::fs;
|
|
|
|
|
use std::os::unix::fs::PermissionsExt;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
use temp_env::with_var;
|
|
|
|
|
use tempfile::tempdir;
|
|
|
|
|
|
|
|
|
|
fn write_executable(dir: &Path, name: &str, body: &str) {
|
|
|
|
|
let path = dir.join(name);
|
|
|
|
|
fs::write(&path, body).expect("write script");
|
|
|
|
|
let mut perms = fs::metadata(&path).expect("metadata").permissions();
|
|
|
|
|
perms.set_mode(0o755);
|
|
|
|
|
fs::set_permissions(path, perms).expect("chmod");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_fake_command(name: &str, script_body: &str, f: impl FnOnce()) {
|
|
|
|
|
let dir = tempdir().expect("tempdir");
|
|
|
|
|
write_executable(dir.path(), name, script_body);
|
|
|
|
|
let prior = std::env::var("PATH").unwrap_or_default();
|
|
|
|
|
let merged = if prior.is_empty() {
|
|
|
|
|
dir.path().display().to_string()
|
|
|
|
|
} else {
|
|
|
|
|
format!("{}:{prior}", dir.path().display())
|
|
|
|
|
};
|
|
|
|
|
with_var("PATH", Some(merged), f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_fake_pactl(script_body: &str, f: impl FnOnce()) {
|
|
|
|
|
with_fake_command("pactl", script_body, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_fake_pw_dump(script_body: &str, f: impl FnOnce()) {
|
|
|
|
|
with_fake_command("pw-dump", script_body, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn pulse_source_name_heuristic_matches_launcher_catalog_names() {
|
|
|
|
|
assert!(looks_like_pulse_source_name(
|
|
|
|
|
"alsa_input.usb-Neat_Microphones_Bumblebee_II_USB_Microphone-00.mono-fallback"
|
|
|
|
|
));
|
|
|
|
|
assert!(looks_like_pulse_source_name("bluez_input.headset"));
|
|
|
|
|
assert!(!looks_like_pulse_source_name("PipeWire Nick Mic"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[serial]
|
|
|
|
|
fn resolve_source_desc_prefers_pulse_for_pulse_catalog_names() {
|
|
|
|
|
let pactl_script = r#"#!/usr/bin/env sh
|
|
|
|
|
if [ "$1" = "list" ] && [ "$2" = "short" ] && [ "$3" = "sources" ]; then
|
|
|
|
|
echo "1 alsa_input.usb-Bumblebee_II-00.mono-fallback module-alsa-card.c s16le 1ch 48000Hz RUNNING"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
exit 0
|
|
|
|
|
"#;
|
|
|
|
|
let pw_script = r#"#!/usr/bin/env sh
|
|
|
|
|
cat <<'JSON'
|
|
|
|
|
[
|
|
|
|
|
{"info":{"props":{"media.class":"Audio/Source","node.name":"alsa_input.usb-Bumblebee_II-00.mono-fallback"}}}
|
|
|
|
|
]
|
|
|
|
|
JSON
|
|
|
|
|
"#;
|
|
|
|
|
with_fake_pactl(pactl_script, || {
|
|
|
|
|
with_fake_pw_dump(pw_script, || {
|
|
|
|
|
let desc = MicrophoneCapture::resolve_source_desc("alsa_input.usb-Bumblebee_II")
|
|
|
|
|
.expect("pulse source");
|
|
|
|
|
assert!(
|
|
|
|
|
desc.contains("pulsesrc device=alsa_input.usb-Bumblebee_II-00.mono-fallback"),
|
|
|
|
|
"Pulse catalog source names should route through pulsesrc: {desc}"
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(coverage)]
|
|
|
|
|
#[serial]
|
|
|
|
|
fn default_source_desc_can_fall_back_to_pulse_when_pipewire_is_disabled() {
|
|
|
|
|
with_var("LESAVKA_MIC_TEST_SOURCE_DESC", None::<&str>, || {
|
|
|
|
|
with_var("LESAVKA_MIC_DISABLE_PIPEWIRE", Some("1"), || {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
MicrophoneCapture::default_source_desc(),
|
|
|
|
|
"pulsesrc do-timestamp=true"
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|