//! End-to-end server coverage for shared upstream media stream helpers. //! //! Scope: run local helper-level and output-delay probe checks around the public //! upstream media RPC implementation. //! Targets: `server/src/main.rs`, `server/src/output_delay_probe.rs`. //! Why: the coverage harness should keep freshness decisions and probe plumbing //! stable without physical UVC, HDMI, or ALSA hardware in CI. #[cfg(coverage)] #[allow(warnings)] mod server_upstream_media { include!(env!("LESAVKA_SERVER_MAIN_SRC")); include!("../support/server_upstream_media_harness.rs"); use serial_test::serial; use temp_env::with_var; #[test] fn coverage_relay_freshness_helpers_keep_live_media_bounded() { temp_env::with_var("LESAVKA_UPSTREAM_STALE_DROP_MS", Some("42"), || { assert_eq!( upstream_stale_drop_budget(), std::time::Duration::from_millis(42) ); }); let mut video = std::collections::VecDeque::from([ VideoPacket { pts: 1, ..Default::default() }, VideoPacket { pts: 2, ..Default::default() }, VideoPacket { pts: 3, ..Default::default() }, ]); assert_eq!(retain_freshest_video_packet(&mut video), 2); assert_eq!(video.len(), 1); assert_eq!(video[0].pts, 3); let mut audio = (0..12) .map(|pts| AudioPacket { pts, ..Default::default() }) .collect::>(); assert_eq!(retain_freshest_audio_packet(&mut audio), 4); assert_eq!(audio.front().map(|packet| packet.pts), Some(4)); assert_eq!(audio.len(), AUDIO_PENDING_LIVE_WINDOW_PACKETS); let plan = lesavka_server::upstream_media_runtime::PlannedUpstreamPacket { local_pts_us: 7, due_at: tokio::time::Instant::now(), late_by: std::time::Duration::ZERO, source_lag: std::time::Duration::ZERO, }; assert_eq!( coverage_playable_plan( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(plan) ) .map(|plan| plan.local_pts_us), Some(7) ); assert!( coverage_playable_plan( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::AwaitingPair ) .is_none() ); assert!( coverage_playable_plan( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::DropBeforeOverlap ) .is_none() ); assert!( coverage_playable_plan( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::DropStale("stale") ) .is_none() ); assert!( coverage_playable_plan( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::StartupFailed("cold") ) .is_none() ); let mut requeued_audio = std::collections::VecDeque::new(); assert!(coverage_requeue_audio_packet( &mut requeued_audio, AudioPacket { pts: 11, ..Default::default() }, false, )); assert_eq!(requeued_audio.front().map(|packet| packet.pts), Some(11)); assert!(!coverage_requeue_audio_packet( &mut requeued_audio, AudioPacket { pts: 12, ..Default::default() }, true, )); assert_eq!(requeued_audio.len(), 1); let mut requeued_video = std::collections::VecDeque::new(); assert!(coverage_requeue_video_packet( &mut requeued_video, VideoPacket { pts: 21, ..Default::default() }, false, )); assert_eq!(requeued_video.front().map(|packet| packet.pts), Some(21)); assert!(!coverage_requeue_video_packet( &mut requeued_video, VideoPacket { pts: 22, ..Default::default() }, true, )); requeued_video.push_back(VideoPacket { pts: 23, ..Default::default() }); assert_eq!(coverage_drop_late_video_packet(&mut requeued_video), 1); assert_eq!(requeued_video.front().map(|packet| packet.pts), Some(23)); let wait_runtime = UpstreamMediaRuntime::new(); coverage_record_video_wait_failure(&wait_runtime); assert_eq!(wait_runtime.snapshot().video_freezes, 1); let fresh_plan = lesavka_server::upstream_media_runtime::PlannedUpstreamPacket { local_pts_us: 31, due_at: tokio::time::Instant::now(), late_by: std::time::Duration::from_millis(1), source_lag: std::time::Duration::ZERO, }; let mut pending_audio = std::collections::VecDeque::new(); assert_eq!( coverage_audio_plan_from_decision( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(fresh_plan), &mut pending_audio, AudioPacket { pts: 31, ..Default::default() }, false, std::time::Duration::from_millis(2), ) .map(|plan| plan.local_pts_us), Some(31) ); assert!( coverage_audio_plan_from_decision( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::AwaitingPair, &mut pending_audio, AudioPacket { pts: 32, ..Default::default() }, false, std::time::Duration::from_millis(2), ) .is_none() ); assert_eq!(pending_audio.front().map(|packet| packet.pts), Some(32)); assert!( coverage_audio_plan_from_decision( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play( lesavka_server::upstream_media_runtime::PlannedUpstreamPacket { local_pts_us: 33, due_at: tokio::time::Instant::now(), late_by: std::time::Duration::from_millis(5), source_lag: std::time::Duration::ZERO, }, ), &mut pending_audio, AudioPacket { pts: 33, ..Default::default() }, false, std::time::Duration::from_millis(2), ) .is_none() ); let mut pending_video = std::collections::VecDeque::from([ VideoPacket { pts: 40, ..Default::default() }, VideoPacket { pts: 41, ..Default::default() }, ]); assert!( coverage_video_plan_from_decision( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::AwaitingPair, &mut pending_video, VideoPacket { pts: 42, ..Default::default() }, false, std::time::Duration::from_millis(2), ) .is_none() ); assert_eq!(pending_video.front().map(|packet| packet.pts), Some(42)); assert!( coverage_video_plan_from_decision( lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play( lesavka_server::upstream_media_runtime::PlannedUpstreamPacket { local_pts_us: 43, due_at: tokio::time::Instant::now(), late_by: std::time::Duration::from_millis(5), source_lag: std::time::Duration::ZERO, }, ), &mut pending_video, VideoPacket { pts: 43, ..Default::default() }, false, std::time::Duration::from_millis(2), ) .is_none() ); assert_eq!(pending_video.len(), 1); let master_runtime = UpstreamMediaRuntime::new(); assert!(coverage_audio_master_ready(&master_runtime, true)); assert!(!coverage_audio_master_ready(&master_runtime, false)); assert_eq!(master_runtime.snapshot().video_freezes, 1); } #[test] #[serial] fn run_output_delay_probe_streams_coverage_summary() { let rt = tokio::runtime::Runtime::new().expect("runtime"); with_var("LESAVKA_CAPTURE_POWER_UNIT", Some("none"), || { with_var("LESAVKA_DISABLE_UVC", None::<&str>, || { rt.block_on(async { let (_dir, handler) = build_handler_for_tests(); let (server, mut cli) = serve_handler(handler).await; let mut response = cli .run_output_delay_probe(tonic::Request::new(OutputDelayProbeRequest { duration_seconds: 2, warmup_seconds: 0, pulse_period_ms: 500, pulse_width_ms: 100, event_width_codes: "1,2,3".to_string(), audio_delay_us: 0, video_delay_us: 0, })) .await .expect("output delay probe should open") .into_inner(); let reply = tokio::time::timeout(std::time::Duration::from_secs(1), response.message()) .await .expect("output delay probe timeout") .expect("output delay probe grpc") .expect("output delay probe item"); assert!(reply.ok); assert!(reply.detail.contains("video_frames=1")); assert!(reply.server_timeline_json.contains("lesavka.output-delay")); server.abort(); }); }); }); } }