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.
This commit is contained in:
Jonathan Liebig
2026-09-14 01:27:45 +02:00
parent aa961df943
commit 679fe59059
+20 -5
View File
@@ -18,6 +18,10 @@ use tracing::warn;
const DISABLE_SOUND_ENV: &str = "HERDR_DISABLE_SOUND";
#[cfg(any(windows, test))]
const WINDOWS_SOUND_PATH_ENV: &str = "HERDR_SOUND_PATH";
/// Test-only override for the Windows player's playback timeout. Production
/// leaves this unset so the embedded script keeps its 15 second bound.
#[cfg(test)]
const WINDOWS_SOUND_TIMEOUT_ENV: &str = "HERDR_SOUND_TIMEOUT_SECONDS";
#[cfg(not(any(windows, target_os = "macos")))]
const AUDIO_PLAYER_TIMEOUT: Duration = Duration::from_secs(15);
#[cfg(not(any(windows, target_os = "macos")))]
@@ -134,13 +138,16 @@ fn windows_media_player_script() -> &'static str {
$ErrorActionPreference = 'Stop'
$Path = [Environment]::GetEnvironmentVariable('HERDR_SOUND_PATH', 'Process')
if ([string]::IsNullOrWhiteSpace($Path)) { throw 'HERDR_SOUND_PATH is not set' }
$timeoutSeconds = 15
$timeoutOverride = [Environment]::GetEnvironmentVariable('HERDR_SOUND_TIMEOUT_SECONDS', 'Process')
if (-not [string]::IsNullOrWhiteSpace($timeoutOverride)) { $timeoutSeconds = [int]$timeoutOverride }
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
$resolved = (Resolve-Path -LiteralPath $Path).ProviderPath
$script:player = [System.Windows.Media.MediaPlayer]::new()
$script:frame = [System.Windows.Threading.DispatcherFrame]::new()
$script:timer = [System.Windows.Threading.DispatcherTimer]::new()
$script:timer.Interval = [TimeSpan]::FromSeconds(15)
$script:timer.Interval = [TimeSpan]::FromSeconds($timeoutSeconds)
$script:failed = $null
$script:timedOut = $false
$script:player.add_MediaOpened({ $script:player.Play() })
@@ -443,23 +450,31 @@ mod tests {
assert!(script.contains("Dispatcher]::PushFrame"));
assert!(script.contains("add_MediaEnded"));
assert!(script.contains("add_MediaFailed"));
assert!(script.contains(WINDOWS_SOUND_TIMEOUT_ENV));
assert!(script.contains("FromSeconds($timeoutSeconds)"));
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 mut command = windows_player_command(&path);
// Whether the host media stack raises MediaFailed before the playback
// timeout is environment-dependent, so keep the bound short and accept
// either terminal error instead of waiting out the production timeout.
command.env(WINDOWS_SOUND_TIMEOUT_ENV, "2");
let output = command.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}"
);
}
}