Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/atspi"
|
||||
"git.stormux.org/storm/cthulhu/internal/config"
|
||||
)
|
||||
|
||||
func TestNativeProgressBars(t *testing.T) {
|
||||
if os.Getenv("CTHULHU_NATIVE_PROGRESS") != "1" {
|
||||
t.Skip("requires private X11/AT-SPI progress fixture")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
adapter := atspi.New()
|
||||
if err := adapter.Start(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
stopCtx, stopCancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer stopCancel()
|
||||
if err := adapter.Stop(stopCtx); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
fixture := exec.CommandContext(ctx, "python3", "../../tests/gtk_progress.py")
|
||||
fixture.Stderr = os.Stderr
|
||||
commands, err := fixture.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ready, err := fixture.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := fixture.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { commands.Close(); fixture.Process.Kill(); fixture.Wait() }()
|
||||
line, err := bufio.NewReader(ready).ReadString('\n')
|
||||
if err != nil || strings.TrimSpace(line) != "ready" {
|
||||
t.Fatalf("fixture: %q %v", line, err)
|
||||
}
|
||||
focus := func(title string) {
|
||||
t.Helper()
|
||||
windows, err := exec.Command("xdotool", "search", "--onlyvisible", "--name", "^"+title+"$").Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := exec.Command("xdotool", "windowfocus", "--sync", strings.Fields(string(windows))[0]).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
focus("Progress probe")
|
||||
settings := config.DefaultSettings()
|
||||
settings.ProgressBar.OutputMode = "both"
|
||||
live := &liveSettings{}
|
||||
live.Store(settings)
|
||||
speech := &fakeSpeechQueue{spoken: make(chan string, 16), requests: make(chan speechRequest, 16)}
|
||||
state := &eventLoopState{}
|
||||
handler := &accessibilityEventHandler{state: state, live: live, speech: speech, logger: slog.New(slog.DiscardHandler)}
|
||||
update := func(value float64, hidden ...bool) atspi.Event {
|
||||
t.Helper()
|
||||
command := fmt.Sprint(value)
|
||||
if len(hidden) != 0 && hidden[0] {
|
||||
command = "hide " + command
|
||||
}
|
||||
if _, err := fmt.Fprintln(commands, command); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("progress value event missing")
|
||||
return atspi.Event{}
|
||||
case event := <-adapter.Events():
|
||||
if isValueChangeEvent(event.Type) && event.Source.Role == "progress bar" && event.Source.CurrentValue == value {
|
||||
return event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
event := update(0.5)
|
||||
if event.Source.Focused || !event.Source.Visible || !event.Source.Showing || event.ForegroundMismatch {
|
||||
t.Fatalf("foreground progress capture: %+v", event)
|
||||
}
|
||||
state.owner.window, state.owner.applicationBus = event.Window, event.Source.BusName
|
||||
if !handler.handle_progress_bar(event) {
|
||||
t.Fatal("foreground progress was lost")
|
||||
}
|
||||
request := <-speech.requests
|
||||
if request.Text != "50 percent" || len(request.Sound) != 1 || request.Sound[0].Name != "generated-progress-tone:50" {
|
||||
t.Fatalf("foreground progress output: %+v", request)
|
||||
}
|
||||
focus("Other probe")
|
||||
event = update(0.75)
|
||||
if !event.ForegroundMismatch {
|
||||
t.Fatal("native background progress was not identified")
|
||||
}
|
||||
handler.handle_progress_bar(event)
|
||||
if len(speech.requests) != 0 {
|
||||
t.Fatal("background output enabled by default")
|
||||
}
|
||||
settings.ProgressBar.ReportBackground = true
|
||||
live.Store(settings)
|
||||
handler.handle_progress_bar(event)
|
||||
request = <-speech.requests
|
||||
if request.Text != "75 percent" || len(request.Sound) != 1 {
|
||||
t.Fatalf("background output: %+v", request)
|
||||
}
|
||||
event = update(0.9, true)
|
||||
if event.Source.Showing || handler.handle_progress_bar(event) {
|
||||
t.Fatal("hidden progress generated automatic output")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user