//! Include-based coverage for server camera sink internals. //! //! Scope: include `server/src/video_sinks.rs` and directly exercise private sink //! selection/dispatch helpers through stable constructor paths. //! Targets: `server/src/video_sinks.rs`. //! Why: sink internals carry substantial branch logic beyond public smoke tests. mod camera { pub use lesavka_server::camera::*; } mod video_support { pub use lesavka_server::video_support::*; } #[allow(warnings)] mod video_sinks_include_contract { include!(env!("LESAVKA_SERVER_VIDEO_SINKS_SRC")); use crate::camera::{CameraOutput, HdmiConnector, HdmiMode}; use serial_test::serial; use temp_env::with_var; fn cfg(codec: CameraCodec) -> CameraConfig { CameraConfig { output: CameraOutput::Hdmi, codec, width: 640, height: 360, fps: 24, hdmi: None, } } fn hdmi_cfg_with_ugreen_like_modes(codec: CameraCodec) -> CameraConfig { CameraConfig { output: CameraOutput::Hdmi, codec, width: 1280, height: 720, fps: 30, hdmi: Some(HdmiConnector { name: String::from("card1-HDMI-A-2"), id: Some(43), modes: vec![ HdmiMode { width: 1920, height: 1080, }, HdmiMode { width: 1024, height: 768, }, ], }), } } fn init_gst() { let _ = gst::init(); } #[test] #[serial] fn build_hdmi_sink_respects_env_override_success_path() { init_gst(); with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { let sink = build_hdmi_sink(&cfg(CameraCodec::H264)); assert!(sink.is_ok(), "known override sink should build"); }); } #[test] #[serial] #[cfg(not(coverage))] fn build_hdmi_sink_configures_fbdev_override_for_capture_adapters() { init_gst(); if gst::ElementFactory::find("fbdevsink").is_none() { return; } with_var("LESAVKA_HDMI_SINK", Some("fbdevsink"), || { with_var("LESAVKA_HDMI_FBDEV", Some("/dev/fb42"), || { let sink = build_hdmi_sink(&cfg(CameraCodec::H264)) .expect("fbdevsink override should build"); if sink.has_property("device", None) { assert_eq!(sink.property::("device"), "/dev/fb42"); } if sink.has_property("sync", None) { assert!( !sink.property::("sync"), "fbdev HDMI output should not clock-sync WAN camera frames" ); } }); }); } #[test] #[serial] fn build_hdmi_sink_invalid_override_surfaces_error() { init_gst(); with_var( "LESAVKA_HDMI_SINK", Some("definitely-not-a-real-gst-element"), || { let sink = build_hdmi_sink(&cfg(CameraCodec::H264)); assert!(sink.is_err(), "invalid override must fail"); }, ); } #[test] #[serial] fn build_hdmi_sink_falls_back_when_override_is_unset() { init_gst(); with_var("LESAVKA_HDMI_SINK", None::<&str>, || { let sink = build_hdmi_sink(&cfg(CameraCodec::H264)); assert!(sink.is_ok(), "fallback sink should build"); }); } #[test] fn hdmi_display_size_scales_uplink_to_capture_adapter_mode() { let cfg = hdmi_cfg_with_ugreen_like_modes(CameraCodec::H264); assert_eq!((cfg.width, cfg.height), (1280, 720)); assert_eq!(cfg.hdmi_display_size(), (1920, 1080)); } #[test] #[serial] #[cfg(not(coverage))] fn build_hdmi_sink_pins_kms_connector_and_modesetting_when_available() { init_gst(); if gst::ElementFactory::find("kmssink").is_none() { return; } with_var("LESAVKA_HDMI_SINK", None::<&str>, || { with_var("LESAVKA_HDMI_DRIVER", Some("vc4"), || { with_var("LESAVKA_HDMI_RESTORE_CRTC", None::<&str>, || { let sink = build_hdmi_sink(&hdmi_cfg_with_ugreen_like_modes(CameraCodec::H264)) .expect("kmssink should build"); if sink.has_property("force-modesetting", None) { assert!( sink.property::("force-modesetting"), "kmssink must drive the HDMI mode instead of relying on desktop state" ); } if sink.has_property("restore-crtc", None) { assert!( !sink.property::("restore-crtc"), "dedicated HDMI capture output should not restore the console CRTC" ); } if sink.has_property("connector-id", None) { assert_eq!(sink.property::("connector-id"), 43); } if sink.has_property("driver-name", None) { assert_eq!(sink.property::("driver-name"), "vc4"); } }); }); }); } #[test] #[serial] fn bool_env_parser_accepts_operator_friendly_values() { with_var("LESAVKA_BOOL_TEST", Some("yes"), || { assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(true)); }); with_var("LESAVKA_BOOL_TEST", Some("off"), || { assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(false)); }); with_var("LESAVKA_BOOL_TEST", Some("shrug"), || { assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), None); }); } #[test] #[serial] fn camera_sink_dispatch_is_stable_for_hdmi_variant() { with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { if let Ok(sink) = HdmiSink::new(&cfg(CameraCodec::Mjpeg)) { let cam_sink = CameraSink::Hdmi(sink); cam_sink.push(VideoPacket { id: 8, pts: 1, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }); } }); } #[test] #[serial] fn camera_sink_dispatch_is_stable_for_uvc_variant() { if let Ok(sink) = WebcamSink::new("/dev/video-definitely-missing", &cfg(CameraCodec::Mjpeg)) { let cam_sink = CameraSink::Uvc(sink); cam_sink.push(VideoPacket { id: 9, pts: 2, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }); } } #[test] #[serial] fn camera_relay_feed_covers_dev_mode_dump_branch_without_panicking() { with_var("LESAVKA_DEV_MODE", Some("1"), || { with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { if let Ok(relay) = CameraRelay::new_hdmi(3, &cfg(CameraCodec::H264)) { relay.feed(VideoPacket { id: 3, pts: 3, data: vec![0, 0, 0, 1, 0x65, 0x88, 0x84], ..Default::default() }); } }); }); } }