blob: d00a299bf55678a88f60236384c650714e0564b5 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_tree_manager_base.h"
#include <memory>
#include <optional>
#include <utility>
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/accessibility/ax_tree_observer.h"
#include "ui/accessibility/ax_tree_serializer.h"
namespace ui {
namespace {
class AXTreeManagerBaseTest : public ::testing::Test {
public:
AXTreeManagerBaseTest();
AXTreeManagerBaseTest(const AXTreeManagerBaseTest&) = delete;
AXTreeManagerBaseTest& operator=(const AXTreeManagerBaseTest&) = delete;
~AXTreeManagerBaseTest() override = default;
protected:
static constexpr AXNodeID kIframeID = 4;
static AXTreeUpdate CreateSimpleTreeUpdate();
static std::unique_ptr<AXTree> CreateSimpleTree();
static std::unique_ptr<AXTree> CreateComplexTree();
// Modifies `host_node` and its owning tree, as well as `child_tree`, in
// order to connect the two trees in a parent - child relationship.
static void HostChildTreeAtNode(AXNode& host_node, AXTree& child_tree);
void SetUp() override;
AXTreeManagerBase empty_manager_;
AXTreeID simple_tree_id_;
AXTreeManagerBase simple_manager_;
AXTreeID complex_tree_id_;
AXTreeManagerBase complex_manager_;
};
AXTreeManagerBaseTest::AXTreeManagerBaseTest()
: simple_manager_(CreateSimpleTree()),
complex_manager_(CreateComplexTree()) {}
// static
AXTreeUpdate AXTreeManagerBaseTest::CreateSimpleTreeUpdate() {
AXNodeData root;
root.id = 1;
root.role = ax::mojom::Role::kRootWebArea;
AXTreeUpdate update;
update.root_id = root.id;
update.nodes = {root};
update.has_tree_data = true;
AXTreeData tree_data;
tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
update.tree_data = tree_data;
return update;
}
// static
std::unique_ptr<AXTree> AXTreeManagerBaseTest::CreateSimpleTree() {
return std::make_unique<AXTree>(CreateSimpleTreeUpdate());
}
// static
std::unique_ptr<AXTree> AXTreeManagerBaseTest::CreateComplexTree() {
AXNodeData root;
AXNodeData generic_container_ignored;
AXNodeData paragraph;
AXNodeData iframe;
root.id = 1;
generic_container_ignored.id = 2;
paragraph.id = 3;
iframe.id = kIframeID;
root.role = ax::mojom::Role::kRootWebArea;
root.child_ids = {generic_container_ignored.id};
generic_container_ignored.role = ax::mojom::Role::kGenericContainer;
generic_container_ignored.AddState(ax::mojom::State::kIgnored);
generic_container_ignored.child_ids = {paragraph.id, iframe.id};
iframe.role = ax::mojom::Role::kIframe;
AXTreeUpdate update;
update.root_id = root.id;
update.nodes = {root, generic_container_ignored, paragraph, iframe};
update.has_tree_data = true;
AXTreeData tree_data;
tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
tree_data.title = "Application";
update.tree_data = tree_data;
return std::make_unique<AXTree>(update);
}
// static
void AXTreeManagerBaseTest::HostChildTreeAtNode(AXNode& host_node,
AXTree& child_tree) {
ASSERT_NE(nullptr, host_node.tree());
{
AXNodeData host_node_data = host_node.data();
ASSERT_NE(ax::mojom::AXTreeIDType::kUnknown,
child_tree.GetAXTreeID().type());
host_node_data.AddChildTreeId(child_tree.GetAXTreeID());
AXTreeUpdate update;
update.nodes = {host_node_data};
AXTree* parent_tree = static_cast<AXTree*>(host_node.tree());
ASSERT_TRUE(parent_tree->Unserialize(update)) << parent_tree->error();
}
{
AXTreeData tree_data = child_tree.data();
ASSERT_NE(ax::mojom::AXTreeIDType::kUnknown,
host_node.tree()->GetAXTreeID().type());
tree_data.parent_tree_id = host_node.tree()->GetAXTreeID();
AXTreeUpdate update;
update.has_tree_data = true;
update.tree_data = tree_data;
ASSERT_TRUE(child_tree.Unserialize(update)) << child_tree.error();
}
}
void AXTreeManagerBaseTest::SetUp() {
ASSERT_EQ(AXTreeIDUnknown(), empty_manager_.GetTreeID());
simple_tree_id_ = simple_manager_.GetTreeID();
ASSERT_NE(ax::mojom::AXTreeIDType::kUnknown, simple_tree_id_.type());
complex_tree_id_ = complex_manager_.GetTreeID();
ASSERT_NE(ax::mojom::AXTreeIDType::kUnknown, complex_tree_id_.type());
}
class TestAXTreeObserver final : public AXTreeObserver {
public:
TestAXTreeObserver() = default;
~TestAXTreeObserver() override = default;
TestAXTreeObserver(const TestAXTreeObserver&) = delete;
TestAXTreeObserver& operator=(const TestAXTreeObserver&) = delete;
void OnTreeManagerWillBeRemoved(AXTreeID previous_tree_id) override {
++manager_remove_count_;
previous_tree_id_ = previous_tree_id;
}
int manager_remove_count() const { return manager_remove_count_; }
const AXTreeID& previous_tree_id() const { return previous_tree_id_; }
private:
int manager_remove_count_ = 0;
AXTreeID previous_tree_id_;
};
} // namespace
TEST_F(AXTreeManagerBaseTest, GetManager) {
// Since the following two trees are destroyed when their respective managers
// are destructed, we cannot use a reference to their tree IDs. We should copy
// the tree IDs by value if we want to verify that the managers have indeed
// been destructed.
std::unique_ptr<AXTree> simple_tree = CreateSimpleTree();
const AXTreeID simple_tree_id = simple_tree->GetAXTreeID();
std::unique_ptr<AXTree> complex_tree = CreateComplexTree();
const AXTreeID complex_tree_id = complex_tree->GetAXTreeID();
{
const AXTreeManagerBase simple_manager(std::move(simple_tree));
const AXTreeManagerBase complex_manager(std::move(complex_tree));
ASSERT_NE(nullptr, AXTreeManagerBase::GetManager(simple_tree_id));
EXPECT_EQ(&simple_manager, AXTreeManagerBase::GetManager(simple_tree_id));
ASSERT_NE(nullptr, AXTreeManagerBase::GetManager(complex_tree_id));
EXPECT_EQ(&complex_manager, AXTreeManagerBase::GetManager(complex_tree_id));
}
ASSERT_EQ(nullptr, AXTreeManagerBase::GetManager(simple_tree_id));
ASSERT_EQ(nullptr, AXTreeManagerBase::GetManager(complex_tree_id));
}
TEST_F(AXTreeManagerBaseTest, MoveConstructor) {
AXTreeManagerBase new_manager(std::move(simple_manager_));
EXPECT_EQ(simple_tree_id_, new_manager.GetTreeID());
EXPECT_NE(nullptr, new_manager.GetTree());
EXPECT_EQ(AXTreeIDUnknown(), simple_manager_.GetTreeID());
EXPECT_EQ(nullptr, simple_manager_.GetTree());
new_manager = std::move(complex_manager_);
EXPECT_EQ(complex_tree_id_, new_manager.GetTreeID());
EXPECT_NE(nullptr, new_manager.GetTree());
EXPECT_EQ(AXTreeIDUnknown(), complex_manager_.GetTreeID());
EXPECT_EQ(nullptr, complex_manager_.GetTree());
empty_manager_ = std::move(new_manager);
EXPECT_EQ(complex_tree_id_, empty_manager_.GetTreeID());
EXPECT_NE(nullptr, empty_manager_.GetTree());
EXPECT_EQ(AXTreeIDUnknown(), new_manager.GetTreeID());
EXPECT_EQ(nullptr, new_manager.GetTree());
}
TEST_F(AXTreeManagerBaseTest, SetTree) {
// Try setting a new tree on construction via an `AXTreeUpdate`.
const AXTreeUpdate initial_state = CreateSimpleTreeUpdate();
const AXTreeID& initial_tree_id = initial_state.tree_data.tree_id;
const AXTreeManagerBase initial_manager(initial_state);
EXPECT_EQ(initial_tree_id, initial_manager.GetTreeID());
std::unique_ptr<AXTree> new_tree = CreateSimpleTree();
const AXTreeID& new_tree_id = new_tree->GetAXTreeID();
std::unique_ptr<AXTree> old_tree =
simple_manager_.SetTree(std::move(new_tree));
ASSERT_NE(nullptr, old_tree.get());
ASSERT_EQ(nullptr, new_tree.get());
EXPECT_EQ(simple_tree_id_, old_tree->GetAXTreeID());
EXPECT_EQ(new_tree_id, simple_manager_.GetTreeID());
new_tree = simple_manager_.SetTree(initial_state);
ASSERT_NE(nullptr, new_tree.get());
EXPECT_EQ(new_tree_id, new_tree->GetAXTreeID());
EXPECT_EQ(initial_tree_id, initial_manager.GetTreeID());
}
TEST_F(AXTreeManagerBaseTest, ReleaseTree) {
std::unique_ptr<AXTree> simple_tree = simple_manager_.ReleaseTree();
EXPECT_EQ(AXTreeIDUnknown(), simple_manager_.GetTreeID());
EXPECT_EQ(nullptr, simple_manager_.GetTree());
ASSERT_NE(nullptr, simple_tree.get());
EXPECT_EQ(simple_tree_id_, simple_tree->GetAXTreeID());
}
TEST_F(AXTreeManagerBaseTest, GetNode) {
EXPECT_EQ(simple_manager_.GetRoot(), AXTreeManagerBase::GetNodeFromTree(
simple_tree_id_, /* AXNodeID */ 1));
EXPECT_EQ(
complex_manager_.GetRoot(),
AXTreeManagerBase::GetNodeFromTree(complex_tree_id_, /* AXNodeID */ 1));
EXPECT_EQ(simple_manager_.GetRoot(),
simple_manager_.GetNode(/* AXNodeID */ 1));
AXNode* iframe =
AXTreeManagerBase::GetNodeFromTree(complex_tree_id_, kIframeID);
ASSERT_NE(nullptr, iframe);
EXPECT_EQ(kIframeID, iframe->id());
}
TEST_F(AXTreeManagerBaseTest, ParentChildTreeRelationship) {
EXPECT_EQ(nullptr, empty_manager_.GetRoot());
EXPECT_EQ(nullptr, empty_manager_.GetHostNode());
AXNode* iframe =
AXTreeManagerBase::GetNodeFromTree(complex_tree_id_, kIframeID);
ASSERT_NE(nullptr, iframe);
const AXNode* simple_manager_root = simple_manager_.GetTree()->root();
ASSERT_NE(nullptr, simple_manager_root);
HostChildTreeAtNode(*iframe, *simple_manager_.GetTree());
EXPECT_EQ(complex_tree_id_, simple_manager_.GetParentTreeID());
EXPECT_EQ(nullptr, complex_manager_.GetHostNode());
EXPECT_EQ(iframe, simple_manager_.GetHostNode());
EXPECT_EQ(simple_manager_root,
complex_manager_.GetRootOfChildTree(kIframeID));
EXPECT_EQ(simple_manager_root, complex_manager_.GetRootOfChildTree(*iframe));
}
TEST_F(AXTreeManagerBaseTest, AttachingAndDetachingChildTrees) {
AXNode* iframe =
AXTreeManagerBase::GetNodeFromTree(complex_tree_id_, kIframeID);
ASSERT_NE(nullptr, iframe);
AXNode* root = complex_manager_.GetTree()->root();
ASSERT_NE(nullptr, root);
const AXNode* child_root = simple_manager_.GetTree()->root();
ASSERT_NE(nullptr, child_root);
EXPECT_FALSE(complex_manager_.AttachChildTree(root->id(), simple_manager_))
<< "This particular rootnode is not a leaf node.";
EXPECT_FALSE(complex_manager_.AttachChildTree(*root, simple_manager_))
<< "This particular rootnode is not a leaf node.";
EXPECT_FALSE(complex_manager_.AttachChildTree(kIframeID, empty_manager_))
<< "Cannot attach an empty tree to any node.";
EXPECT_FALSE(complex_manager_.AttachChildTree(*iframe, empty_manager_))
<< "Cannot attach an empty tree to any node.";
EXPECT_TRUE(complex_manager_.AttachChildTree(kIframeID, simple_manager_));
EXPECT_EQ(iframe, simple_manager_.GetHostNode());
EXPECT_EQ(child_root, complex_manager_.GetRootOfChildTree(kIframeID));
EXPECT_EQ(&simple_manager_, complex_manager_.DetachChildTree(*iframe));
EXPECT_EQ(nullptr, simple_manager_.GetHostNode());
EXPECT_EQ(nullptr, complex_manager_.GetRootOfChildTree(*iframe));
EXPECT_TRUE(complex_manager_.AttachChildTree(*iframe, simple_manager_));
EXPECT_EQ(iframe, simple_manager_.GetHostNode());
EXPECT_EQ(child_root, complex_manager_.GetRootOfChildTree(*iframe));
EXPECT_EQ(&simple_manager_, complex_manager_.DetachChildTree(kIframeID));
EXPECT_EQ(nullptr, simple_manager_.GetHostNode());
EXPECT_EQ(nullptr, complex_manager_.GetRootOfChildTree(kIframeID));
std::optional<AXTreeManagerBase> child_manager =
complex_manager_.AttachChildTree(*iframe, CreateSimpleTreeUpdate());
ASSERT_TRUE(child_manager.has_value());
EXPECT_NE(nullptr, child_manager->GetTree());
EXPECT_EQ(&(child_manager.value()),
complex_manager_.DetachChildTree(*iframe));
child_manager =
complex_manager_.AttachChildTree(kIframeID, CreateSimpleTreeUpdate());
EXPECT_NE(nullptr, child_manager->GetTree());
ASSERT_TRUE(child_manager.has_value());
;
EXPECT_EQ(&(child_manager.value()),
complex_manager_.DetachChildTree(kIframeID));
}
TEST_F(AXTreeManagerBaseTest, Observers) {
TestAXTreeObserver observer;
simple_manager_.GetTree()->AddObserver(&observer);
EXPECT_TRUE(simple_manager_.GetTree()->HasObserver(&observer));
EXPECT_FALSE(complex_manager_.GetTree()->HasObserver(&observer));
std::unique_ptr<AXTree> new_tree = CreateSimpleTree();
// `new_tree_id` should be copied by value and not accessed by reference,
// because `new_tree` will be destructed before we access `new_tree_id`.
const AXTreeID new_tree_id = new_tree->GetAXTreeID();
simple_manager_.SetTree(std::move(new_tree));
EXPECT_EQ(1, observer.manager_remove_count());
EXPECT_EQ(simple_tree_id_, observer.previous_tree_id());
simple_manager_.GetTree()->AddObserver(&observer);
simple_manager_.ReleaseTree();
EXPECT_EQ(2, observer.manager_remove_count());
EXPECT_EQ(new_tree_id, observer.previous_tree_id());
simple_manager_ = std::move(complex_manager_);
EXPECT_EQ(2, observer.manager_remove_count())
<< "Tree must have already been destroyed.";
EXPECT_EQ(new_tree_id, observer.previous_tree_id());
}
} // namespace ui