// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package review import ( "context" "testing" "time" "git.stormux.org/storm/cthulhu/internal/atspi" ) type refreshingActivator struct { *readAllCapturer target atspi.ObjectID staleOnce bool } func (activator *refreshingActivator) ActivateDocumentObject(_ context.Context, target atspi.ObjectReference, _ atspi.ObjectID) error { if activator.staleOnce { activator.staleOnce = false return atspi.ErrStaleObject } activator.target = target.ID() return nil } func activationTree(t *testing.T, includeSelected bool) *atspi.AccessibleTree { t.Helper() root := atspi.TreeNode{Snapshot: atspi.Snapshot{BusName: webID("/doc").BusName, Path: "/doc", Role: "document web"}, Children: []atspi.ObjectID{webID("/focused")}} nodes := []atspi.TreeNode{root, {Snapshot: atspi.Snapshot{BusName: webID("/focused").BusName, Path: "/focused", Role: "link"}}} if includeSelected { nodes[0].Children = append(nodes[0].Children, webID("/selected")) nodes = append(nodes, atspi.TreeNode{Snapshot: atspi.Snapshot{BusName: webID("/selected").BusName, Path: "/selected", Role: "link"}}) } tree, err := atspi.NewAccessibleTree(webID("/doc"), nodes) if err != nil { t.Fatal(err) } return tree } func TestActivationRecoversUncapturedAndStaleCursors(t *testing.T) { for _, kind := range []string{"uncaptured", "stale", "removed", "native-stale"} { t.Run(kind, func(t *testing.T) { fresh := activationTree(t, kind != "removed") activator := &refreshingActivator{readAllCapturer: &readAllCapturer{tree: fresh}, staleOnce: kind == "native-stale"} worker, _ := NewWorker(activator, time.Second) worker.SyncFocus(webID("/focused")) var cursor *DocumentCursor if kind != "uncaptured" { old := activationTree(t, true) cursor, _ = NewDocumentCursor(old) cursor.SyncFocus(context.Background(), webID("/focused")) cursor.places[0].object, _ = old.Reference(webID("/selected")) if kind != "native-stale" { old.Invalidate(webID("/selected")) } } result, updated := worker.activateDocumentCaret(context.Background(), cursor, 0) if kind == "removed" { if result.Err == nil || activator.target.Valid() { t.Fatal("activated a different link after selected link disappeared") } } else { want := webID("/selected") if kind == "uncaptured" { want = webID("/focused") } if result.Err != nil || activator.target != want { t.Fatalf("activation = %v, %v; want %v", activator.target, result.Err, want) } if current, err := updated.ActivationTarget(); err != nil || current.ID() != want { t.Fatalf("cursor lost after activation: %v, %v", current, err) } } }) } }