Files
cthulhu/internal/app/test_helpers_test.go

248 lines
6.4 KiB
Go

// Cthulhu screen reader.
// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
package app
import (
"context"
"sync"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/input"
"git.stormux.org/storm/cthulhu/internal/sound"
)
type fakeSpeaker struct {
connectError error
connected bool
speakError error
spoken chan string
closed bool
canceled bool
canceledOnClose bool
}
func (speaker *fakeSpeaker) Connect(context.Context) error {
speaker.connected = true
return speaker.connectError
}
func (speaker *fakeSpeaker) Speak(_ context.Context, text string) error {
if speaker.speakError != nil {
return speaker.speakError
}
speaker.spoken <- text
return nil
}
func (speaker *fakeSpeaker) Cancel(context.Context) error { speaker.canceled = true; return nil }
func (speaker *fakeSpeaker) Close(context.Context) error {
speaker.canceledOnClose = speaker.canceled
speaker.closed = true
return nil
}
type fakeAdapter struct {
startError error
stopError error
started bool
stopped bool
events chan atspi.Event
tree *atspi.AccessibleTree
captureErr error
capture func(context.Context) (*atspi.AccessibleTree, error)
captureSoundGeometry bool
}
type fakeSpeechQueue struct {
spoken chan string
requests chan speechRequest
interrupt chan struct{}
}
type fixedInputActivity struct{ activity input.Activity }
func (source fixedInputActivity) RecentInput() input.Activity { return source.activity }
type sequencedInputActivity struct {
mutex sync.Mutex
sequence uint64
}
func (source *sequencedInputActivity) RecentInput() input.Activity {
source.mutex.Lock()
defer source.mutex.Unlock()
return input.Activity{Sequence: source.sequence}
}
func (source *sequencedInputActivity) set(sequence uint64) {
source.mutex.Lock()
source.sequence = sequence
source.mutex.Unlock()
}
func (queue *fakeSpeechQueue) Enqueue(request speechRequest) {
if queue.requests != nil {
queue.requests <- request
}
queue.spoken <- request.Text
}
func (queue *fakeSpeechQueue) Interrupt() {
if queue.interrupt != nil {
queue.interrupt <- struct{}{}
}
}
func (adapter *fakeAdapter) Events() <-chan atspi.Event { return adapter.events }
func (adapter *fakeAdapter) Overflow() uint64 { return 0 }
func (adapter *fakeAdapter) SetCaptureSoundGeometry(enabled bool) {
adapter.captureSoundGeometry = enabled
}
func (adapter *fakeAdapter) CaptureDesktop(ctx context.Context) (*atspi.AccessibleTree, error) {
if adapter.capture != nil {
return adapter.capture(ctx)
}
return adapter.tree, adapter.captureErr
}
func (adapter *fakeAdapter) CaptureReviewWindow(ctx context.Context, _ atspi.ObjectID) (*atspi.AccessibleTree, error) {
return adapter.CaptureDesktop(ctx)
}
func (adapter *fakeAdapter) CaptureDocument(ctx context.Context, _ atspi.ObjectID) (*atspi.AccessibleTree, error) {
return adapter.CaptureDesktop(ctx)
}
func (adapter *fakeAdapter) CaptureDocumentText(ctx context.Context, _ atspi.ObjectID) (*atspi.AccessibleTree, error) {
return adapter.CaptureDesktop(ctx)
}
func (adapter *fakeAdapter) Start(context.Context) error {
adapter.started = true
return adapter.startError
}
func (adapter *fakeAdapter) Stop(context.Context) error {
adapter.stopped = true
return adapter.stopError
}
type fakeInputListener struct {
events chan input.Event
modifierKeycodes input.ModifierKeycodes
startError error
started bool
stopped bool
}
func (listener *fakeInputListener) Events() <-chan input.Event {
return listener.events
}
func (listener *fakeInputListener) ModifierKeycodes() input.ModifierKeycodes {
return listener.modifierKeycodes
}
func (listener *fakeInputListener) Overflow() uint64 { return 0 }
func (listener *fakeInputListener) Start(context.Context) error {
listener.started = true
return listener.startError
}
func (listener *fakeInputListener) Stop(context.Context) error {
listener.stopped = true
return nil
}
func contains_string(values []string, wanted string) bool {
for _, value := range values {
if value == wanted {
return true
}
}
return false
}
type eventDiagnosticsWriter struct {
started chan struct{}
startOnce sync.Once
canceled chan struct{}
cancelOnce sync.Once
}
func (writer *eventDiagnosticsWriter) Write([]byte) (int, error) {
writer.startOnce.Do(func() { close(writer.started) })
<-writer.canceled
return 0, nil
}
func (writer *eventDiagnosticsWriter) Cancel() error {
writer.cancelOnce.Do(func() { close(writer.canceled) })
return nil
}
func (writer *eventDiagnosticsWriter) Close() error { return nil }
type silentSound struct{}
type rejectingSound struct{ silentSound }
type recordingSound struct {
started chan struct{}
stopped bool
stopError error
}
func (sound *recordingSound) Start(context.Context) error {
close(sound.started)
return nil
}
func (sound *recordingSound) Stop(context.Context) error {
sound.stopped = true
return sound.stopError
}
func (sound *recordingSound) Enqueue(sound.Cue) bool { return true }
func (sound *recordingSound) EnqueueBatch([]sound.Cue) bool { return true }
func (sound *recordingSound) Available(string) bool { return true }
type silentCommands struct{}
type fakeCommands struct {
commands chan input.CommandEvent
lockStates chan input.LockState
}
func (commands *fakeCommands) Commands() <-chan input.CommandEvent { return commands.commands }
func (commands *fakeCommands) LockStates() <-chan input.LockState { return commands.lockStates }
func (commands *fakeCommands) Failures() <-chan error { return nil }
func (commands *fakeCommands) Start(context.Context) error { return nil }
func (commands *fakeCommands) Stop(context.Context) error { return nil }