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 = 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)); }