116 lines
3.3 KiB
Go
116 lines
3.3 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"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/braille"
|
|
)
|
|
|
|
func TestNativeEmbeddedPanelAppletPresentation(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_PANEL_APPLET") != "1" {
|
|
t.Skip("requires private Xorg/AT-SPI session")
|
|
}
|
|
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 adapter.Stop(context.Background())
|
|
start := func(args ...string) (io.WriteCloser, string, string) {
|
|
t.Helper()
|
|
command := exec.CommandContext(ctx, "/usr/bin/python3", append([]string{"../../tests/gtk_panel_applet.py"}, args...)...)
|
|
input, err := command.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
output, err := command.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
command.Stderr = os.Stderr
|
|
if err := command.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { input.Close(); command.Process.Kill(); command.Wait() })
|
|
line, err := bufio.NewReader(output).ReadString('\n')
|
|
fields := strings.Fields(line)
|
|
if err != nil || len(fields) != 3 || fields[0] != "ready" {
|
|
t.Fatalf("fixture readiness: %q, %v", line, err)
|
|
}
|
|
return input, fields[1], fields[2]
|
|
}
|
|
host, xid, socketID := start("panel")
|
|
start("applet", socketID)
|
|
editor, editorXID, _ := start("editor")
|
|
key := func(args ...string) {
|
|
t.Helper()
|
|
if output, err := exec.CommandContext(ctx, "xdotool", args...).CombinedOutput(); err != nil {
|
|
t.Fatalf("xdotool: %s, %v", output, err)
|
|
}
|
|
}
|
|
speech := &fakeSpeechQueue{spoken: make(chan string, 128)}
|
|
frames := make(chan braille.Frame, 128)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
processEventsWithInput(ctx, slog.New(slog.DiscardHandler),
|
|
outputQueue{speech: speech, braille: browserBrailleProbe{frames: frames}},
|
|
adapter, nil, nil, 0, nil, nil, nil, nil)
|
|
}()
|
|
defer func() { cancel(); <-done }()
|
|
wait := func(name string) {
|
|
t.Helper()
|
|
spoken, displayed := false, false
|
|
var messages []string
|
|
for !spoken || !displayed {
|
|
select {
|
|
case text := <-speech.spoken:
|
|
messages = append(messages, text)
|
|
spoken = spoken || strings.Contains(text, name)
|
|
case frame := <-frames:
|
|
displayed = displayed || strings.Contains(frame.Text, name)
|
|
case <-ctx.Done():
|
|
t.Fatalf("missing presentation %q: speech=%v braille=%v messages=%q", name, spoken, displayed, messages)
|
|
}
|
|
}
|
|
}
|
|
for iteration := 0; iteration < 2; iteration++ {
|
|
key("windowfocus", "--sync", xid)
|
|
fmt.Fprintln(host, "first")
|
|
wait("Before applet")
|
|
key("key", "Tab")
|
|
wait("Network applet")
|
|
key("key", "Tab")
|
|
wait("Applet settings")
|
|
key("key", "shift+Tab")
|
|
wait("Network applet")
|
|
key("key", "Tab")
|
|
wait("Applet settings")
|
|
key("key", "Tab")
|
|
wait("After applet")
|
|
for _, name := range []string{"Applet settings", "Network applet", "Before applet"} {
|
|
key("key", "shift+Tab")
|
|
wait(name)
|
|
}
|
|
key("windowfocus", "--sync", editorXID)
|
|
text := fmt.Sprintf("Returned to editor %d", iteration)
|
|
fmt.Fprintln(editor, "edit "+text)
|
|
wait(text)
|
|
}
|
|
}
|