From add3dd28f91db1a2b33e9582c60ac9c1809c1bf5 Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Mon, 14 Sep 2026 03:42:59 +0400 Subject: test(windows): deflake media player invalid media assertion (#4082) * test(windows): deflake media player invalid media assertion The Windows sound test required MediaPlayer's MediaFailed event for invalid media. Some runners never raise it, so the script exits through its playback timer and the test saw "sound playback timed out" instead, panicking on the MediaFailed assertion. Accept either terminal error and keep the timer short under test via HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the production 15 second bound. * test(windows): keep media player timeout override test-only Generate the short-timer player script only in tests instead of reading a process environment override. This keeps the production script's fixed 15 second bound unchanged, and the script-content test now proves the test variant rewrites the timer. --- src/sound.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/sound.rs b/src/sound.rs index d1b4fc90..b8870b8b 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -167,8 +167,18 @@ if ($script:timedOut) { throw 'sound playback timed out' } "# } +/// Builds the same player script with a shorter timer for tests. Production +/// keeps the fixed 15 second bound in `windows_media_player_script`. +#[cfg(test)] +fn windows_media_player_script_with_timeout(timeout_seconds: u64) -> String { + windows_media_player_script().replace( + "FromSeconds(15)", + &format!("FromSeconds({timeout_seconds})"), + ) +} + #[cfg(any(windows, test))] -fn windows_player_command(path: &Path) -> Command { +fn windows_player_command(path: &Path, script: &str) -> Command { let mut command = crate::noninteractive_process::command("powershell.exe"); command .args([ @@ -178,7 +188,7 @@ fn windows_player_command(path: &Path) -> Command { "-ExecutionPolicy", "Bypass", "-Command", - windows_media_player_script(), + script, ]) .env(WINDOWS_SOUND_PATH_ENV, path); command @@ -186,7 +196,7 @@ fn windows_player_command(path: &Path) -> Command { #[cfg(windows)] fn run_windows_player(path: &Path) -> Result { - windows_player_command(path) + windows_player_command(path, windows_media_player_script()) .output() .map_err(|e| format!("Windows MediaPlayer playback failed: {e}")) } @@ -430,7 +440,7 @@ mod tests { fn windows_media_player_uses_process_environment_and_dispatcher() { let script = windows_media_player_script(); let path = Path::new(r"C:\sound dir\döne.mp3"); - let command = windows_player_command(path); + let command = windows_player_command(path, script); let env_path = command.get_envs().find_map(|(key, value)| { (key == std::ffi::OsStr::new(WINDOWS_SOUND_PATH_ENV)) .then_some(value) @@ -443,23 +453,30 @@ mod tests { assert!(script.contains("Dispatcher]::PushFrame")); assert!(script.contains("add_MediaEnded")); assert!(script.contains("add_MediaFailed")); + assert!(script.contains("FromSeconds(15)")); + assert!(windows_media_player_script_with_timeout(2).contains("FromSeconds(2)")); assert_eq!(env_path, Some(path.as_os_str())); assert!(!command.get_args().any(|arg| arg == path.as_os_str())); } #[cfg(windows)] #[test] - fn windows_media_player_reports_invalid_media_without_waiting_for_timeout() { + fn windows_media_player_reports_invalid_media() { let _lock = crate::integration::integration_env_lock(); let path = temp_sound_path(); std::fs::write(&path, b"not an mp3").unwrap(); - let output = run_windows_player(&path).unwrap(); + let script = windows_media_player_script_with_timeout(2); + // Whether the host media stack raises MediaFailed before the playback + // timeout is environment-dependent, so use a short test-only timer and + // accept either terminal error instead of the production 15 second wait. + let output = windows_player_command(&path, &script).output().unwrap(); let _ = std::fs::remove_file(path); assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); assert!( - String::from_utf8_lossy(&output.stderr).contains("sound media failed"), - "stderr should identify a MediaFailed error" + stderr.contains("sound media failed") || stderr.contains("sound playback timed out"), + "stderr should report why playback stopped: {stderr}" ); } } -- cgit v1.2.3