98 lines
3.4 KiB
Go
98 lines
3.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
"git.stormux.org/storm/cthulhu/internal/review"
|
|
)
|
|
|
|
func TestFlatReviewSpellingPresentation(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
command input.Command
|
|
text string
|
|
parts []string
|
|
character bool
|
|
}{
|
|
{input.ReviewCurrentWord, "book", nil, false},
|
|
{input.ReviewCurrentCharacter, "b", nil, true},
|
|
{input.ReviewPhoneticCharacter, "B", []string{"bravo"}, false},
|
|
{input.ReviewSpellWord, "Book", []string{"B", "o", "o", "k"}, true},
|
|
{input.ReviewPhoneticWord, "Book", []string{"bravo", "oscar", "oscar", "kilo"}, false},
|
|
{input.ReviewSpellWord, "猫1!", []string{"猫", "1", "!"}, true},
|
|
{input.ReviewPhoneticWord, "猫1!", []string{"猫", "1", "!"}, false},
|
|
{input.ReviewPhoneticCharacter, " ", nil, true},
|
|
{input.ReviewPhoneticWord, "", nil, false},
|
|
} {
|
|
queue := &fakeSpeechQueue{requests: make(chan speechRequest, 1), spoken: make(chan string, 1)}
|
|
unit, step := flatReviewCommand(tc.command)
|
|
result := review.Result{FlatUnit: unit, FlatStep: step, FlatSpeech: flat_review_speech(tc.command), FlatPosition: review.FlatPosition{Text: tc.text}}
|
|
presentFlatReviewResult(slog.New(slog.DiscardHandler), queue, result)
|
|
request := <-queue.requests
|
|
if !reflect.DeepEqual(request.Spelling, tc.parts) || request.Character != tc.character {
|
|
t.Fatalf("%s %q request=%#v", tc.command, tc.text, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
type spellingTestSpeaker struct {
|
|
parts []string
|
|
characters []string
|
|
after func()
|
|
}
|
|
|
|
func (*spellingTestSpeaker) Connect(context.Context) error { return nil }
|
|
func (*spellingTestSpeaker) Close(context.Context) error { return nil }
|
|
func (*spellingTestSpeaker) Cancel(context.Context) error { return nil }
|
|
func (speaker *spellingTestSpeaker) Speak(_ context.Context, text string) error {
|
|
speaker.parts = append(speaker.parts, text)
|
|
if speaker.after != nil {
|
|
speaker.after()
|
|
}
|
|
return nil
|
|
}
|
|
func (speaker *spellingTestSpeaker) SpeakCharacter(ctx context.Context, text string) error {
|
|
speaker.characters = append(speaker.characters, text)
|
|
return speaker.Speak(ctx, text)
|
|
}
|
|
|
|
func TestReviewSpellingDeliveryAndInterruption(t *testing.T) {
|
|
for _, style := range []review.FlatSpeech{review.FlatSpell, review.FlatPhonetic} {
|
|
speaker := &spellingTestSpeaker{}
|
|
worker := newSpeechWorker(slog.New(slog.DiscardHandler), speaker)
|
|
request := speechRequest{Text: "book", Cause: "flat-review"}
|
|
set_review_spelling(&request, strings.Repeat("book", 20), style)
|
|
if err := worker.deliver(context.Background(), request); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(speaker.parts) != 80 {
|
|
t.Fatalf("long spelling lost letters: %d", len(speaker.parts))
|
|
}
|
|
wantCharacters := 0
|
|
if style == review.FlatSpell {
|
|
wantCharacters = 80
|
|
}
|
|
if len(speaker.characters) != wantCharacters {
|
|
t.Fatalf("character-mode delivery=%d", len(speaker.characters))
|
|
}
|
|
worker.running = true // Exercise the real interruption generation barrier.
|
|
speaker.parts = nil
|
|
speaker.after = worker.InterruptForUser
|
|
if err := worker.deliver(context.Background(), request); !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("interrupt=%v", err)
|
|
}
|
|
if len(speaker.parts) != 1 {
|
|
t.Fatalf("spelling continued after input: %v", speaker.parts)
|
|
}
|
|
}
|
|
}
|