// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "log/slog" "testing" "git.stormux.org/storm/cthulhu/internal/input" ) func TestAutomaticTitleSurvivesMovementRepeatsBeforeAndAfterDelivery(t *testing.T) { speaker := &fakeSpeaker{spoken: make(chan string, 1)} worker := newSpeechWorker(slog.New(slog.DiscardHandler), speaker) worker.running = true worker.Enqueue(speechRequest{Text: "Game status", Cause: "window-title", Append: true}) repeat := input.Event{Name: "Up", Pressed: true, Repeat: true, Timestamp: 100} if worker.InterruptForInput(repeat) || len(worker.pending) != 1 { t.Fatal("repeat discarded queued title") } request, ok := worker.next() if !ok { t.Fatal("title missing from queue") } if err := worker.deliver(context.Background(), request); err != nil { t.Fatal(err) } if text := <-speaker.spoken; text != "Game status" { t.Fatalf("speech = %q", text) } if worker.InterruptForInput(repeat) || worker.generation != 0 { t.Fatal("repeat interrupted title after backend accepted it") } if !worker.InterruptForInput(input.Event{Name: "Up", Pressed: true, Timestamp: 200}) { t.Fatal("fresh movement key did not interrupt") } } func TestAutomaticTitleStillStopsForExplicitInput(t *testing.T) { for _, event := range []input.Event{ {Name: "Left", Pressed: true}, {Name: "Control_L", Pressed: true, Repeat: true}, {Name: "Control_R", Pressed: true, Repeat: true}, {Name: "Escape", Pressed: true, Repeat: true}, } { t.Run(event.Name, func(t *testing.T) { worker := newSpeechWorker(slog.New(slog.DiscardHandler), nil) worker.running = true worker.Enqueue(speechRequest{Text: "Game status", Cause: "window-title", Append: true}) if !worker.InterruptForInput(event) || len(worker.pending) != 0 || worker.generation != 1 { t.Fatal("explicit input did not stop title speech") } }) } } func TestTitleProtectionDoesNotChangeOrdinaryReading(t *testing.T) { for _, cause := range []string{"focus", "flat-review", "current-time", "key-echo", "read-all"} { t.Run(cause, func(t *testing.T) { worker := newSpeechWorker(slog.New(slog.DiscardHandler), nil) worker.running = true worker.Enqueue(speechRequest{Text: "Game status", Cause: "window-title", Append: true}) worker.Enqueue(speechRequest{Text: "Reading", Cause: cause}) if !worker.InterruptForInput(input.Event{Name: "Up", Pressed: true, Repeat: true}) || len(worker.pending) != 0 { t.Fatal("title protection leaked into ordinary reading") } }) } worker := newSpeechWorker(slog.New(slog.DiscardHandler), nil) worker.running = true ctx := worker.BeginReadAll(context.Background(), 100) worker.Enqueue(speechRequest{Text: "Game status", Cause: "window-title", Append: true}) if !worker.InterruptForInput(input.Event{Name: "Up", Pressed: true, Repeat: true, Timestamp: 101}) || ctx.Err() != context.Canceled { t.Fatal("appended title prevented held key from stopping Say All") } }