diff --git a/client.go b/client.go index 429fec9..cc8078f 100644 --- a/client.go +++ b/client.go @@ -56,12 +56,8 @@ func (b *Barnard) connect(reconnect bool) bool { // --- Tone test mode: skip all OpenAL; generate 440 Hz tone // --- and save incoming audio to a file. - // Start the tone generator goroutine. - b.toneTestStop = make(chan struct{}) - go StartToneGenerator(b.Client, b.toneTestStop) - b.Tx = true - - // Attach the audio file saver. + // Open the output first. Starting transmission before this succeeds + // leaves an orphaned tone goroutine when the path is unusable. saver, err := NewAudioFileSaver(b.ToneTestOutput) if err != nil { b.exitWithError(err) @@ -70,6 +66,10 @@ func (b *Barnard) connect(reconnect bool) bool { b.toneTestSaver = saver b.Client.Config.AttachAudio(saver) + b.toneTestStop = make(chan struct{}) + go StartToneGenerator(b.Client, b.toneTestStop) + b.Tx = true + b.Connected = true return true } diff --git a/fix.txt b/fix.txt index 30b676f..2a8cde9 100644 --- a/fix.txt +++ b/fix.txt @@ -115,7 +115,7 @@ Priority 1: transport, lifecycle, and correctness closure: signal stop, wait for run to finish/close stdin and Wait ffmpeg, then return its result. Do not close stdin concurrently from Stop. -14. Tone-test startup leaks transmission on output-file error +[x] 14. Tone-test startup leaks transmission on output-file error File: client.go connect starts StartToneGenerator and sets Tx before NewAudioFileSaver. If output file creation fails, the tone goroutine continues. Create the saver diff --git a/tonetest_test.go b/tonetest_test.go new file mode 100644 index 0000000..bf42ff2 --- /dev/null +++ b/tonetest_test.go @@ -0,0 +1,15 @@ +package main + +import ( + "path/filepath" + "testing" +) + +// Regression: tone transmission was started before opening its output file, +// so a creation error left audio transmission running without cleanup. +func TestNewAudioFileSaverReportsUnavailableOutputPath(t *testing.T) { + path := filepath.Join(t.TempDir(), "missing", "tone.pcm") + if saver, err := NewAudioFileSaver(path); err == nil || saver != nil { + t.Fatalf("got saver=%v err=%v", saver, err) + } +}