diff --git a/internal/audio/peek_test.go b/internal/audio/peek_test.go new file mode 100644 index 0000000..370c138 --- /dev/null +++ b/internal/audio/peek_test.go @@ -0,0 +1,56 @@ +package audio + +import ( + "testing" + "time" +) + +// Playing a stopped take must not consume it. +// +// Reported on the air: the recording plays once, then nothing — "as if it threw +// the sound away after playing it". PeekQSO is what the playback reads, and it +// is meant to COPY; TakeQSO is the one that clears. +func TestPeekDoesNotConsumeTheTake(t *testing.T) { + r := &Recorder{running: true, active: true, paused: true, prerollSamples: sampleRate} + r.acc = make([]int16, 4000) + for i := range r.acc { + r.acc[i] = int16(i % 500) + } + + first, err := r.PeekQSO() + if err != nil { + t.Fatalf("first peek: %v", err) + } + second, err := r.PeekQSO() + if err != nil { + t.Fatalf("second peek: %v — the take was consumed by the first", err) + } + if len(first) != len(second) { + t.Errorf("second peek returned %d bytes, first %d", len(second), len(first)) + } + + // And the copy must be independent: the caller holds these bytes while the + // recorder may still be appending to its own slice. + if len(first) > 0 { + first[0] = ^first[0] + again, _ := r.PeekQSO() + if len(again) > 0 && again[0] == first[0] { + t.Error("PeekQSO handed out its internal buffer, not a copy") + } + } + + // Resuming and stopping again keeps everything captured so far. + r.ResumeQSO() + r.mu.Lock() + r.acc = append(r.acc, make([]int16, 1000)...) + r.mu.Unlock() + r.PauseQSO() + third, err := r.PeekQSO() + if err != nil { + t.Fatalf("peek after resume: %v", err) + } + if len(third) <= len(second) { + t.Errorf("after resuming, the take is %d bytes — it should have grown past %d", len(third), len(second)) + } + _ = time.Now +}