// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "reflect" "testing" "time" "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" ) func TestDeletionThroughSharedRouterWithAutoreadOff(t *testing.T) { for _, key := range []uint16{input.KeyBackspace, input.KeyDelete} { for _, mode := range []string{"enabled", "muted", "temporary silence", "speech disabled"} { t.Run(mode+"/"+map[uint16]string{input.KeyBackspace: "backspace", input.KeyDelete: "delete"}[key], func(t *testing.T) { router, err := input.New_router(input.Layout{Bindings: []input.Binding{{Name: "Quit", Keys: []string{"FENRIR", "Q"}, Command: command.Quit}}}) if err != nil { t.Fatal(err) } beforeX := 11 if key == input.KeyDelete { beforeX = 10 } source := &fakeSource{snapshot: screen.Snapshot{Terminal: 1, Active: true, Rows: 2, Cols: 20, X: beforeX, Cells: []rune("type 2% abc " + "Download 20% ")}, events: make(chan error, 1)} keys := &fakeKeys{events: make(chan error, 1)} voice, sounds := &fakeVoice{}, &fakeSounds{} voice.muted = mode == "speech disabled" reads, polls := 0, 0 source.afterRead = func() { reads++ if reads == 2 { keys.events <- nil } } keys.poll = func(_ bool, handle func(input.Decision)) error { polls++ if polls == 1 { handle(input.Decision{Command: command.AutoRead, Interrupt: true}) if mode == "muted" { handle(input.Decision{Command: command.Mute, Interrupt: true}) } handle(router.Key(0, key, 1, false, false)) if mode == "temporary silence" { handle(input.Decision{Command: command.TempDisableSpeech, Interrupt: true}) } voice.messages = nil source.snapshot.Cells = []rune("type 2% ab " + "Download 30% ") source.snapshot.X = 10 source.events <- nil } else { handle(input.Decision{Command: command.Quit}) } return nil } settings := config.Defaults() settings.Keyboard.CharacterEcho = false settings.Keyboard.WordEcho = false settings.Reading.CursorEcho = false settings.Presentation.Progress.Enabled = true settings.Presentation.Speech.Enabled = mode != "speech disabled" ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() if err := run(ctx, source, keys, voice, nil, RunOptions{Settings: settings, Sounds: sounds}); err != nil || reads != 2 { t.Fatal(err, reads) } var want []string if mode == "enabled" { want = []string{"c"} } if !reflect.DeepEqual(voice.messages, want) { t.Fatal("deletion speech or concurrent output", voice.messages, want) } wantProgress := []float64{30} if mode == "temporary silence" { wantProgress = nil } if !reflect.DeepEqual(sounds.progress, wantProgress) { t.Fatal("concurrent background progress lost or deletion beeped", sounds.progress) } }) } } }