96 lines
3.6 KiB
Go
96 lines
3.6 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
||
// Authors: Storm Dragon, Angus Kola
|
||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
||
package app
|
||
|
||
import (
|
||
"context"
|
||
"log/slog"
|
||
"reflect"
|
||
"testing"
|
||
|
||
"git.stormux.org/storm/cthulhu/internal/config"
|
||
"git.stormux.org/storm/cthulhu/internal/review"
|
||
)
|
||
|
||
func TestReadingSpeechText(t *testing.T) {
|
||
for _, test := range []struct {
|
||
name string
|
||
settings config.ReadingSettings
|
||
input string
|
||
want string
|
||
}{
|
||
{"unchanged", config.ReadingSettings{}, "saveFile 123", "saveFile 123"},
|
||
{"multicase", config.ReadingSettings{SpeakMultiCaseStringsAsWords: true}, "saveFile and HTTPServer", "save File and HTTPServer"},
|
||
{"digits", config.ReadingSettings{SpeakNumbersAsDigits: true}, "Room 123, A12, ٤٥", "Room 1 2 3, A12, ٤ ٥"},
|
||
{"both", config.ReadingSettings{SpeakNumbersAsDigits: true, SpeakMultiCaseStringsAsWords: true}, "orderNumber 42", "order Number 4 2"},
|
||
} {
|
||
t.Run(test.name, func(t *testing.T) {
|
||
if got := readingSpeechText(test.input, test.settings); got != test.want {
|
||
t.Fatalf("speech text = %q, want %q", got, test.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestPronunciationPreservesCharacterReadingAndSpelling(t *testing.T) {
|
||
entries := []config.PronunciationEntry{{Word: "a", Replacement: "article"}, {Word: "space", Replacement: "blank"}}
|
||
for _, test := range []struct {
|
||
name string
|
||
request speechRequest
|
||
want []string
|
||
}{
|
||
{"character", speechRequest{Text: "A", Character: true}, []string{"A"}},
|
||
{"echo", speechRequest{Text: "a", Character: true, CustomEcho: true}, []string{"a"}},
|
||
{"spelling", func() speechRequest {
|
||
request := speechRequest{Text: "Aa"}
|
||
set_review_spelling(&request, "Aa", review.FlatSpell)
|
||
return request
|
||
}(), []string{"A", "a"}},
|
||
{"word", speechRequest{Text: "a"}, []string{"article"}},
|
||
{"character name", speechRequest{Text: "space", Character: true}, []string{"blank"}},
|
||
} {
|
||
t.Run(test.name, func(t *testing.T) {
|
||
speaker := &spellingTestSpeaker{}
|
||
worker := newSpeechWorker(slog.New(slog.DiscardHandler), speaker)
|
||
worker.textTransform = func(text string) string { return pronunciationSpeechText(text, entries) }
|
||
if err := worker.deliver(context.Background(), test.request); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if !reflect.DeepEqual(speaker.parts, test.want) {
|
||
t.Fatalf("delivered %q, want %q", speaker.parts, test.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestSpeechHistoryReplayDoesNotApplyPronunciationTwice(t *testing.T) {
|
||
speaker := &spellingTestSpeaker{}
|
||
worker := newSpeechWorker(slog.New(slog.DiscardHandler), speaker)
|
||
worker.history = newSpeechHistory(10)
|
||
worker.textTransform = func(text string) string {
|
||
return pronunciationSpeechText(text, []config.PronunciationEntry{
|
||
{Word: "SQL", Replacement: "sequel"}, {Word: "sequel", Replacement: "next"},
|
||
})
|
||
}
|
||
if err := worker.deliver(context.Background(), speechRequest{Text: "SQL"}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
recorded := worker.history.snapshot()[0]
|
||
if err := worker.deliver(context.Background(), speechRequest{Text: recorded, Role: "table cell", ExcludeFromHistory: true}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if !reflect.DeepEqual(speaker.parts, []string{"sequel", "sequel"}) {
|
||
t.Fatalf("history replay changed recorded speech: %q", speaker.parts)
|
||
}
|
||
}
|
||
|
||
func TestPronunciationSpeechTextReplacesWholeWordsOnce(t *testing.T) {
|
||
entries := []config.PronunciationEntry{{Word: "SQL", Replacement: "sequel"}, {Word: "sequel", Replacement: "next"}, {Word: "café", Replacement: "coffee"}}
|
||
input := "SQL, sql; SQLite café!"
|
||
if got := pronunciationSpeechText(input, entries); got != "sequel, sequel; SQLite coffee!" {
|
||
t.Fatalf("pronunciation = %q", got)
|
||
}
|
||
}
|