commit 87ea90df507740bf93d4cf2840953b0b38977098
parent 9ffd7724e3c2f8bec605d5b958910a57f6bb930d
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Thu, 17 Sep 2026 11:58:19 -0400
test: play a silent file twice and check each call runs full length
Play was untested, so neither a second call failing on the process-wide
audio context nor the tail being cut off when the source hit EOF could
be caught. A generated second of silence keeps the test quiet; timing
bounds catch an early return, and the second iteration catches a context
that cannot be reused. Skips when no audio output is available.
Diffstat:
1 file changed, 48 insertions(+), 0 deletions(-)
diff --git a/internal/test/play_test.go b/internal/test/play_test.go
@@ -0,0 +1,48 @@
+package test
+
+import (
+ "os/exec"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "git.sr.ht/~jackmordaunt/nativeaudio"
+)
+
+// TestPlayTwice plays one second of silence twice in the same process.
+//
+// Each call must take at least the file's duration: returning early means
+// the tail of the audio was cut off. The second call must succeed at all:
+// the playback context is process-wide and used to be re-created per call,
+// which the audio backend refuses.
+//
+// The test skips when no audio output is available.
+func TestPlayTwice(t *testing.T) {
+ if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
+ // Other platforms play through ffplay; that path has its own test.
+ if _, err := exec.LookPath("ffplay"); err != nil {
+ t.Skip("ffplay not installed")
+ }
+ }
+ const seconds = 1
+ path := writeTemp(t, "silence.wav", silentWAV(44100, 2, seconds))
+ for i := 1; i <= 2; i++ {
+ start := time.Now()
+ err := nativeaudio.Play(path)
+ elapsed := time.Since(start)
+ if err != nil {
+ if i == 1 && strings.Contains(err.Error(), "starting playback context") {
+ t.Skipf("no audio output available: %v", err)
+ }
+ t.Fatalf("play %d: %v", i, err)
+ }
+ t.Logf("play %d took %v", i, elapsed.Round(time.Millisecond))
+ if min := time.Duration(seconds) * time.Second * 9 / 10; elapsed < min {
+ t.Errorf("play %d returned after %v, want at least %v: audio tail was cut off", i, elapsed, min)
+ }
+ if max := 10 * time.Second; elapsed > max {
+ t.Errorf("play %d took %v, want under %v", i, elapsed, max)
+ }
+ }
+}