Files
fenrir/internal/app/audio_recovery_test.go

80 lines
2.3 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"errors"
"io"
"sync"
"syscall"
"testing"
"time"
"git.stormux.org/storm/fenrir/internal/command"
"git.stormux.org/storm/fenrir/internal/input"
"git.stormux.org/storm/fenrir/internal/screen"
"git.stormux.org/storm/fenrir/internal/speech"
)
type unavailableSpeech struct {
entered chan struct{}
once sync.Once
fatal bool
}
func (f *unavailableSpeech) Say(string) error { return io.EOF }
func (f *unavailableSpeech) Configure(speech.Parameters) error { return nil }
func (f *unavailableSpeech) Set_pitch(int) error { return nil }
func (f *unavailableSpeech) Cancel() error { return nil }
func (f *unavailableSpeech) Close() {}
func (f *unavailableSpeech) Reopen(ctx context.Context) (speech.Transport, error) {
f.once.Do(func() { close(f.entered) })
if f.fatal {
return nil, syscall.EACCES
}
<-ctx.Done()
return nil, ctx.Err()
}
func TestAudioRecoveryKeepsKeyboardCommandsAndReleasesOnFailure(t *testing.T) {
for _, fatal := range []bool{false, true} {
broken := &unavailableSpeech{entered: make(chan struct{}), fatal: fatal}
voice := speech.New(broken)
keys := &fakeKeys{events: make(chan error, 1)}
polls := 0
keys.poll = func(_ bool, handle func(input.Decision)) error {
polls++
if polls > 1 {
handle(input.Decision{Forward: true, Interrupt: true, Keypress: true})
handle(input.Decision{Command: command.Quit})
}
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
result := make(chan error, 1)
go func() {
result <- run(ctx, &fakeSource{snapshot: screen.Snapshot{Terminal: 1, Active: true, Rows: 1, Cols: 5, Cells: []rune("ready")}}, keys, voice, nil)
}()
select {
case <-broken.entered:
if !fatal {
keys.events <- nil
}
case <-ctx.Done():
t.Fatal("fixture did not begin speech recovery")
}
select {
case err := <-result:
if fatal && !errors.Is(err, syscall.EACCES) || !fatal && (err != nil || polls < 2) || !keys.closed {
t.Fatal("audio failure trapped input", err, polls, keys.closed)
}
case <-ctx.Done():
t.Fatal("reader could not quit or release keyboards during audio recovery")
}
cancel()
voice.Close()
}
}