// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "strings" "testing" "git.stormux.org/storm/fenrir/internal/command" "git.stormux.org/storm/fenrir/internal/config" "git.stormux.org/storm/fenrir/internal/input" "git.stormux.org/storm/fenrir/internal/presentation" "git.stormux.org/storm/fenrir/internal/punctuation" "git.stormux.org/storm/fenrir/internal/screen" ) func TestSpellingPreservesRunesAndNamesWithoutDoubleProcessing(t *testing.T) { for _, profile := range []string{"default", "fr"} { processor, _, err := punctuation.Load([]string{"../../config/punctuation/" + profile + ".toml"}) if err != nil { t.Fatal(err) } for _, level := range punctuation.Levels() { for choices := range 8 { settings := config.Defaults() settings.Reading.RaisePitchForCapitals = choices&1 != 0 settings.Reading.BeepForCapitals = choices&2 != 0 settings.Reading.SayCapForCapitals = choices&4 != 0 voice := &capitalVoice{} beeps := 0 echo := &echoSpeaker{Speaker: voice, settings: &settings, punctuation: processor, punctuationLevel: level, play: func(presentation.Cue) bool { beeps++; return true }} const source = "A book!?É界🙂\t" echo.say_spelling(presentation.Spelling{Text: source, Phonetic: true}) prefix := "" if settings.Reading.SayCapForCapitals { prefix = "cap " } want := []string{prefix + "Alpha", processor.Character(' '), "bravo", "oscar", "oscar", "kilo", processor.Character('!'), processor.Character('?'), prefix + "É", "界", "🙂", processor.Character('\t')} if got := strings.Join(voice.messages, "|"); got != strings.Join(want, "|") || beeps != 0 || len(voice.pitches) != 0 { t.Fatal(profile, level, choices, got, voice.pitches, beeps) } voice.messages = nil echo.say_spelling(presentation.Spelling{Text: source}) want[0], want[2], want[3], want[4], want[5] = prefix+"A", "b", "o", "o", "k" if got := strings.Join(voice.messages, "|"); got != strings.Join(want, "|") { t.Fatal(profile, level, got) } wantPitch, wantBeeps := 0, 0 if settings.Reading.RaisePitchForCapitals { wantPitch = 2 } if settings.Reading.BeepForCapitals { wantBeeps = 2 } if len(voice.pitches) != wantPitch || beeps != wantBeeps || echo.last != source { t.Fatal("ordinary capital behavior or last echo changed", voice.pitches, beeps, echo.last) } } } } } func TestReviewSpellingThroughSharedLoop(t *testing.T) { processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"}) if err != nil { t.Fatal(err) } for _, muted := range []bool{false, true} { source := &fakeSource{snapshot: screen.Snapshot{Terminal: 2, Active: true, Rows: 1, Cols: 5, Cells: []rune("Book!"), X: 1}} voice := &fakeVoice{muted: muted} settings := config.Defaults() settings.Reading.Automatic = false settings.Reading.RaisePitchForCapitals = false settings.Presentation.Speech.Enabled = !muted settings.Presentation.Sound.Enabled = false keys := &fakeKeys{poll: func(_ bool, handle func(input.Decision)) error { voice.messages = nil for _, trial := range []struct { id command.ID want string }{ {command.SpellWord, "B|o|o|k|bang"}, {command.PhoneticWord, "Bravo|oscar|oscar|kilo|bang"}, {command.CopyLastEcho, "Book!"}, {command.CurrentClipboard, "Book!"}, {command.SpellLine, "B|o|o|k|bang"}, {command.PhoneticLine, "Bravo|oscar|oscar|kilo|bang"}, {command.PhoneticChar, "oscar"}, {command.CurrentChar, "o"}, {command.CurrentLine, "Book!"}, } { voice.messages = nil handle(input.Decision{Command: trial.id, Keypress: true, Interrupt: true}) want := trial.want if muted { want = "" } if got := strings.Join(voice.messages, "|"); got != want { t.Fatal(muted, trial.id, got, want) } } handle(input.Decision{Command: command.Quit}) return nil }} if err := run(context.Background(), source, keys, voice, nil, RunOptions{Settings: settings, Punctuation: processor}); err != nil { t.Fatal(err) } } }