use super::*; use crate::launcher::{ devices::DeviceCatalog, preview::PreviewBinding, state::LauncherState, ui_components::build_launcher_view, }; use gtk::prelude::*; use serial_test::serial; use std::{cell::RefCell, rc::Rc}; #[test] fn local_test_detail_mentions_idle_and_running_modes() { assert!(local_test_detail(false, false, false, false).contains("idle")); let running = local_test_detail(true, true, false, false); assert!(running.contains("camera preview")); assert!(running.contains("mic monitor")); } #[test] fn gpio_power_label_tracks_detected_devices() { let mut power = CapturePowerStatus::default(); assert_eq!(gpio_power_label(&power), "Unavailable"); power.available = true; assert_eq!(gpio_power_label(&power), "Power Off"); power.enabled = true; assert_eq!(gpio_power_label(&power), "No Eyes"); power.detected_devices = 1; assert_eq!(gpio_power_label(&power), "1 Eye"); power.detected_devices = 2; assert_eq!(gpio_power_label(&power), "2 Eyes"); } #[gtk::test] #[serial] fn launcher_shell_measures_inside_a_1080p_desktop_budget() { if gtk::gdk::Display::default().is_none() { return; } let app = gtk::Application::builder() .application_id("dev.lesavka.test-layout-budget") .build(); let _ = app.register(None::<>k::gio::Cancellable>); let state = LauncherState::new(); let view = build_launcher_view( &app, "http://127.0.0.1:50051", &DeviceCatalog::default(), &state, ); let (min_width, natural_width, _, _) = view.window.measure(gtk::Orientation::Horizontal, -1); let (tall_min_width, tall_natural_width, _, _) = view.window.measure(gtk::Orientation::Horizontal, 1080); let (min_height, natural_height, _, _) = view.window.measure(gtk::Orientation::Vertical, 1920); assert!( min_width <= 1920 && natural_width <= 1920, "launcher width budget regressed: min={min_width}, natural={natural_width}" ); assert!( tall_min_width <= 1920 && tall_natural_width <= 1920, "launcher 1080p-tall width regressed: min={tall_min_width}, natural={tall_natural_width}" ); assert!( min_height <= 1080 && natural_height <= 1080, "launcher height budget regressed: min={min_height}, natural={natural_height}" ); } #[test] fn server_chip_state_tracks_connection_not_just_reachability() { let mut state = LauncherState::new(); assert_eq!(server_light_state(&state, false), StatusLightState::Idle); assert_eq!(server_version_label(&state), "-"); state.set_server_available(true); state.set_server_version(Some("0.12.3".to_string())); assert_eq!(server_light_state(&state, false), StatusLightState::Live); assert_eq!(server_version_label(&state), "v0.12.3"); assert_eq!( server_light_state(&state, true), StatusLightState::Connected ); state.set_server_version(Some("v0.12.4".to_string())); assert_eq!(server_light_state(&state, false), StatusLightState::Warning); assert_eq!(server_light_state(&state, true), StatusLightState::Caution); assert_eq!(server_version_label(&state), "v0.12.4"); state.set_server_version(Some(" ".to_string())); assert_eq!(server_light_state(&state, false), StatusLightState::Idle); assert_eq!(server_version_label(&state), "-"); } #[test] fn capture_power_detail_mentions_detected_eyes_when_powered() { let power = CapturePowerStatus { available: true, enabled: true, detail: "active/running".to_string(), detected_devices: 1, ..Default::default() }; assert!(capture_power_detail(&power).contains("1 eye detected")); } #[test] fn compact_device_name_prefers_basename_when_available() { assert_eq!(compact_device_name("/dev/video0"), "video0"); assert_eq!(compact_device_name("alsa_input.usb"), "alsa_input.usb"); } #[test] fn strip_ansi_sequences_removes_terminal_codes() { let raw = "\u{1b}[32mINFO\u{1b}[0m hello"; assert_eq!(strip_ansi_sequences(raw), "INFO hello"); } #[test] fn classify_log_tags_assigns_prefix_and_severity_colors() { let tags = classify_log_tags("[relay] WARN pipeline failed"); assert!(tags.contains(&"log-relay")); assert!(tags.contains(&"log-error") || tags.contains(&"log-warn")); } #[test] #[doc = "Verifies the default console filter hides relay INFO noise."] fn session_log_filter_hides_noisy_info_by_default_but_keeps_errors() { assert!(!should_show_session_log_line( "[relay] 2026-04-22T23:20:17Z INFO ThreadId(01) audio packet received packet=3000", ConsoleLogLevel::Warn )); assert!(!should_show_session_log_line( "[relay] 2026-04-22T23:20:17Z INFO ThreadId(04) decoded audio level rms=-32", ConsoleLogLevel::Warn )); assert!(should_show_session_log_line( "[relay] 2026-04-22T23:20:17Z WARN pipeline is recovering", ConsoleLogLevel::Warn )); assert!(should_show_session_log_line( "[relay] 2026-04-22T23:20:17Z INFO ❌ connect failed", ConsoleLogLevel::Error )); assert!(should_show_session_log_line( "[launcher] Relay connected with inputs routed to remote.", ConsoleLogLevel::Error )); assert!(should_show_session_log_line( "[relay] 2026-04-22T23:20:17Z INFO audio packet received", ConsoleLogLevel::Info )); } #[test] fn write_audio_gain_request_formats_live_control_file() { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("gain.control"); write_audio_gain_request(&path, 425).expect("write gain"); let raw = std::fs::read_to_string(path).expect("read gain"); assert!(raw.starts_with("4.250 "), "{raw}"); } #[test] fn write_mic_gain_request_formats_live_control_file() { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("mic-gain.control"); write_mic_gain_request(&path, 325).expect("write gain"); let raw = std::fs::read_to_string(path).expect("read gain"); assert!(raw.starts_with("3.250 "), "{raw}"); } #[gtk::test] #[serial] fn dock_all_displays_to_preview_closes_popouts_and_resets_surfaces() { if gtk::gdk::Display::default().is_none() { return; } let app = gtk::Application::builder() .application_id("dev.lesavka.test-dock") .build(); let _ = app.register(None::<>k::gio::Cancellable>); let state = Rc::new(RefCell::new(LauncherState::new())); state .borrow_mut() .set_display_surface(0, DisplaySurface::Window); state .borrow_mut() .set_display_surface(1, DisplaySurface::Window); let state_snapshot = state.borrow().clone(); let view = build_launcher_view( &app, "http://127.0.0.1:50051", &DeviceCatalog::default(), &state_snapshot, ); let child_proc = Rc::new(RefCell::new(None::)); let left_binding = PreviewBinding::test_stub(); let right_binding = PreviewBinding::test_stub(); { let mut popouts = view.popouts.borrow_mut(); popouts[0] = Some(PopoutWindowHandle { window: gtk::ApplicationWindow::builder() .application(&app) .title("Left") .build(), frame: gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false), picture: gtk::Picture::new(), status_label: gtk::Label::new(None), binding: left_binding, }); popouts[1] = Some(PopoutWindowHandle { window: gtk::ApplicationWindow::builder() .application(&app) .title("Right") .build(), frame: gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false), picture: gtk::Picture::new(), status_label: gtk::Label::new(None), binding: right_binding, }); } dock_all_displays_to_preview(&state, &child_proc, &view.popouts, &view.widgets); assert!(view.popouts.borrow().iter().all(|handle| handle.is_none())); assert_eq!(state.borrow().display_surface(0), DisplaySurface::Preview); assert_eq!(state.borrow().display_surface(1), DisplaySurface::Preview); } #[gtk::test] #[serial] fn dock_all_displays_to_preview_handles_reentrant_close_callbacks() { if gtk::gdk::Display::default().is_none() { return; } let app = gtk::Application::builder() .application_id("dev.lesavka.test-reentrant-dock") .build(); let _ = app.register(None::<>k::gio::Cancellable>); let state = Rc::new(RefCell::new(LauncherState::new())); state .borrow_mut() .set_display_surface(0, DisplaySurface::Window); let state_snapshot = state.borrow().clone(); let view = build_launcher_view( &app, "http://127.0.0.1:50051", &DeviceCatalog::default(), &state_snapshot, ); let child_proc = Rc::new(RefCell::new(None::)); let popouts = Rc::clone(&view.popouts); let window = gtk::ApplicationWindow::builder() .application(&app) .title("Reentrant") .build(); { let popouts = Rc::clone(&popouts); window.connect_close_request(move |_| { let _ = popouts.borrow_mut()[0].take(); glib::Propagation::Proceed }); } { let mut slot = popouts.borrow_mut(); slot[0] = Some(PopoutWindowHandle { window, frame: gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false), picture: gtk::Picture::new(), status_label: gtk::Label::new(None), binding: PreviewBinding::test_stub(), }); } dock_all_displays_to_preview(&state, &child_proc, &popouts, &view.widgets); assert!(popouts.borrow().iter().all(|handle| handle.is_none())); assert_eq!(state.borrow().display_surface(0), DisplaySurface::Preview); } #[gtk::test] #[serial] fn shutdown_launcher_runtime_closes_preview_bindings_and_popouts() { if gtk::gdk::Display::default().is_none() { return; } let app = gtk::Application::builder() .application_id("dev.lesavka.test-shutdown") .build(); let _ = app.register(None::<>k::gio::Cancellable>); let state = Rc::new(RefCell::new(LauncherState::new())); let state_snapshot = state.borrow().clone(); let view = build_launcher_view( &app, "http://127.0.0.1:50051", &DeviceCatalog::default(), &state_snapshot, ); let child_proc = Rc::new(RefCell::new(None::)); let tests = Rc::new(RefCell::new(DeviceTestController::new())); let left_binding = PreviewBinding::test_stub(); let right_binding = PreviewBinding::test_stub(); *view.widgets.display_panes[0].preview_binding.borrow_mut() = Some(left_binding.clone()); *view.widgets.display_panes[1].preview_binding.borrow_mut() = Some(right_binding.clone()); { let mut popouts = view.popouts.borrow_mut(); popouts[0] = Some(PopoutWindowHandle { window: gtk::ApplicationWindow::builder() .application(&app) .title("Left") .build(), frame: gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false), picture: gtk::Picture::new(), status_label: gtk::Label::new(None), binding: PreviewBinding::test_stub(), }); } *view.diagnostics_popout.borrow_mut() = Some( gtk::ApplicationWindow::builder() .application(&app) .title("Diagnostics") .build(), ); *view.log_popout.borrow_mut() = Some( gtk::ApplicationWindow::builder() .application(&app) .title("Log") .build(), ); shutdown_launcher_runtime( &child_proc, &tests, None, &view.widgets, &view.popouts, &view.diagnostics_popout, &view.log_popout, ); assert!(view.popouts.borrow().iter().all(|handle| handle.is_none())); assert!( view.widgets.display_panes[0] .preview_binding .borrow() .is_none() ); assert!( view.widgets.display_panes[1] .preview_binding .borrow() .is_none() ); assert!(view.diagnostics_popout.borrow().is_none()); assert!(view.log_popout.borrow().is_none()); }