//! Integration smoke coverage for server camera sink constructors. //! //! Scope: exercise public sink and relay constructors with resilient //! assertions that tolerate host-specific media capabilities. //! Targets: `server/src/video_sinks.rs`. //! Why: sink setup contains substantial branch logic that should be executed //! in CI even when real HDMI/UVC hardware is unavailable. use lesavka_common::lesavka::VideoPacket; use lesavka_server::camera::{CameraCodec, CameraConfig, CameraOutput}; use lesavka_server::video::{CameraRelay, HdmiSink, WebcamSink}; use serial_test::serial; use temp_env::with_var; fn hdmi_config(codec: CameraCodec) -> CameraConfig { CameraConfig { output: CameraOutput::Hdmi, codec, width: 640, height: 360, fps: 30, hdmi: None, } } #[test] #[serial] fn webcam_sink_constructor_is_stable_for_missing_uvc_device() { let cfg = hdmi_config(CameraCodec::Mjpeg); match WebcamSink::new("/dev/video-definitely-missing", &cfg) { Ok(sink) => sink.push(VideoPacket { id: 2, pts: 0, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } } #[test] #[serial] fn hdmi_sink_constructor_and_push_are_stable_with_override() { with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { let cfg = hdmi_config(CameraCodec::H264); match HdmiSink::new(&cfg) { Ok(sink) => sink.push(VideoPacket { id: 2, pts: 0, data: vec![0, 0, 0, 1, 0x65], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } }); } #[test] #[serial] fn camera_relay_hdmi_constructor_and_feed_are_stable() { with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { let cfg = hdmi_config(CameraCodec::Mjpeg); match CameraRelay::new_hdmi(7, &cfg) { Ok(relay) => relay.feed(VideoPacket { id: 2, pts: 123, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } }); } #[test] #[serial] fn camera_relay_uvc_constructor_is_stable_for_missing_device() { let cfg = hdmi_config(CameraCodec::Mjpeg); match CameraRelay::new_uvc(9, "/dev/video-definitely-missing", &cfg) { Ok(relay) => relay.feed(VideoPacket { id: 2, pts: 321, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } } #[test] #[serial] fn webcam_sink_h264_constructor_path_is_stable() { let cfg = hdmi_config(CameraCodec::H264); match WebcamSink::new("/dev/video-definitely-missing", &cfg) { Ok(sink) => sink.push(VideoPacket { id: 3, pts: 55, data: vec![0, 0, 0, 1, 0x65], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } } #[test] #[serial] fn hdmi_sink_mjpeg_constructor_path_is_stable() { with_var("LESAVKA_HDMI_SINK", Some("autovideosink"), || { let cfg = hdmi_config(CameraCodec::Mjpeg); match HdmiSink::new(&cfg) { Ok(sink) => sink.push(VideoPacket { id: 4, pts: 99, data: vec![0xFF, 0xD8, 0xFF, 0xD9], ..Default::default() }), Err(err) => assert!(!err.to_string().trim().is_empty()), } }); } #[test] #[serial] fn hdmi_sink_override_with_invalid_element_returns_error() { with_var( "LESAVKA_HDMI_SINK", Some("definitely-not-a-real-gst-element"), || { let cfg = hdmi_config(CameraCodec::H264); let result = HdmiSink::new(&cfg); assert!( result.is_err(), "invalid sink override should fail construction" ); }, ); }