use super::*; use serial_test::serial; fn test_args(width: usize, height: usize, fps: u32) -> Args { Args { server: DEFAULT_SERVER.to_string(), width, height, fps, duration: Duration::from_secs(1), jpeg_quality: DEFAULT_JPEG_QUALITY, session_id: 1, artifact_dir: None, print_every: 0, max_frame_bytes: 232_106, tls_ca: None, tls_client_cert: None, tls_client_key: None, tls_domain: None, } } #[test] fn args_frame_timing_rounds_up_short_runs() { let mut args = test_args(640, 360, 30); args.duration = Duration::from_millis(1_050); assert_eq!(args.frame_step_us(), 33_333); assert_eq!(args.total_frames(), 32); args.duration = Duration::ZERO; assert_eq!(args.total_frames(), 1); } #[test] fn encode_stats_track_bounds_mean_and_oversize_frames() { let mut stats = EncodeStats::default(); assert_eq!(stats.mean_bytes(), 0); stats.record(100, 150); stats.record_sent(); stats.record(200, 150); stats.record_sent(); assert_eq!(stats.frames, 2); assert_eq!(stats.sent_frames, 2); assert_eq!(stats.min_bytes, 100); assert_eq!(stats.max_bytes, 200); assert_eq!(stats.mean_bytes(), 150); assert_eq!(stats.oversize_frames, 1); } #[test] fn synthetic_bundle_preserves_video_audio_and_session_metadata() { let args = test_args(320, 180, 60); let bundle = synthetic_bundle(&args, 7, 116_667, vec![1, 2, 3, 4]); let video = bundle.video.as_ref().expect("video packet"); let audio = bundle.audio.first().expect("audio packet"); assert_eq!(bundle.session_id, 1); assert_eq!(bundle.seq, 7); assert_eq!(bundle.capture_start_us, 116_667); assert_eq!(bundle.video_width, 320); assert_eq!(bundle.video_height, 180); assert_eq!(bundle.video_fps, 60); assert_eq!(video.seq, 7); assert_eq!(video.pts, 116_667); assert_eq!(video.data, vec![1, 2, 3, 4]); assert_eq!(audio.seq, 7); assert_eq!(audio.sample_rate, DEFAULT_SAMPLE_RATE); assert_eq!(audio.channels, DEFAULT_CHANNELS); assert_eq!(audio.frame_duration_us, args.frame_step_us() as u32); assert_eq!(audio.data.len(), silence_pcm(args.frame_step_us()).len()); } #[test] fn synthetic_frame_helpers_handle_markers_bounds_and_silence() { assert_eq!(marker_cell(320, 240), 6); assert_eq!(marker_cell(3840, 2160), 16); assert_eq!(silence_pcm(0).len(), 4); assert_eq!(silence_pcm(20_000).len(), 3_840); let tiny = synthetic_rgb_frame(3, 2, 42); assert_eq!(tiny.len(), 18); let mut frame = vec![0_u8; 6 * 4 * 3]; fill_rect(&mut frame, 6, 4, 2, 8, 8, 77); assert_eq!(&frame[(2 * 6 + 4) * 3..(2 * 6 + 5) * 3], &[77, 77, 77]); assert_eq!(&frame[(3 * 6 + 5) * 3..(3 * 6 + 6) * 3], &[77, 77, 77]); } #[test] fn parse_mode_and_duration_helpers_cover_valid_and_invalid_inputs() { assert_eq!(parse_mode("1920x1080@30").unwrap(), (1920, 1080, 30)); assert!(parse_mode("1920x1080").is_err()); assert!(parse_mode("1920@30").is_err()); assert_eq!( duration_mul(Duration::from_millis(33), 3), Duration::from_millis(99) ); assert_eq!( duration_mul(Duration::from_secs(u64::MAX), 2), Duration::from_nanos(u64::MAX) ); } #[test] fn uri_and_tls_helpers_identify_https_and_host_names() { assert!(is_https(" https://example.test:8443/path")); assert!(!is_https("http://example.test")); assert_eq!( host_from_uri("https://user@example.test:8443/path").as_deref(), Some("example.test") ); assert_eq!( host_from_uri("https://[fd00::1]:50051").as_deref(), Some("fd00::1") ); assert_eq!(host_from_uri("not-a-uri"), None); let mut args = test_args(320, 180, 30); args.server = "https://relay.example.test:50051".to_string(); args.tls_ca = None; let err = client_tls_config(&args).expect_err("missing CA should fail"); assert!(err.to_string().contains("--tls-ca")); } #[test] #[serial] fn env_path_and_default_pki_path_ignore_empty_values() { temp_env::with_vars( [ ("LESAVKA_SYNTHETIC_TEST_PATH", Some("/tmp/lesavka-test")), ("HOME", Some("/home/tester")), ], || { assert_eq!( env_path("LESAVKA_SYNTHETIC_TEST_PATH").as_deref(), Some(PathBuf::from("/tmp/lesavka-test").as_path()) ); assert_eq!( default_pki_path("ca.crt").as_deref(), Some(PathBuf::from("/home/tester/.config/lesavka/pki/ca.crt").as_path()) ); }, ); temp_env::with_var("LESAVKA_SYNTHETIC_TEST_PATH", Some(""), || { assert_eq!(env_path("LESAVKA_SYNTHETIC_TEST_PATH"), None); }); } #[test] #[serial] fn env_usize_and_summary_json_handle_empty_stats() { temp_env::with_var("LESAVKA_SYNTHETIC_MAX_FRAME_BYTES", Some("2048"), || { assert_eq!(env_usize("LESAVKA_SYNTHETIC_MAX_FRAME_BYTES"), Some(2048)); }); temp_env::with_var("LESAVKA_SYNTHETIC_MAX_FRAME_BYTES", Some("nope"), || { assert_eq!(env_usize("LESAVKA_SYNTHETIC_MAX_FRAME_BYTES"), None); }); let mut args = test_args(320, 180, 30); args.server = "https://relay.example.test:50051".to_string(); args.duration = Duration::from_millis(1500); let summary = args_summary_json(&args, None, Some("complete")); assert!(summary.contains("\"tls\":true")); assert!(summary.contains("\"encoded_frames\":0")); assert!(summary.contains("\"encoded_min_bytes\":null")); assert!(summary.contains("\"exit_reason\":\"complete\"")); } #[test] fn synthetic_frames_fit_safe_720p_and_1080p_isochronous_budget() { for (width, height, fps) in [(1280, 720, 30), (1920, 1080, 30)] { let args = test_args(width, height, fps); let mut encoder = MjpegEncoder::new(&args).expect("synthetic encoder"); for sequence in [0, 1, 30, 120, 300] { let encoded = encoder.encode(sequence).expect("encode frame"); assert!( encoded.len() <= args.max_frame_bytes, "{}x{}@{} synthetic frame {sequence} encoded to {} bytes, above {}", width, height, fps, encoded.len(), args.max_frame_bytes ); } } }