Files
cthulhu/internal/atspi/current_line_native_test.go

100 lines
3.0 KiB
Go

package atspi
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"testing"
"time"
)
func TestNativeCurrentLine(t *testing.T) {
if os.Getenv("CTHULHU_NATIVE_CURRENT_LINE") != "1" {
t.Skip("requires private Xvfb and AT-SPI")
}
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
adapter := New()
if err := adapter.Start(ctx); err != nil {
t.Fatal(err)
}
defer adapter.Stop(context.Background())
fixture := exec.CommandContext(ctx, "/usr/bin/python3", "-u", "../../tests/current_line_fixture.py")
fixture.Stderr = os.Stderr
stdin, _ := fixture.StdinPipe()
stdout, _ := fixture.StdoutPipe()
if err := fixture.Start(); err != nil {
t.Fatal(err)
}
defer func() { stdin.Close(); fixture.Wait() }()
scanner := bufio.NewScanner(stdout)
ready := func() {
t.Helper()
if !scanner.Scan() || scanner.Text() != "ready" {
t.Fatalf("fixture not ready: %q %v", scanner.Text(), scanner.Err())
}
}
ready()
var focus ObjectID
lastControl := ""
for _, probe := range []struct {
control, text string
offset int
want string
password bool
}{
{"entry", "Café 猫 complete", 7, "Café 猫 complete", false},
{"text", "First\nSecond café 猫\nThird", 11, "Second café 猫", false},
{"entry", "Final line", 10, "Final line", false},
{"text", "First\n\nLast", 6, "", false},
{"entry", "", 0, "", false},
{"text", "Final\n", 6, "", false},
{"password", "private test text", 5, "", true},
{"button", "", 0, "Example button", false},
} {
command, _ := json.Marshal(map[string]any{"action": "focus", "control": probe.control, "text": probe.text, "offset": probe.offset})
fmt.Fprintln(stdin, string(command))
ready()
if probe.control != lastControl {
for {
select {
case event := <-adapter.Events():
if event.Type == "object:state-changed:focused" && event.Detail1 == 1 &&
(event.Source.Name == map[string]string{"entry": "Entry", "text": "Text", "password": "Password", "button": "Example button"}[probe.control]) {
focus = event.Source.ID()
goto focused
}
case <-ctx.Done():
t.Fatal(ctx.Err())
}
}
}
focused:
lastControl = probe.control
content, err := adapter.ReadFocusedLine(ctx, focus)
if probe.password {
if err == nil || content.Text != "" || content.Snapshot.Name != "" {
t.Fatalf("password report: %+v %v", content, err)
}
continue
}
if err != nil || strings.TrimRight(content.Text, "\r\n") != probe.want {
t.Fatalf("%s offset %d: %+v %v, want %q", probe.control, probe.offset, content, err, probe.want)
}
if probe.control != "button" {
fmt.Fprintf(stdin, "{\"action\":\"caret\",\"control\":%q}\n", probe.control)
if !scanner.Scan() || scanner.Text() != fmt.Sprint(probe.offset) {
t.Fatalf("caret moved: %q, want %d", scanner.Text(), probe.offset)
}
ready()
}
}
if _, err := adapter.ReadFocusedLine(ctx, ObjectID{BusName: focus.BusName, Path: "/stale"}); err == nil {
t.Fatal("accepted stale native focus")
}
}