121 lines
4.5 KiB
Go
121 lines
4.5 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
func TestDocumentMissingObjectRetiresCachedTarget(t *testing.T) {
|
|
adapter := New()
|
|
adapter.tree, adapter.documentTree, adapter.documentTextTree = testTree(t), testTree(t), testTree(t)
|
|
object := testID("/second")
|
|
failure := fmt.Errorf("get document caret: Method %q with signature %q on interface %q could not be processed as object %s does not exist\n",
|
|
"Get", "ss", "org.freedesktop.DBus.Properties", object.Path)
|
|
err := adapter.document_object_error(object, failure)
|
|
if !errors.Is(err, ErrDeadObject) || !errors.Is(err, failure) {
|
|
t.Fatalf("missing-object error lost classification or details: %v", err)
|
|
}
|
|
for _, tree := range []*AccessibleTree{adapter.tree, adapter.documentTree, adapter.documentTextTree} {
|
|
if _, err := tree.Reference(object); !errors.Is(err, ErrObjectNotFound) {
|
|
t.Fatalf("removed target remains cached: %v", err)
|
|
}
|
|
if _, err := tree.Reference(testID("/grandchild")); !errors.Is(err, ErrObjectNotFound) {
|
|
t.Fatalf("removed descendant remains cached: %v", err)
|
|
}
|
|
if _, err := tree.Reference(testID("/first")); err != nil {
|
|
t.Fatalf("live sibling was retired: %v", err)
|
|
}
|
|
root, _ := tree.Root()
|
|
if _, _, err := tree.FirstChild(root); !errors.Is(err, ErrIncompleteTree) {
|
|
t.Fatalf("parent child order was not marked for refresh: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDocumentCaretFailuresDoNotRetireLiveTargets(t *testing.T) {
|
|
for _, message := range []string{
|
|
"browser refused document caret",
|
|
"get document caret: timeout",
|
|
"get document caret: unknown property CaretOffset",
|
|
"could not be processed as object /other does not exist",
|
|
"could not be processed as object /second_child does not exist",
|
|
} {
|
|
t.Run(message, func(t *testing.T) {
|
|
adapter := New()
|
|
adapter.documentTree = testTree(t)
|
|
failure := errors.New(message)
|
|
if err := adapter.document_object_error(testID("/second"), failure); err != failure {
|
|
t.Fatalf("ordinary failure reclassified: %v", err)
|
|
}
|
|
if _, err := adapter.documentTree.Reference(testID("/second")); err != nil {
|
|
t.Fatalf("unconfirmed removal retired live target: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDocumentEmptyTextRefreshesMetadataAndParentWithoutRetiringTargets(t *testing.T) {
|
|
adapter := New()
|
|
tree := testTree(t)
|
|
adapter.documentTree = tree
|
|
object := testID("/second")
|
|
target, _ := tree.Reference(object)
|
|
sibling, _ := tree.Reference(testID("/first"))
|
|
if err := adapter.document_text_became_empty(object); !errors.Is(err, ErrStaleText) {
|
|
t.Fatalf("unexpected empty-text classification: %v", err)
|
|
}
|
|
if current, err := tree.Reference(object); err != nil || current != target {
|
|
t.Fatalf("empty text retired an unconfirmed target: %v", err)
|
|
}
|
|
if _, err := tree.Resolve(target); !errors.Is(err, ErrStaleSnapshot) {
|
|
t.Fatalf("text metadata did not require refresh: %v", err)
|
|
}
|
|
if _, err := tree.Resolve(sibling); err != nil {
|
|
t.Fatalf("live sibling was invalidated: %v", err)
|
|
}
|
|
root, _ := tree.Root()
|
|
if _, _, err := tree.FirstChild(root); !errors.Is(err, ErrIncompleteTree) {
|
|
t.Fatalf("parent's children did not require refresh: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDocumentMissingDescendantRefreshesAncestorChildOrder(t *testing.T) {
|
|
adapter := New()
|
|
adapter.tree, adapter.documentTree, adapter.documentTextTree = testTree(t), testTree(t), testTree(t)
|
|
object := testID("/grandchild")
|
|
failure := fmt.Errorf("get document caret: could not be processed as object %s does not exist", object.Path)
|
|
if err := adapter.document_object_error(object, failure); !errors.Is(err, ErrDeadObject) {
|
|
t.Fatal(err)
|
|
}
|
|
for _, tree := range []*AccessibleTree{adapter.tree, adapter.documentTree, adapter.documentTextTree} {
|
|
for _, id := range []ObjectID{testID("/second"), testID("/root")} {
|
|
ref, err := tree.Reference(id)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := tree.FirstChild(ref); !errors.Is(err, ErrIncompleteTree) {
|
|
t.Fatalf("ancestor %s retained obsolete child order: %v", id.Path, err)
|
|
}
|
|
}
|
|
// Refresh ancestry without declaring unrelated siblings dead or dirty.
|
|
sibling, err := tree.Reference(testID("/first"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := tree.Resolve(sibling); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := tree.FirstChild(sibling); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := tree.Reference(object); !errors.Is(err, ErrObjectNotFound) {
|
|
t.Fatalf("missing object survived: %v", err)
|
|
}
|
|
}
|
|
}
|