82 lines
2.3 KiB
Go
82 lines
2.3 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
package atspi
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/platform/xorg"
|
|
)
|
|
|
|
func TestNativeMouseReviewPointerAndHitTest(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_MOUSE_REVIEW") != "1" {
|
|
t.Skip("requires private GTK and Xorg fixture")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
|
defer cancel()
|
|
adapter := New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
stopCtx, stop := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer stop()
|
|
if err := adapter.Stop(stopCtx); err != nil {
|
|
t.Errorf("stop adapter: %v", err)
|
|
}
|
|
}()
|
|
tree, err := adapter.CaptureDesktop(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pointer := xorg.OpenPointerReader()
|
|
if pointer == nil {
|
|
t.Fatal("X pointer reader unavailable")
|
|
}
|
|
defer pointer.Close()
|
|
for _, item := range []struct {
|
|
name, role string
|
|
protected bool
|
|
}{
|
|
{"Caret review probe", "text", false},
|
|
{"Protected text probe", "password text", true},
|
|
} {
|
|
ref := findNamedReference(t, tree, item.name)
|
|
if !ref.Valid() {
|
|
t.Fatalf("%s absent", item.name)
|
|
}
|
|
geometry, err := tree.Geometry(ref)
|
|
if err != nil || !geometry.BoundsKnown {
|
|
t.Fatalf("%s geometry: %+v, %v", item.name, geometry, err)
|
|
}
|
|
x := int(geometry.Bounds.X + min(25, geometry.Bounds.Width/2))
|
|
y := int(geometry.Bounds.Y + geometry.Bounds.Height/2)
|
|
if out, err := exec.Command("xdotool", "mousemove", strconv.Itoa(x), strconv.Itoa(y)).CombinedOutput(); err != nil {
|
|
t.Fatalf("move pointer: %s: %v", out, err)
|
|
}
|
|
position := pointer.Read()
|
|
if !position.Known || position.PID <= 0 || position.X != x || position.Y != y {
|
|
t.Fatalf("%s pointer = %+v", item.name, position)
|
|
}
|
|
point, err := adapter.ReviewAtPoint(ctx, position.X, position.Y, position.PID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if point.Snapshot.Role != item.role || point.Snapshot.Name != item.name || !point.Window.Valid() {
|
|
t.Fatalf("%s hit test = %+v", item.name, point)
|
|
}
|
|
if item.protected && (point.Word != "" || point.Line != "") {
|
|
t.Fatalf("password content exposed: %+v", point)
|
|
}
|
|
if !item.protected && (point.Line == "" || point.Offset < 0) {
|
|
t.Fatalf("entry text missing: %+v", point)
|
|
}
|
|
}
|
|
}
|