129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
// 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")
|
|
}
|
|
}
|