99 lines
3.5 KiB
Go
99 lines
3.5 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"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/screen"
|
|
"git.stormux.org/storm/fenrir/internal/spellcheck"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type LoopDictionary struct {
|
|
entered chan error
|
|
release <-chan struct{}
|
|
}
|
|
|
|
func (d *LoopDictionary) Check(string) (bool, error) {
|
|
d.entered <- nil
|
|
<-d.release
|
|
return false, nil
|
|
}
|
|
func (*LoopDictionary) Suggest(string) ([]string, error) { return nil, nil }
|
|
func (*LoopDictionary) Close() {}
|
|
|
|
func TestAutomaticSpellcheckDropsResultsAfterInputMuteSilenceAndScreenChanges(t *testing.T) {
|
|
for _, state := range []string{"enabled", "disabled", "muted", "temporary", "new input", "screen generation", "screen size"} {
|
|
t.Run(state, func(t *testing.T) {
|
|
settings := config.Defaults()
|
|
settings.Reading.Spellcheck = state != "disabled"
|
|
settings.Keyboard.CharacterEcho = false
|
|
settings.Reading.Automatic = false // Spellcheck is an independent choice.
|
|
settings.Presentation.Sound.Enabled = false
|
|
keys := &fakeKeys{events: make(chan error, 2)}
|
|
gate := make(chan struct{})
|
|
service := spellcheck.New_service(context.Background(), spellcheck.Options{ExceptionsPath: filepath.Join(t.TempDir(), "exceptions.json"), OpenDictionary: func(string) (spellcheck.Dictionary, error) { return &LoopDictionary{keys.events, gate}, nil }})
|
|
defer service.Close()
|
|
voice := &fakeVoice{}
|
|
source := &fakeSource{snapshot: screen.Snapshot{Terminal: 2, Active: true, Rows: 1, Cols: 32, X: 6, Cells: []rune("$ helo" + strings.Repeat(" ", 26))}, events: make(chan error, 2)}
|
|
reads, steps := 0, 0
|
|
source.afterRead = func() {
|
|
reads++
|
|
if reads == 3 && (state == "screen generation" || state == "screen size") {
|
|
close(gate)
|
|
}
|
|
}
|
|
keys.poll = func(_ bool, handle func(input.Decision)) error {
|
|
steps++
|
|
if steps == 1 {
|
|
voice.messages = nil
|
|
handle(input.Decision{Forward: true, Typing: true, WordBoundary: true, Interrupt: true, Keypress: true})
|
|
source.snapshot.Cells = []rune("$ helo " + strings.Repeat(" ", 25))
|
|
source.snapshot.X = 7
|
|
source.events <- nil
|
|
} else if steps == 2 {
|
|
switch state {
|
|
case "muted":
|
|
handle(input.Decision{Command: command.Mute, Interrupt: true})
|
|
case "temporary":
|
|
handle(input.Decision{Command: command.TempDisableSpeech, Interrupt: true})
|
|
case "new input":
|
|
handle(input.Decision{Forward: true, Typing: true, Interrupt: true, Keypress: true})
|
|
case "screen generation":
|
|
source.snapshot.Generation++
|
|
source.events <- nil
|
|
return nil
|
|
case "screen size":
|
|
source.snapshot.Cols++
|
|
source.snapshot.Cells = append(source.snapshot.Cells, ' ')
|
|
source.events <- nil
|
|
return nil
|
|
}
|
|
close(gate)
|
|
}
|
|
return nil
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 150*time.Millisecond)
|
|
err := run(ctx, source, keys, voice, nil, RunOptions{Settings: settings, Spellcheck: service})
|
|
cancel()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "disabled" && steps < 2 {
|
|
t.Fatal("fixture did not reach the delayed dictionary result", reads, steps)
|
|
}
|
|
got := strings.Contains(strings.Join(voice.messages, "|"), "misspelled")
|
|
if got != (state == "enabled") {
|
|
t.Fatal(state, voice.messages, reads, steps)
|
|
}
|
|
})
|
|
}
|
|
}
|