// 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 . package app import "testing" type remoteSpeechQueue struct { requests []speechRequest interrupts int } type remoteBrailleQueue struct { running bool text []string } func (queue *remoteBrailleQueue) EnqueueMessage(text string) bool { if !queue.running { return false } queue.text = append(queue.text, text) return true } func TestRemoteBrailleMessage(t *testing.T) { queue := &remoteBrailleQueue{} presenter := remoteSpeech{braille: queue} if presenter.BrailleMessage("Unavailable") || len(queue.text) != 0 { t.Fatal("message accepted while braille is stopped") } queue.running = true if !presenter.BrailleMessage("Braille") || len(queue.text) != 1 || queue.text[0] != "Braille" { t.Fatalf("braille message was not queued: %v", queue.text) } if presenter.BrailleMessage("") { t.Fatal("empty message accepted") } } func TestRemotePresentMessageUsesSpeechAndBraille(t *testing.T) { speech := &remoteSpeechQueue{} braille := &remoteBrailleQueue{running: true} presenter := remoteSpeech{speech: speech, braille: braille} if !presenter.PresentMessage("Status") || len(speech.requests) != 1 || len(braille.text) != 1 || braille.text[0] != "Status" { t.Fatalf("present message: speech=%v braille=%v", speech.requests, braille.text) } if !(remoteSpeech{braille: braille}).PresentMessage("Braille only") { t.Fatal("braille-only presentation rejected") } } func TestWineMessageKeepsSourcePrivateInSpeechRequest(t *testing.T) { speech := &remoteSpeechQueue{} presenter := remoteSpeech{speech: speech} if !presenter.presentMessage("Game control", "wine-accessible-event") { t.Fatal("Wine presentation rejected") } if len(speech.requests) != 1 || speech.requests[0].Cause != "wine-accessible-event" { t.Fatalf("speech requests = %+v", speech.requests) } } func (queue *remoteSpeechQueue) Enqueue(request speechRequest) { queue.requests = append(queue.requests, request) } func (queue *remoteSpeechQueue) Interrupt() { queue.interrupts++ } func TestRemoteSpeechQueuesWithoutInterruptingByDefault(t *testing.T) { queue := &remoteSpeechQueue{} presenter := remoteSpeech{speech: queue} if !presenter.PresentMessage("Status") { t.Fatal("PresentMessage rejected available speech") } if !presenter.SpeakText("Queued", false) { t.Fatal("SpeakText rejected available speech") } if queue.interrupts != 0 { t.Fatalf("interrupts = %d, want 0", queue.interrupts) } if len(queue.requests) != 2 { t.Fatalf("requests = %d, want 2", len(queue.requests)) } for index, request := range queue.requests { if !request.Append { t.Fatalf("request %d was not queued as append speech", index) } } } func TestRemoteSpeechInterruptsBeforeRequestedSpeech(t *testing.T) { queue := &remoteSpeechQueue{} presenter := remoteSpeech{speech: queue} if !presenter.SpeakText("Now", true) { t.Fatal("SpeakText rejected available speech") } if queue.interrupts != 1 { t.Fatalf("interrupts = %d, want 1", queue.interrupts) } if len(queue.requests) != 1 || queue.requests[0].Text != "Now" || !queue.requests[0].Append { t.Fatalf("request = %+v, want queued Now speech", queue.requests) } } func TestRemoteSpeechCancellationAndUnavailableBraille(t *testing.T) { queue := &remoteSpeechQueue{} presenter := remoteSpeech{speech: queue} if !presenter.CancelSpeech() { t.Fatal("CancelSpeech rejected available speech") } if queue.interrupts != 1 { t.Fatalf("interrupts = %d, want 1", queue.interrupts) } if presenter.BrailleMessage("Braille") { t.Fatal("BrailleMessage succeeded without a braille backend") } unavailable := remoteSpeech{} if unavailable.PresentMessage("Status") || unavailable.SpeakText("Text", false) || unavailable.CancelSpeech() { t.Fatal("remote speech succeeded without a speech worker") } }