397 lines
13 KiB
Rust
397 lines
13 KiB
Rust
use super::*;
|
|
use serial_test::serial;
|
|
|
|
#[cfg(not(coverage))]
|
|
mod utility_actions;
|
|
|
|
#[test]
|
|
fn resolve_server_addr_prefers_explicit_server_flag() {
|
|
let args = vec![
|
|
"--launcher".to_string(),
|
|
"--server".to_string(),
|
|
"http://example:50051".to_string(),
|
|
"http://fallback:50051".to_string(),
|
|
];
|
|
assert_eq!(resolve_server_addr(&args), "http://example:50051");
|
|
}
|
|
|
|
#[test]
|
|
fn resolve_server_addr_uses_first_non_flag_or_default() {
|
|
let args = vec![
|
|
"--launcher".to_string(),
|
|
"http://from-arg:50051".to_string(),
|
|
];
|
|
assert_eq!(resolve_server_addr(&args), "http://from-arg:50051");
|
|
|
|
let args = vec!["--launcher".to_string()];
|
|
assert_eq!(resolve_server_addr(&args), DEFAULT_SERVER_ADDR);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn resolve_server_addr_falls_back_to_env_before_default() {
|
|
temp_env::with_var("LESAVKA_SERVER_ADDR", Some("http://env:50051"), || {
|
|
let args = vec!["--launcher".to_string()];
|
|
assert_eq!(resolve_server_addr(&args), "http://env:50051");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn launcher_ipc_paths_have_stable_defaults_and_env_overrides() {
|
|
temp_env::with_vars(
|
|
[
|
|
(LAUNCHER_FOCUS_SIGNAL_ENV, None::<&str>),
|
|
(LAUNCHER_CLIPBOARD_CONTROL_ENV, None::<&str>),
|
|
],
|
|
|| {
|
|
assert_eq!(
|
|
launcher_focus_signal_path(),
|
|
PathBuf::from(DEFAULT_LAUNCHER_FOCUS_SIGNAL_PATH)
|
|
);
|
|
assert_eq!(
|
|
launcher_clipboard_control_path(),
|
|
PathBuf::from(DEFAULT_LAUNCHER_CLIPBOARD_CONTROL_PATH)
|
|
);
|
|
},
|
|
);
|
|
|
|
temp_env::with_vars(
|
|
[
|
|
(LAUNCHER_FOCUS_SIGNAL_ENV, Some("/tmp/focus-now")),
|
|
(LAUNCHER_CLIPBOARD_CONTROL_ENV, Some("/tmp/clip-now")),
|
|
],
|
|
|| {
|
|
assert_eq!(
|
|
launcher_focus_signal_path(),
|
|
PathBuf::from("/tmp/focus-now")
|
|
);
|
|
assert_eq!(
|
|
launcher_clipboard_control_path(),
|
|
PathBuf::from("/tmp/clip-now")
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn launcher_parent_env_parsing_is_strict_and_trims_ticks() {
|
|
temp_env::with_vars(
|
|
[
|
|
(LAUNCHER_PARENT_PID_ENV, None::<&str>),
|
|
(LAUNCHER_PARENT_START_TICKS_ENV, None::<&str>),
|
|
],
|
|
|| assert!(launcher_parent_process_from_env().is_none()),
|
|
);
|
|
|
|
temp_env::with_var(LAUNCHER_PARENT_PID_ENV, Some("not-a-pid"), || {
|
|
assert!(launcher_parent_process_from_env().is_none());
|
|
});
|
|
|
|
temp_env::with_vars(
|
|
[
|
|
(LAUNCHER_PARENT_PID_ENV, Some("42")),
|
|
(LAUNCHER_PARENT_START_TICKS_ENV, Some(" 123456 ")),
|
|
],
|
|
|| {
|
|
let parent = launcher_parent_process_from_env().expect("parent env");
|
|
assert_eq!(parent.pid, 42);
|
|
assert_eq!(parent.start_ticks.as_deref(), Some("123456"));
|
|
},
|
|
);
|
|
|
|
temp_env::with_vars(
|
|
[
|
|
(LAUNCHER_PARENT_PID_ENV, Some("42")),
|
|
(LAUNCHER_PARENT_START_TICKS_ENV, Some(" ")),
|
|
],
|
|
|| {
|
|
let parent = launcher_parent_process_from_env().expect("parent env");
|
|
assert_eq!(parent.pid, 42);
|
|
assert_eq!(parent.start_ticks, None);
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
#[cfg(coverage)]
|
|
fn launcher_parent_watchdog_stub_is_non_exiting_under_coverage() {
|
|
temp_env::with_var(LAUNCHER_PARENT_PID_ENV, None::<&str>, || {
|
|
start_launcher_child_parent_watchdog_from_env();
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn runtime_env_vars_emit_selected_controls() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_PASTE_KEY", None::<&str>),
|
|
("LESAVKA_PASTE_KEY_FILE", None::<&str>),
|
|
("LESAVKA_PASTE_RPC", None::<&str>),
|
|
("LESAVKA_PASTE_MAX", None::<&str>),
|
|
("LESAVKA_PASTE_DELAY_MS", None::<&str>),
|
|
("LESAVKA_CLIPBOARD_CMD", None::<&str>),
|
|
("LESAVKA_CLIPBOARD_TIMEOUT_MS", None::<&str>),
|
|
],
|
|
|| {
|
|
let mut state = LauncherState::new();
|
|
state.set_routing(InputRouting::Local);
|
|
state.set_view_mode(ViewMode::Unified);
|
|
state.select_camera(Some("/dev/video0".to_string()));
|
|
state.select_camera_quality(Some(devices::CameraMode::new(1920, 1080, 30)));
|
|
state.select_microphone(Some("alsa_input.test".to_string()));
|
|
state.select_speaker(Some("alsa_output.test".to_string()));
|
|
state.set_camera_channel_enabled(true);
|
|
state.set_microphone_channel_enabled(true);
|
|
state.set_audio_channel_enabled(true);
|
|
state.select_keyboard(Some("/dev/input/event10".to_string()));
|
|
state.select_mouse(Some("/dev/input/event11".to_string()));
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_CAPTURE_REMOTE"), Some(&"0".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_VIEW_MODE"), Some(&"unified".to_string()));
|
|
assert!(!envs.contains_key("LESAVKA_AUDIO_DISABLE"));
|
|
assert!(!envs.contains_key("LESAVKA_MIC_DISABLE"));
|
|
assert_eq!(
|
|
envs.get("LESAVKA_CLIPBOARD_DELAY_MS"),
|
|
Some(&"18".to_string())
|
|
);
|
|
assert_eq!(envs.get("LESAVKA_AUDIO_GAIN"), Some(&"2.000".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_MIC_GAIN"), Some(&"1.000".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_CAM_WIDTH"), Some(&"1920".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_CAM_HEIGHT"), Some(&"1080".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_CAM_FPS"), Some(&"30".to_string()));
|
|
assert_eq!(
|
|
envs.get("LESAVKA_CAM_H264_KBIT"),
|
|
Some(&"12000".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get(REMOTE_INPUT_FAILSAFE_SECONDS_ENV),
|
|
Some(&DEFAULT_REMOTE_INPUT_FAILSAFE_SECONDS.to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_DISABLE_VIDEO_RENDER"),
|
|
Some(&"1".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_CAM_SOURCE"),
|
|
Some(&"/dev/video0".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_MIC_SOURCE"),
|
|
Some(&"alsa_input.test".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_AUDIO_SINK"),
|
|
Some(&"alsa_output.test".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_KEYBOARD_DEVICE"),
|
|
Some(&"/dev/input/event10".to_string())
|
|
);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_MOUSE_DEVICE"),
|
|
Some(&"/dev/input/event11".to_string())
|
|
);
|
|
assert!(!envs.contains_key("LESAVKA_PASTE_KEY_FILE"));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn runtime_env_vars_passes_through_clipboard_transport_env() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_PASTE_KEY_FILE", Some("/tmp/paste-key")),
|
|
("LESAVKA_PASTE_RPC", Some("1")),
|
|
("LESAVKA_CLIPBOARD_CMD", Some("cat /tmp/secret")),
|
|
],
|
|
|| {
|
|
let state = LauncherState::new();
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(
|
|
envs.get("LESAVKA_PASTE_KEY_FILE"),
|
|
Some(&"/tmp/paste-key".to_string())
|
|
);
|
|
assert_eq!(envs.get("LESAVKA_PASTE_RPC"), Some(&"1".to_string()));
|
|
assert_eq!(
|
|
envs.get("LESAVKA_CLIPBOARD_CMD"),
|
|
Some(&"cat /tmp/secret".to_string())
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn runtime_env_vars_passes_through_remote_failsafe_launch_option() {
|
|
temp_env::with_var(REMOTE_INPUT_FAILSAFE_SECONDS_ENV, Some("60"), || {
|
|
let state = LauncherState::new();
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(
|
|
envs.get(REMOTE_INPUT_FAILSAFE_SECONDS_ENV),
|
|
Some(&"60".to_string())
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn runtime_env_vars_keeps_remote_failsafe_disabled_for_invalid_launch_option() {
|
|
temp_env::with_var(REMOTE_INPUT_FAILSAFE_SECONDS_ENV, Some("later"), || {
|
|
let state = LauncherState::new();
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(
|
|
envs.get(REMOTE_INPUT_FAILSAFE_SECONDS_ENV),
|
|
Some(&DEFAULT_REMOTE_INPUT_FAILSAFE_SECONDS.to_string())
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_env_vars_do_not_disable_breakout_video_windows() {
|
|
let mut state = LauncherState::new();
|
|
state.set_view_mode(ViewMode::Breakout);
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert!(!envs.contains_key("LESAVKA_DISABLE_VIDEO_RENDER"));
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_env_vars_disable_enabled_channels_without_real_devices() {
|
|
let mut state = LauncherState::new();
|
|
state.select_microphone(Some("auto".to_string()));
|
|
state.select_speaker(Some("auto".to_string()));
|
|
state.set_microphone_channel_enabled(true);
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_MIC_DISABLE"), Some(&"1".to_string()));
|
|
assert!(!envs.contains_key("LESAVKA_MIC_SOURCE"));
|
|
assert_eq!(envs.get("LESAVKA_AUDIO_DISABLE"), Some(&"1".to_string()));
|
|
assert!(!envs.contains_key("LESAVKA_AUDIO_SINK"));
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_env_vars_emit_selected_audio_gain() {
|
|
let mut state = LauncherState::new();
|
|
state.set_audio_gain_percent(425);
|
|
state.set_mic_gain_percent(275);
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_AUDIO_GAIN"), Some(&"4.250".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_MIC_GAIN"), Some(&"2.750".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_env_vars_use_channel_toggles_for_media_inclusion() {
|
|
let mut state = LauncherState::new();
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_CAM_DISABLE"), Some(&"1".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_MIC_DISABLE"), Some(&"1".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_AUDIO_DISABLE"), Some(&"1".to_string()));
|
|
|
|
state.select_camera(Some("/dev/video0".to_string()));
|
|
state.select_microphone(Some("alsa_input.usb".to_string()));
|
|
state.select_speaker(Some("alsa_output.usb".to_string()));
|
|
state.set_camera_channel_enabled(true);
|
|
state.set_microphone_channel_enabled(true);
|
|
let envs = runtime_env_vars(&state);
|
|
assert!(!envs.contains_key("LESAVKA_CAM_DISABLE"));
|
|
assert!(!envs.contains_key("LESAVKA_MIC_DISABLE"));
|
|
assert!(!envs.contains_key("LESAVKA_AUDIO_DISABLE"));
|
|
|
|
state.set_audio_channel_enabled(false);
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_AUDIO_DISABLE"), Some(&"1".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_env_vars_disable_uplink_media_when_unstaged() {
|
|
let state = LauncherState::new();
|
|
|
|
let envs = runtime_env_vars(&state);
|
|
assert_eq!(envs.get("LESAVKA_CAM_DISABLE"), Some(&"1".to_string()));
|
|
assert_eq!(envs.get("LESAVKA_MIC_DISABLE"), Some(&"1".to_string()));
|
|
assert!(!envs.contains_key("LESAVKA_CAM_SOURCE"));
|
|
assert!(!envs.contains_key("LESAVKA_MIC_SOURCE"));
|
|
}
|
|
|
|
#[test]
|
|
fn maybe_run_launcher_returns_false_with_explicit_opt_out() {
|
|
let args = vec!["--no-launcher".to_string()];
|
|
assert!(!maybe_run_launcher(&args).expect("launcher check"));
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(coverage)]
|
|
fn maybe_run_launcher_returns_true_with_launcher_flag() {
|
|
let args = vec!["--launcher".to_string()];
|
|
assert!(maybe_run_launcher(&args).expect("launcher should run"));
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(coverage)]
|
|
fn maybe_run_launcher_defaults_to_launcher_for_empty_args() {
|
|
let args: Vec<String> = vec![];
|
|
assert!(maybe_run_launcher(&args).expect("launcher should run"));
|
|
}
|
|
|
|
#[test]
|
|
fn should_run_launcher_defaults_true_for_empty_args() {
|
|
assert!(should_run_launcher(&[]));
|
|
}
|
|
|
|
#[test]
|
|
fn should_run_launcher_honors_explicit_opt_out() {
|
|
let args = vec!["--no-launcher".to_string()];
|
|
assert!(!should_run_launcher(&args));
|
|
}
|
|
|
|
#[test]
|
|
fn should_run_launcher_includes_legacy_direct_server_args() {
|
|
let args = vec!["http://server:50051".to_string()];
|
|
assert!(should_run_launcher(&args));
|
|
}
|
|
|
|
#[test]
|
|
fn should_run_launcher_with_server_flag() {
|
|
let args = vec!["--server".to_string(), "http://server:50051".to_string()];
|
|
assert!(should_run_launcher(&args));
|
|
}
|
|
|
|
#[test]
|
|
fn proc_stat_start_ticks_handles_process_names_with_spaces() {
|
|
let stat = "1234 (lesavka client) S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 424242 21";
|
|
|
|
assert_eq!(proc_stat_start_ticks(stat).as_deref(), Some("424242"));
|
|
assert_eq!(proc_stat_start_ticks("missing-parens"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn launcher_parent_start_ticks_is_available_for_current_process() {
|
|
assert!(launcher_parent_start_ticks().is_some());
|
|
|
|
let parent = LauncherParentProcess {
|
|
pid: std::process::id(),
|
|
start_ticks: launcher_parent_start_ticks(),
|
|
};
|
|
assert!(launcher_parent_process_matches(&parent));
|
|
|
|
let mismatched = LauncherParentProcess {
|
|
pid: std::process::id(),
|
|
start_ticks: Some("definitely-not-current".to_string()),
|
|
};
|
|
assert!(!launcher_parent_process_matches(&mismatched));
|
|
|
|
let missing = LauncherParentProcess {
|
|
pid: u32::MAX,
|
|
start_ticks: None,
|
|
};
|
|
assert!(!launcher_parent_process_matches(&missing));
|
|
}
|