Files
cthulhu/internal/atspi/document_error_test.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)
}
}
}