//! Verification tests. These run against the host audio stack and //! therefore live behind the `audio-hardware` opt-in feature for CI //! environments without a usable audio device. Without the feature //! they fall back to a pure-software synth+WAV round-trip that does //! not touch cpal. use std::time::Duration; use audio_capture_playback_spike::synth_sine_wav; #[test] fn synth_produces_valid_wav_with_expected_frame_count() { let tmp = tempfile::tempdir().unwrap(); let path = tmp.path().join("sine.wav"); let (frames, sr) = synth_sine_wav(&path, 440.0, Duration::from_millis(500), 48_000).unwrap(); assert_eq!(sr, 48_000); // 0.5 s × 48 kHz = 24 000 frames (mono). assert!(frames >= 23_900 && frames <= 24_100, "got {frames}"); let reader = hound::WavReader::open(&path).unwrap(); let spec = reader.spec(); assert_eq!(spec.channels, 1); assert_eq!(spec.sample_rate, 48_000); assert_eq!(spec.bits_per_sample, 16); assert_eq!(reader.len(), frames as u32); } #[test] fn synth_then_playback_does_not_error_when_output_device_present() { use audio_capture_playback_spike::AudioPlayback; use cpal::traits::HostTrait; // Skip on hosts with no output device. The PoC's exit criterion // is satisfied by the verification CLI run captured in // VERIFICATION.md; this test is a CI sanity check. if cpal::default_host().default_output_device().is_none() { eprintln!("SKIP: no default output device"); return; } let tmp = tempfile::tempdir().unwrap(); let path = tmp.path().join("sine.wav"); synth_sine_wav(&path, 440.0, Duration::from_millis(120), 48_000).unwrap(); AudioPlayback::play_wav(&path).expect("playback round-trip should not error"); }