// Unit coverage for the HEVC-to-MJPEG decoded-frame guard. // // Scope: include the server guard in the root test taxonomy and model the UVC // helper decision that freezes suspicious frames. // Targets: server/src/video_sinks/hevc_mjpeg_guard.rs. // Why: when HEVC decode produces a syntactically valid but visually damaged // JPEG, users should see a short last-good-frame freeze instead of grey slabs, // black frames, or tearing. mod video_support { pub fn env_u32(name: &str, default: u32) -> u32 { std::env::var(name) .ok() .and_then(|value| value.trim().parse().ok()) .unwrap_or(default) } } #[allow(clippy::items_after_test_module)] mod guard { include!(concat!( env!("CARGO_MANIFEST_DIR"), "/server/src/video_sinks/hevc_mjpeg_guard.rs" )); pub fn jpeg_quality() -> u32 { hevc_jpeg_quality() } pub fn min_reference() -> u32 { min_reference_bytes() } pub fn drop_pct() -> u32 { size_drop_pct() } pub fn should_freeze(previous_bytes: u64, next_bytes: usize) -> bool { should_freeze_decoded_mjpeg(previous_bytes, next_bytes) } pub fn should_freeze_frame(previous_bytes: u64, decoded_mjpeg: &[u8]) -> bool { should_freeze_decoded_mjpeg_frame(previous_bytes, decoded_mjpeg) } } #[derive(Default)] struct GuardedFrameEmitter { last_good_bytes: u64, emitted_bytes: Vec, frozen_frames: usize, } impl GuardedFrameEmitter { fn push(&mut self, bytes: usize) -> bool { if guard::should_freeze(self.last_good_bytes, bytes) { self.frozen_frames += 1; return false; } self.last_good_bytes = bytes as u64; self.emitted_bytes.push(bytes); true } } #[test] fn guard_freezes_suspicious_decoded_collapses_but_allows_normal_variation() { temp_env::with_vars( [ ("LESAVKA_UVC_HEVC_FREEZE_ON_SIZE_DROP", Some("1")), ("LESAVKA_UVC_HEVC_SIZE_DROP_PCT", Some("45")), ("LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES", Some("65536")), ], || { assert!(!guard::should_freeze(200_000, 110_000)); assert!(guard::should_freeze(200_000, 80_000)); assert!(!guard::should_freeze(20_000, 1_000)); }, ); } #[test] fn last_good_frame_model_does_not_emit_damaged_hevc_mjpeg_frames() { temp_env::with_vars( [ ("LESAVKA_UVC_HEVC_FREEZE_ON_SIZE_DROP", Some("1")), ("LESAVKA_UVC_HEVC_SIZE_DROP_PCT", Some("45")), ("LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES", Some("65536")), ], || { let mut emitter = GuardedFrameEmitter::default(); assert!(emitter.push(220_000)); assert!(!emitter.push(20_000)); assert!(!emitter.push(60_000)); assert!(emitter.push(210_000)); assert_eq!(emitter.frozen_frames, 2); assert_eq!(emitter.emitted_bytes, vec![220_000, 210_000]); assert_eq!(emitter.last_good_bytes, 210_000); }, ); } #[test] fn guard_tuning_env_is_clamped_to_safe_runtime_bounds() { temp_env::with_vars( [ ("LESAVKA_UVC_HEVC_JPEG_QUALITY", Some("0")), ("LESAVKA_UVC_HEVC_SIZE_DROP_PCT", Some("0")), ("LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES", Some("0")), ], || { assert_eq!(guard::jpeg_quality(), 1); assert_eq!(guard::drop_pct(), 1); assert_eq!(guard::min_reference(), 1); }, ); temp_env::with_vars( [ ("LESAVKA_UVC_HEVC_JPEG_QUALITY", Some("101")), ("LESAVKA_UVC_HEVC_SIZE_DROP_PCT", Some("100")), ("LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES", Some("2048")), ], || { assert_eq!(guard::jpeg_quality(), 100); assert_eq!(guard::drop_pct(), 95); assert_eq!(guard::min_reference(), 2048); }, ); } fn jpeg_with_payload(payload: &[u8]) -> Vec { let mut bytes = vec![0xff, 0xd8, 0xff, 0xda]; bytes.extend_from_slice(payload); bytes.extend_from_slice(&[0xff, 0xd9]); bytes } #[test] fn guard_freezes_incomplete_and_low_entropy_decoded_frames() { temp_env::with_vars( [ ("LESAVKA_UVC_HEVC_FREEZE_ON_SIZE_DROP", Some("1")), ("LESAVKA_UVC_HEVC_SIZE_DROP_PCT", Some("45")), ("LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES", Some("65536")), ("LESAVKA_UVC_HEVC_MIN_PAYLOAD_DISTINCT_BYTES", Some("12")), ("LESAVKA_UVC_HEVC_DOMINANT_BYTE_PCT", Some("92")), ], || { let healthy_payload: Vec = (0..130_000).map(|idx| (idx % 251) as u8).collect(); let grey_slab_payload = vec![0x80; 130_000]; let black_slab_payload = vec![0x00; 130_000]; let mut truncated = jpeg_with_payload(&healthy_payload); truncated.truncate(truncated.len() - 1); assert!(!guard::should_freeze_frame( 220_000, &jpeg_with_payload(&healthy_payload), )); assert!(guard::should_freeze_frame( 220_000, &jpeg_with_payload(&grey_slab_payload), )); assert!(guard::should_freeze_frame( 220_000, &jpeg_with_payload(&black_slab_payload), )); assert!(guard::should_freeze_frame(220_000, &truncated)); }, ); }