174 lines
5.1 KiB
Rust
174 lines
5.1 KiB
Rust
// Integration coverage for client app support helpers.
|
|
//
|
|
// Scope: exercise server-address, camera-capability, and retry-delay helpers
|
|
// against deterministic local types.
|
|
// Targets: `client/src/app_support.rs`.
|
|
// Why: these startup decisions shape live camera codec negotiation and should
|
|
// stay covered without adding source-file LOC to the hygiene ratchet.
|
|
|
|
mod handshake {
|
|
#[allow(dead_code)]
|
|
#[derive(Clone, Debug)]
|
|
pub struct PeerCaps {
|
|
pub camera: bool,
|
|
pub microphone: bool,
|
|
pub bundled_webcam_media: bool,
|
|
pub server_version: Option<String>,
|
|
pub camera_output: Option<String>,
|
|
pub camera_codec: Option<String>,
|
|
pub camera_width: Option<u32>,
|
|
pub camera_height: Option<u32>,
|
|
pub camera_fps: Option<u32>,
|
|
pub eye_width: Option<u32>,
|
|
pub eye_height: Option<u32>,
|
|
pub eye_fps: Option<u32>,
|
|
}
|
|
}
|
|
|
|
mod input {
|
|
pub mod camera {
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub enum CameraCodec {
|
|
Mjpeg,
|
|
H264,
|
|
Hevc,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct CameraConfig {
|
|
pub codec: CameraCodec,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
pub fps: u32,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[path = "../../../../client/src/app_support.rs"]
|
|
mod app_support;
|
|
|
|
use app_support::{
|
|
DEFAULT_SERVER_ADDR, camera_config_from_caps, next_delay, resolve_server_addr,
|
|
sanitize_video_queue,
|
|
};
|
|
use handshake::PeerCaps;
|
|
use input::camera::CameraCodec;
|
|
use serial_test::serial;
|
|
use std::time::Duration;
|
|
|
|
fn peer_caps(codec: &str) -> PeerCaps {
|
|
PeerCaps {
|
|
camera: true,
|
|
microphone: false,
|
|
bundled_webcam_media: false,
|
|
server_version: None,
|
|
camera_output: Some(String::from("uvc")),
|
|
camera_codec: Some(codec.to_string()),
|
|
camera_width: Some(1280),
|
|
camera_height: Some(720),
|
|
camera_fps: Some(25),
|
|
eye_width: None,
|
|
eye_height: None,
|
|
eye_fps: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn resolve_server_addr_prefers_cli_then_env_then_default() {
|
|
assert_eq!(
|
|
resolve_server_addr(&[String::from("http://cli:1")], Some("http://env:2")),
|
|
"http://cli:1"
|
|
);
|
|
assert_eq!(
|
|
resolve_server_addr(
|
|
&[
|
|
String::from("--no-launcher"),
|
|
String::from("--server"),
|
|
String::from("http://cli-flag:3"),
|
|
],
|
|
Some("http://env:2"),
|
|
),
|
|
"http://cli-flag:3"
|
|
);
|
|
assert_eq!(
|
|
resolve_server_addr(&[String::from("--launcher")], Some("http://env:2")),
|
|
"http://env:2"
|
|
);
|
|
assert_eq!(
|
|
resolve_server_addr(&[], Some("http://env:2")),
|
|
"http://env:2"
|
|
);
|
|
assert_eq!(resolve_server_addr(&[], None), DEFAULT_SERVER_ADDR);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn camera_config_from_caps_requires_complete_profile() {
|
|
temp_env::with_var("LESAVKA_CAM_CODEC", None::<&str>, || {
|
|
let mut caps = peer_caps("mjpeg");
|
|
let config = camera_config_from_caps(&caps).expect("complete caps should map");
|
|
assert!(matches!(config.codec, CameraCodec::Mjpeg));
|
|
assert_eq!(config.width, 1280);
|
|
|
|
caps.camera_codec = Some(String::from("h265"));
|
|
let config = camera_config_from_caps(&caps).expect("h265 alias should map");
|
|
assert!(matches!(config.codec, CameraCodec::Hevc));
|
|
|
|
caps.camera_codec = Some(String::from("vp9"));
|
|
assert!(camera_config_from_caps(&caps).is_none());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn camera_config_from_caps_uses_negotiated_codec_over_launcher_default() {
|
|
temp_env::with_var("LESAVKA_CAM_CODEC", Some("mjpeg"), || {
|
|
let config = camera_config_from_caps(&peer_caps("hevc")).expect("caps codec should map");
|
|
assert!(matches!(config.codec, CameraCodec::Hevc));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn camera_config_from_caps_allows_explicit_forced_codec_override() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_CAM_CODEC", Some("mjpeg")),
|
|
("LESAVKA_CAM_CODEC_FORCE", Some("1")),
|
|
],
|
|
|| {
|
|
let config = camera_config_from_caps(&peer_caps("hevc")).expect("forced override");
|
|
assert!(matches!(config.codec, CameraCodec::Mjpeg));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn camera_config_force_flag_ignores_unknown_override() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_CAM_CODEC", Some("vp9")),
|
|
("LESAVKA_CAM_CODEC_FORCE", Some("1")),
|
|
],
|
|
|| {
|
|
let config = camera_config_from_caps(&peer_caps("hevc")).expect("fallback codec");
|
|
assert!(matches!(config.codec, CameraCodec::Hevc));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sanitize_video_queue_enforces_floor() {
|
|
assert_eq!(sanitize_video_queue(None), 8);
|
|
assert_eq!(sanitize_video_queue(Some(1)), 4);
|
|
assert_eq!(sanitize_video_queue(Some(32)), 32);
|
|
}
|
|
|
|
#[test]
|
|
fn next_delay_doubles_until_capped() {
|
|
assert_eq!(next_delay(Duration::from_secs(1)), Duration::from_secs(2));
|
|
assert_eq!(next_delay(Duration::from_secs(15)), Duration::from_secs(30));
|
|
assert_eq!(next_delay(Duration::from_secs(31)), Duration::from_secs(30));
|
|
}
|