Files
cthulhu/internal/app/interruption_test.go

90 lines
3.5 KiB
Go

package app
import (
"context"
"log/slog"
"testing"
"git.stormux.org/storm/cthulhu/internal/config"
"git.stormux.org/storm/cthulhu/internal/input"
)
func TestProcessInputEventsUsesLiveInterruptionSettings(t *testing.T) {
live := &liveSettings{}
settings := config.DefaultSettings()
settings.EnableKeyEcho = true
settings.EnableActionKeys = true
for _, mode := range []string{input.InterruptAllKeys, input.InterruptCommonKeys, input.InterruptCustomKeys} {
settings.SpeechInterruption = input.InterruptionSettings{Mode: mode, Keys: []string{"Tab"}}
live.Store(settings)
speech := &fakeSpeechQueue{spoken: make(chan string, 10), interrupt: make(chan struct{}, 10)}
listener := &fakeInputListener{events: make(chan input.Event, 8)}
for _, name := range []string{"a", "Tab", "Control_L", "Return"} {
listener.events <- input.NewKeyEvent(42, true, name)
listener.events <- input.NewKeyEvent(42, false, name)
}
close(listener.events)
processInputEvents(context.Background(), slog.New(slog.DiscardHandler), speech, listener, input.EchoSettings{}, live, nil)
want := map[string]int{input.InterruptAllKeys: 4, input.InterruptCommonKeys: 2, input.InterruptCustomKeys: 1}[mode]
if len(speech.interrupt) != want {
t.Fatalf("mode %s interrupted %d times, want %d", mode, len(speech.interrupt), want)
}
// Selecting interruption keys must not suppress ordinary key echo.
if len(speech.spoken) < 2 {
t.Fatalf("mode %s suppressed Tab/Enter echo", mode)
}
}
}
func TestAppliedInterruptionKeysPreserveQueuedSpeechAndSayAll(t *testing.T) {
logger := slog.New(slog.DiscardHandler)
speech := newSpeechWorker(logger, nil)
speech.running = true
live := &liveSettings{}
settings := config.DefaultSettings()
settings.EnableKeyEcho = false
live.Store(settings)
preferences := &preferencesWorker{settings: settings, live: live, speechWorker: speech}
settings.SpeechInterruption.Mode = input.InterruptCommonKeys
if err := preferences.applyRuntime(context.Background(), settings); err != nil {
t.Fatal(err)
}
deliver := func(name string) {
listener := &fakeInputListener{events: make(chan input.Event, 1)}
listener.events <- input.NewKeyEvent(42, true, name)
close(listener.events)
processInputEvents(context.Background(), logger, speech, listener, input.EchoSettings{}, live, nil)
}
readContext := speech.BeginReadAll(context.Background(), 0)
speech.Enqueue(speechRequest{Text: "First announcement"})
speech.Enqueue(speechRequest{Text: "Second announcement"})
generation := speech.generation
deliver("Up")
deliver("a")
if readContext.Err() != nil || len(speech.pending) != 2 || speech.generation != generation {
t.Fatal("unselected keys cancelled Say All or dropped queued speech")
}
deliver("Control_R")
if readContext.Err() != context.Canceled || len(speech.pending) != 0 {
t.Fatal("common interruption key did not cancel Say All and pending output")
}
settings.SpeechInterruption = input.InterruptionSettings{Mode: input.InterruptCustomKeys, Keys: []string{"F8"}}
if err := preferences.applyRuntime(context.Background(), settings); err != nil {
t.Fatal(err)
}
speech.Enqueue(speechRequest{Text: "Custom mode announcement"})
deliver("Control_R")
if len(speech.pending) != 1 {
t.Fatal("previous interruption mode remained active after Apply")
}
deliver("F8")
if len(speech.pending) != 0 {
t.Fatal("custom interruption key did not cancel queued output")
}
speech.Enqueue(speechRequest{Text: "Old output"})
speech.ProtectCommand(100)
if len(speech.pending) != 0 {
t.Fatal("reader command failed to cancel old output in custom mode")
}
}