lesavka/testing/tests/server_video_sinks_include_contract.rs

256 lines
8.5 KiB
Rust

//! 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::*;
}
#[path = "../../server/src/media_timing.rs"]
#[allow(warnings)]
mod media_timing;
#[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::<String>("device"), "/dev/fb42");
}
if sink.has_property("sync", None) {
assert!(
sink.property::<bool>("sync"),
"fbdev HDMI output should honor the shared session clock"
);
}
});
});
}
#[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::<bool>("force-modesetting"),
"kmssink must drive the HDMI mode instead of relying on desktop state"
);
}
if sink.has_property("restore-crtc", None) {
assert!(
!sink.property::<bool>("restore-crtc"),
"dedicated HDMI capture output should not restore the console CRTC"
);
}
if sink.has_property("connector-id", None) {
assert_eq!(sink.property::<i32>("connector-id"), 43);
}
if sink.has_property("driver-name", None) {
assert_eq!(sink.property::<String>("driver-name"), "vc4");
}
});
});
});
}
#[test]
#[serial]
fn bool_env_parser_accepts_operator_friendly_values() {
with_var("LESAVKA_BOOL_TEST", None::<&str>, || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), None);
});
with_var("LESAVKA_BOOL_TEST", Some("yes"), || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(true));
});
with_var("LESAVKA_BOOL_TEST", Some("TRUE"), || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(true));
});
with_var("LESAVKA_BOOL_TEST", Some("1"), || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(true));
});
with_var("LESAVKA_BOOL_TEST", Some("on"), || {
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("0"), || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(false));
});
with_var("LESAVKA_BOOL_TEST", Some("false"), || {
assert_eq!(read_bool_env("LESAVKA_BOOL_TEST"), Some(false));
});
with_var("LESAVKA_BOOL_TEST", Some("no"), || {
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()
});
}
});
});
}
}