// 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) } } }