testing: add camera runtime and uvc supervisor contracts
This commit is contained in:
parent
659a77ff5f
commit
3533b46eb8
79
testing/tests/server_camera_runtime_contract.rs
Normal file
79
testing/tests/server_camera_runtime_contract.rs
Normal file
@ -0,0 +1,79 @@
|
||||
//! Integration coverage for the camera runtime activation contract.
|
||||
//!
|
||||
//! Scope: validate activation error handling and configuration equality edges
|
||||
//! through public camera-runtime APIs.
|
||||
//! Targets: `server/src/camera_runtime.rs`.
|
||||
//! Why: camera runtime generation and guardrails are core to safe stream
|
||||
//! transitions, so they need direct integration-level assertions.
|
||||
|
||||
use lesavka_server::camera::{CameraCodec, CameraConfig, CameraOutput, HdmiConnector};
|
||||
use lesavka_server::camera_runtime::{CameraRuntime, camera_cfg_eq};
|
||||
use serial_test::serial;
|
||||
use temp_env::with_var;
|
||||
use tokio::runtime::Runtime;
|
||||
use tonic::Code;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn activate_rejects_uvc_when_disabled_and_bumps_generation() {
|
||||
let runtime = CameraRuntime::new();
|
||||
let cfg = CameraConfig {
|
||||
output: CameraOutput::Uvc,
|
||||
codec: CameraCodec::Mjpeg,
|
||||
width: 1280,
|
||||
height: 720,
|
||||
fps: 25,
|
||||
hdmi: None,
|
||||
};
|
||||
|
||||
with_var("LESAVKA_DISABLE_UVC", Some("1"), || {
|
||||
let rt = Runtime::new().expect("runtime");
|
||||
let result = rt.block_on(runtime.activate(&cfg));
|
||||
match result {
|
||||
Ok(_) => panic!("UVC should be disabled"),
|
||||
Err(err) => assert_eq!(err.code(), Code::FailedPrecondition),
|
||||
}
|
||||
});
|
||||
|
||||
assert!(runtime.is_active(1));
|
||||
assert!(!runtime.is_active(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camera_cfg_eq_handles_none_and_hdmi_connector_edges() {
|
||||
let uvc_a = CameraConfig {
|
||||
output: CameraOutput::Uvc,
|
||||
codec: CameraCodec::Mjpeg,
|
||||
width: 640,
|
||||
height: 360,
|
||||
fps: 30,
|
||||
hdmi: None,
|
||||
};
|
||||
let uvc_b = uvc_a.clone();
|
||||
assert!(camera_cfg_eq(&uvc_a, &uvc_b));
|
||||
|
||||
let hdmi_a = CameraConfig {
|
||||
output: CameraOutput::Hdmi,
|
||||
codec: CameraCodec::H264,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
fps: 30,
|
||||
hdmi: Some(HdmiConnector {
|
||||
name: String::from("HDMI-A-1"),
|
||||
id: Some(7),
|
||||
}),
|
||||
};
|
||||
let hdmi_b = hdmi_a.clone();
|
||||
assert!(camera_cfg_eq(&hdmi_a, &hdmi_b));
|
||||
|
||||
let mut different_id = hdmi_a.clone();
|
||||
different_id.hdmi = Some(HdmiConnector {
|
||||
name: String::from("HDMI-A-1"),
|
||||
id: Some(8),
|
||||
});
|
||||
assert!(!camera_cfg_eq(&hdmi_a, &different_id));
|
||||
|
||||
let mut missing_connector = hdmi_a;
|
||||
missing_connector.hdmi = None;
|
||||
assert!(!camera_cfg_eq(&missing_connector, &hdmi_b));
|
||||
}
|
||||
75
testing/tests/server_uvc_runtime_contract.rs
Normal file
75
testing/tests/server_uvc_runtime_contract.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! Integration coverage for the UVC runtime supervision contract.
|
||||
//!
|
||||
//! Scope: exercise the long-running UVC helper supervisor in both successful
|
||||
//! spawn and spawn-failure modes.
|
||||
//! Targets: `server/src/uvc_runtime.rs`.
|
||||
//! Why: the helper supervisor is operationally critical and should be covered
|
||||
//! through top-level integration behavior, not only unit checks.
|
||||
|
||||
use lesavka_server::uvc_runtime::supervise_uvc_control;
|
||||
use serial_test::serial;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::time::Duration;
|
||||
use temp_env::with_var;
|
||||
use tempfile::tempdir;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn supervise_uvc_control_starts_helper_when_device_is_set() {
|
||||
let dir = tempdir().expect("create temp dir");
|
||||
let marker = dir.path().join("marker.log");
|
||||
let helper = dir.path().join("helper.sh");
|
||||
fs::write(
|
||||
&helper,
|
||||
format!(
|
||||
"#!/usr/bin/env bash\nset -euo pipefail\necho \"$*\" >> '{}'\n",
|
||||
marker.display()
|
||||
),
|
||||
)
|
||||
.expect("write helper script");
|
||||
let mut perms = fs::metadata(&helper)
|
||||
.expect("helper metadata")
|
||||
.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(&helper, perms).expect("chmod helper script");
|
||||
|
||||
let helper_path = helper.to_string_lossy().to_string();
|
||||
with_var("LESAVKA_UVC_DEV", Some("/dev/video-loop"), || {
|
||||
let rt = Runtime::new().expect("runtime");
|
||||
let result = rt.block_on(async {
|
||||
tokio::time::timeout(
|
||||
Duration::from_millis(350),
|
||||
supervise_uvc_control(helper_path),
|
||||
)
|
||||
.await
|
||||
});
|
||||
assert!(result.is_err(), "supervisor should still be running");
|
||||
});
|
||||
|
||||
let calls = fs::read_to_string(marker).expect("read helper marker");
|
||||
assert!(
|
||||
calls.contains("--device /dev/video-loop"),
|
||||
"expected helper to receive device args, got: {calls}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn supervise_uvc_control_survives_missing_helper_binary() {
|
||||
with_var("LESAVKA_UVC_DEV", Some("/dev/video-loop"), || {
|
||||
let rt = Runtime::new().expect("runtime");
|
||||
let result = rt.block_on(async {
|
||||
tokio::time::timeout(
|
||||
Duration::from_millis(350),
|
||||
supervise_uvc_control(String::from("/definitely/missing/lesavka-uvc")),
|
||||
)
|
||||
.await
|
||||
});
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"supervisor should continue retrying forever"
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user