blob: 2c26e18d2803df2800579e59e4f0cdb2f9231a63 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_node.h"
#include <memory>
#include <utility>
#include <vector>
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.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/test_ax_tree_manager.h"
namespace ui {
namespace {
// The third argument is an optional description string which we don't need
// because, after verifying manually, test errors are descriptive enough.
MATCHER_P(HasAXNodeID, ax_node_data, "") {
return arg->id() == ax_node_data.id;
}
} // namespace
using testing::ElementsAre;
TEST(AXNodeTest, TreeWalking) {
// ++kRootWebArea
// ++++kParagraph
// ++++++kStaticText IGNORED
// ++++kParagraph IGNORED
// ++++++kStaticText
// ++++kParagraph
// ++++++kStaticText
// ++++kParagraph IGNORED
// ++++++kLink IGNORED
// ++++++++kStaticText
// ++++++kButton
// Numbers at the end of variable names indicate their position under the
// root.
AXNodeData root;
AXNodeData paragraph_0;
AXNodeData static_text_0_0_ignored;
AXNodeData paragraph_1_ignored;
AXNodeData static_text_1_0;
AXNodeData paragraph_2;
AXNodeData static_text_2_0;
AXNodeData paragraph_3_ignored;
AXNodeData link_3_0_ignored;
AXNodeData static_text_3_0_0;
AXNodeData button_3_1;
root.id = 1;
paragraph_0.id = 2;
static_text_0_0_ignored.id = 3;
paragraph_1_ignored.id = 4;
static_text_1_0.id = 5;
paragraph_2.id = 6;
static_text_2_0.id = 7;
paragraph_3_ignored.id = 8;
link_3_0_ignored.id = 9;
static_text_3_0_0.id = 10;
button_3_1.id = 11;
root.role = ax::mojom::Role::kRootWebArea;
root.child_ids = {paragraph_0.id, paragraph_1_ignored.id, paragraph_2.id,
paragraph_3_ignored.id};
paragraph_0.role = ax::mojom::Role::kParagraph;
paragraph_0.child_ids = {static_text_0_0_ignored.id};
static_text_0_0_ignored.role = ax::mojom::Role::kStaticText;
static_text_0_0_ignored.AddState(ax::mojom::State::kIgnored);
static_text_0_0_ignored.SetName("static_text_0_0_ignored");
paragraph_1_ignored.role = ax::mojom::Role::kParagraph;
paragraph_1_ignored.AddState(ax::mojom::State::kIgnored);
paragraph_1_ignored.child_ids = {static_text_1_0.id};
static_text_1_0.role = ax::mojom::Role::kStaticText;
static_text_1_0.SetName("static_text_1_0");
paragraph_2.role = ax::mojom::Role::kParagraph;
paragraph_2.child_ids = {static_text_2_0.id};
static_text_2_0.role = ax::mojom::Role::kStaticText;
static_text_2_0.SetName("static_text_2_0");
paragraph_3_ignored.role = ax::mojom::Role::kParagraph;
paragraph_3_ignored.AddState(ax::mojom::State::kIgnored);
paragraph_3_ignored.child_ids = {link_3_0_ignored.id, button_3_1.id};
link_3_0_ignored.role = ax::mojom::Role::kLink;
link_3_0_ignored.AddState(ax::mojom::State::kLinked);
link_3_0_ignored.AddState(ax::mojom::State::kIgnored);
link_3_0_ignored.child_ids = {static_text_3_0_0.id};
static_text_3_0_0.role = ax::mojom::Role::kStaticText;
static_text_3_0_0.SetName("static_text_3_0_0");
button_3_1.role = ax::mojom::Role::kButton;
button_3_1.SetName("button_3_1");
AXTreeUpdate initial_state;
initial_state.root_id = root.id;
initial_state.nodes = {root,
paragraph_0,
static_text_0_0_ignored,
paragraph_1_ignored,
static_text_1_0,
paragraph_2,
static_text_2_0,
paragraph_3_ignored,
link_3_0_ignored,
static_text_3_0_0,
button_3_1};
initial_state.has_tree_data = true;
AXTreeData tree_data;
tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
tree_data.title = "Application";
initial_state.tree_data = tree_data;
AXTree tree;
ASSERT_TRUE(tree.Unserialize(initial_state)) << tree.error();
const AXNode* root_node = tree.root();
ASSERT_EQ(root.id, root_node->id());
EXPECT_THAT(
root_node->GetAllChildren(),
ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(paragraph_1_ignored),
HasAXNodeID(paragraph_2), HasAXNodeID(paragraph_3_ignored)));
EXPECT_EQ(4u, root_node->GetChildCount());
EXPECT_EQ(4u, root_node->GetChildCountCrossingTreeBoundary());
EXPECT_EQ(5u, root_node->GetUnignoredChildCount());
EXPECT_EQ(5u, root_node->GetUnignoredChildCountCrossingTreeBoundary());
EXPECT_EQ(paragraph_0.id, root_node->GetChildAtIndex(0)->id());
EXPECT_EQ(paragraph_1_ignored.id, root_node->GetChildAtIndex(1)->id());
EXPECT_EQ(paragraph_2.id, root_node->GetChildAtIndex(2)->id());
EXPECT_EQ(paragraph_3_ignored.id, root_node->GetChildAtIndex(3)->id());
EXPECT_EQ(nullptr, root_node->GetChildAtIndex(4));
EXPECT_EQ(paragraph_0.id,
root_node->GetChildAtIndexCrossingTreeBoundary(0)->id());
EXPECT_EQ(paragraph_1_ignored.id,
root_node->GetChildAtIndexCrossingTreeBoundary(1)->id());
EXPECT_EQ(paragraph_2.id,
root_node->GetChildAtIndexCrossingTreeBoundary(2)->id());
EXPECT_EQ(paragraph_3_ignored.id,
root_node->GetChildAtIndexCrossingTreeBoundary(3)->id());
EXPECT_EQ(nullptr, root_node->GetChildAtIndexCrossingTreeBoundary(4));
EXPECT_EQ(paragraph_0.id, root_node->GetUnignoredChildAtIndex(0)->id());
EXPECT_EQ(static_text_1_0.id, root_node->GetUnignoredChildAtIndex(1)->id());
EXPECT_EQ(paragraph_2.id, root_node->GetUnignoredChildAtIndex(2)->id());
EXPECT_EQ(static_text_3_0_0.id, root_node->GetUnignoredChildAtIndex(3)->id());
EXPECT_EQ(button_3_1.id, root_node->GetUnignoredChildAtIndex(4)->id());
EXPECT_EQ(nullptr, root_node->GetUnignoredChildAtIndex(5));
EXPECT_EQ(paragraph_0.id,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(0)->id());
EXPECT_EQ(static_text_1_0.id,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(1)->id());
EXPECT_EQ(paragraph_2.id,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(2)->id());
EXPECT_EQ(static_text_3_0_0.id,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(3)->id());
EXPECT_EQ(button_3_1.id,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(4)->id());
EXPECT_EQ(nullptr,
root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(5));
EXPECT_EQ(nullptr, root_node->GetParent());
EXPECT_EQ(nullptr, root_node->GetParentCrossingTreeBoundary());
EXPECT_EQ(nullptr, root_node->GetUnignoredParent());
EXPECT_EQ(nullptr, root_node->GetUnignoredParentCrossingTreeBoundary());
EXPECT_EQ(root_node, tree.GetFromId(paragraph_0.id)->GetParent());
EXPECT_EQ(root_node,
tree.GetFromId(paragraph_0.id)->GetParentCrossingTreeBoundary());
EXPECT_EQ(root_node, tree.GetFromId(paragraph_0.id)->GetUnignoredParent());
EXPECT_EQ(
root_node,
tree.GetFromId(paragraph_0.id)->GetUnignoredParentCrossingTreeBoundary());
EXPECT_EQ(tree.GetFromId(paragraph_1_ignored.id),
tree.GetFromId(static_text_1_0.id)->GetParent());
EXPECT_EQ(
tree.GetFromId(paragraph_1_ignored.id),
tree.GetFromId(static_text_1_0.id)->GetParentCrossingTreeBoundary());
EXPECT_EQ(root_node,
tree.GetFromId(static_text_1_0.id)->GetUnignoredParent());
EXPECT_EQ(root_node, tree.GetFromId(static_text_1_0.id)
->GetUnignoredParentCrossingTreeBoundary());
EXPECT_EQ(0u, root_node->GetIndexInParent());
EXPECT_EQ(0u, root_node->GetUnignoredIndexInParent());
EXPECT_EQ(2u, tree.GetFromId(paragraph_2.id)->GetIndexInParent());
EXPECT_EQ(1u,
tree.GetFromId(static_text_1_0.id)->GetUnignoredIndexInParent());
EXPECT_EQ(2u, tree.GetFromId(paragraph_2.id)->GetUnignoredIndexInParent());
EXPECT_EQ(paragraph_0.id, root_node->GetFirstChild()->id());
EXPECT_EQ(paragraph_0.id,
root_node->GetFirstChildCrossingTreeBoundary()->id());
EXPECT_EQ(paragraph_0.id, root_node->GetFirstUnignoredChild()->id());
EXPECT_EQ(paragraph_0.id,
root_node->GetFirstUnignoredChildCrossingTreeBoundary()->id());
EXPECT_EQ(paragraph_3_ignored.id, root_node->GetLastChild()->id());
EXPECT_EQ(paragraph_3_ignored.id,
root_node->GetLastChildCrossingTreeBoundary()->id());
EXPECT_EQ(button_3_1.id, root_node->GetLastUnignoredChild()->id());
EXPECT_EQ(button_3_1.id,
root_node->GetLastUnignoredChildCrossingTreeBoundary()->id());
EXPECT_EQ(static_text_0_0_ignored.id,
root_node->GetDeepestFirstChild()->id());
EXPECT_EQ(paragraph_0.id, root_node->GetDeepestFirstUnignoredChild()->id());
EXPECT_EQ(button_3_1.id, root_node->GetDeepestLastChild()->id());
EXPECT_EQ(button_3_1.id, root_node->GetDeepestLastUnignoredChild()->id());
{
std::vector<const AXNode*> siblings;
for (const AXNode* sibling = tree.GetFromId(paragraph_0.id); sibling;
sibling = sibling->GetNextSibling()) {
siblings.push_back(sibling);
}
EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
HasAXNodeID(paragraph_1_ignored),
HasAXNodeID(paragraph_2),
HasAXNodeID(paragraph_3_ignored)));
}
{
std::vector<const AXNode*> siblings;
for (const AXNode* sibling = tree.GetFromId(paragraph_0.id); sibling;
sibling = sibling->GetNextUnignoredSibling()) {
siblings.push_back(sibling);
}
EXPECT_THAT(
siblings,
ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
HasAXNodeID(button_3_1)));
}
{
std::vector<const AXNode*> siblings;
for (const AXNode* sibling = tree.GetFromId(paragraph_3_ignored.id);
sibling; sibling = sibling->GetPreviousSibling()) {
siblings.push_back(sibling);
}
EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_3_ignored),
HasAXNodeID(paragraph_2),
HasAXNodeID(paragraph_1_ignored),
HasAXNodeID(paragraph_0)));
}
{
std::vector<const AXNode*> siblings;
for (const AXNode* sibling = tree.GetFromId(button_3_1.id); sibling;
sibling = sibling->GetPreviousUnignoredSibling()) {
siblings.push_back(sibling);
}
EXPECT_THAT(
siblings,
ElementsAre(HasAXNodeID(button_3_1), HasAXNodeID(static_text_3_0_0),
HasAXNodeID(paragraph_2), HasAXNodeID(static_text_1_0),
HasAXNodeID(paragraph_0)));
}
{
std::vector<const AXNode::AllChildIterator> siblings;
for (auto iter = root_node->AllChildrenBegin();
iter != root_node->AllChildrenEnd(); ++iter) {
siblings.push_back(iter);
}
EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
HasAXNodeID(paragraph_1_ignored),
HasAXNodeID(paragraph_2),
HasAXNodeID(paragraph_3_ignored)));
}
{
std::vector<const AXNode::AllChildCrossingTreeBoundaryIterator> siblings;
for (auto iter = root_node->AllChildrenCrossingTreeBoundaryBegin();
iter != root_node->AllChildrenCrossingTreeBoundaryEnd(); ++iter) {
siblings.push_back(iter);
}
EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
HasAXNodeID(paragraph_1_ignored),
HasAXNodeID(paragraph_2),
HasAXNodeID(paragraph_3_ignored)));
}
{
std::vector<const AXNode::UnignoredChildIterator> siblings;
for (auto iter = root_node->UnignoredChildrenBegin();
iter != root_node->UnignoredChildrenEnd(); ++iter) {
siblings.push_back(iter);
}
EXPECT_THAT(
siblings,
ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
HasAXNodeID(button_3_1)));
}
{
std::vector<const AXNode::UnignoredChildCrossingTreeBoundaryIterator>
siblings;
for (auto iter = root_node->UnignoredChildrenCrossingTreeBoundaryBegin();
iter != root_node->UnignoredChildrenCrossingTreeBoundaryEnd();
++iter) {
siblings.push_back(iter);
}
EXPECT_THAT(
siblings,
ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
HasAXNodeID(button_3_1)));
}
}
TEST(AXNodeTest, TreeWalkingCrossingTreeBoundary) {
AXTreeData tree_data_1;
tree_data_1.tree_id = AXTreeID::CreateNewAXTreeID();
tree_data_1.title = "Application";
AXTreeData tree_data_2;
tree_data_2.tree_id = AXTreeID::CreateNewAXTreeID();
tree_data_2.parent_tree_id = tree_data_1.tree_id;
tree_data_2.title = "Iframe";
AXNodeData root_1;
AXNodeData root_2;
root_1.id = 1;
root_2.id = 1;
root_1.role = ax::mojom::Role::kRootWebArea;
root_1.AddChildTreeId(tree_data_2.tree_id);
root_2.role = ax::mojom::Role::kRootWebArea;
AXTreeUpdate initial_state_1;
initial_state_1.root_id = root_1.id;
initial_state_1.nodes = {root_1};
initial_state_1.has_tree_data = true;
initial_state_1.tree_data = tree_data_1;
AXTreeUpdate initial_state_2;
initial_state_2.root_id = root_2.id;
initial_state_2.nodes = {root_2};
initial_state_2.has_tree_data = true;
initial_state_2.tree_data = tree_data_2;
auto tree_1 = std::make_unique<AXTree>(initial_state_1);
TestAXTreeManager tree_manager_1(std::move(tree_1));
auto tree_2 = std::make_unique<AXTree>(initial_state_2);
TestAXTreeManager tree_manager_2(std::move(tree_2));
const AXNode* root_node_1 = tree_manager_1.GetRootAsAXNode();
ASSERT_EQ(root_1.id, root_node_1->id());
const AXNode* root_node_2 = tree_manager_2.GetRootAsAXNode();
ASSERT_EQ(root_2.id, root_node_2->id());
EXPECT_EQ(0u, root_node_1->GetChildCount());
EXPECT_EQ(1u, root_node_1->GetChildCountCrossingTreeBoundary());
EXPECT_EQ(0u, root_node_1->GetUnignoredChildCount());
EXPECT_EQ(1u, root_node_1->GetUnignoredChildCountCrossingTreeBoundary());
EXPECT_EQ(nullptr, root_node_1->GetChildAtIndex(0));
EXPECT_EQ(root_node_2, root_node_1->GetChildAtIndexCrossingTreeBoundary(0));
EXPECT_EQ(nullptr, root_node_1->GetUnignoredChildAtIndex(0));
EXPECT_EQ(root_node_2,
root_node_1->GetUnignoredChildAtIndexCrossingTreeBoundary(0));
EXPECT_EQ(nullptr, root_node_2->GetParent());
EXPECT_EQ(root_node_1, root_node_2->GetParentCrossingTreeBoundary());
EXPECT_EQ(nullptr, root_node_2->GetUnignoredParent());
EXPECT_EQ(root_node_1, root_node_2->GetUnignoredParentCrossingTreeBoundary());
}
TEST(AXNodeTest, GetValueForControlTextField) {
// kRootWebArea
// ++kTextField (contenteditable)
// ++++kGenericContainer
// ++++++kStaticText "Line 1"
// ++++++kLineBreak '\n'
// ++++++kStaticText "Line 2"
AXNodeData root;
root.id = 1;
AXNodeData rich_text_field;
rich_text_field.id = 2;
AXNodeData rich_text_field_text_container;
rich_text_field_text_container.id = 3;
AXNodeData rich_text_field_line_1;
rich_text_field_line_1.id = 4;
AXNodeData rich_text_field_line_break;
rich_text_field_line_break.id = 5;
AXNodeData rich_text_field_line_2;
rich_text_field_line_2.id = 6;
root.role = ax::mojom::Role::kRootWebArea;
root.child_ids = {rich_text_field.id};
rich_text_field.role = ax::mojom::Role::kTextField;
rich_text_field.AddState(ax::mojom::State::kEditable);
rich_text_field.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field.AddBoolAttribute(
ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
rich_text_field.SetName("Rich text field");
rich_text_field.child_ids = {rich_text_field_text_container.id};
rich_text_field_text_container.role = ax::mojom::Role::kGenericContainer;
rich_text_field_text_container.AddState(ax::mojom::State::kIgnored);
rich_text_field_text_container.AddState(ax::mojom::State::kEditable);
rich_text_field_text_container.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field_text_container.child_ids = {rich_text_field_line_1.id,
rich_text_field_line_break.id,
rich_text_field_line_2.id};
rich_text_field_line_1.role = ax::mojom::Role::kStaticText;
rich_text_field_line_1.AddState(ax::mojom::State::kEditable);
rich_text_field_line_1.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field_line_1.SetName("Line 1");
rich_text_field_line_break.role = ax::mojom::Role::kLineBreak;
rich_text_field_line_break.AddState(ax::mojom::State::kEditable);
rich_text_field_line_break.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field_line_break.SetName("\n");
rich_text_field_line_2.role = ax::mojom::Role::kStaticText;
rich_text_field_line_2.AddState(ax::mojom::State::kEditable);
rich_text_field_line_2.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field_line_2.SetName("Line 2");
AXTreeUpdate update;
update.has_tree_data = true;
update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
update.root_id = root.id;
update.nodes = {root,
rich_text_field,
rich_text_field_text_container,
rich_text_field_line_1,
rich_text_field_line_break,
rich_text_field_line_2};
AXTree tree(update);
{
const AXNode* text_field_node = tree.GetFromId(rich_text_field.id);
ASSERT_NE(nullptr, text_field_node);
EXPECT_EQ("Line 1\nLine 2", text_field_node->GetValueForControl());
}
// Only rich text fields should have their value attribute automatically
// computed from their inner text. Atomic text fields, such as <input> or
// <textarea> should not.
rich_text_field.RemoveState(ax::mojom::State::kRichlyEditable);
rich_text_field.RemoveBoolAttribute(
ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot);
AXTreeUpdate update_2;
update_2.nodes = {rich_text_field};
ASSERT_TRUE(tree.Unserialize(update_2)) << tree.error();
{
const AXNode* text_field_node = tree.GetFromId(rich_text_field.id);
ASSERT_NE(nullptr, text_field_node);
EXPECT_EQ("", text_field_node->GetValueForControl());
}
rich_text_field.AddState(ax::mojom::State::kRichlyEditable);
rich_text_field.AddBoolAttribute(
ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
// A node's data should override any computed node data.
rich_text_field.SetValue("Other value");
AXTreeUpdate update_3;
update_3.nodes = {rich_text_field};
ASSERT_TRUE(tree.Unserialize(update_3)) << tree.error();
{
const AXNode* text_field_node = tree.GetFromId(rich_text_field.id);
ASSERT_NE(nullptr, text_field_node);
EXPECT_EQ("Other value", text_field_node->GetValueForControl());
}
}
TEST(AXNodeTest, GetLowestPlatformAncestor) {
// ++kRootWebArea
// ++++kButton (IsLeaf=false)
// ++++++kGenericContainer ignored
// ++++++++kStaticText "Hello"
// ++++++++++kInlineTextBox "Hello" (IsLeaf=true)
// ++++kTextField "World" (IsLeaf=true)
// ++++++kStaticText "World"
// ++++++++kInlineTextBox "World" (IsLeaf=true)
AXNodeData root;
AXNodeData button;
AXNodeData generic_container;
AXNodeData static_text_1;
AXNodeData inline_box_1;
AXNodeData text_field;
AXNodeData static_text_2;
AXNodeData inline_box_2;
root.id = 1;
button.id = 2;
generic_container.id = 3;
static_text_1.id = 4;
inline_box_1.id = 5;
text_field.id = 6;
static_text_2.id = 7;
inline_box_2.id = 8;
root.role = ax::mojom::Role::kRootWebArea;
root.child_ids = {button.id, text_field.id};
button.role = ax::mojom::Role::kButton;
button.SetValue("Hello");
button.child_ids = {generic_container.id};
generic_container.role = ax::mojom::Role::kGenericContainer;
generic_container.AddState(ax::mojom::State::kIgnored);
generic_container.child_ids = {static_text_1.id};
static_text_1.role = ax::mojom::Role::kStaticText;
static_text_1.SetName("Hello");
static_text_1.child_ids = {inline_box_1.id};
inline_box_1.role = ax::mojom::Role::kInlineTextBox;
inline_box_1.SetName("Hello");
text_field.role = ax::mojom::Role::kTextField;
text_field.AddState(ax::mojom::State::kEditable);
text_field.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "input");
text_field.SetValue("World");
text_field.child_ids = {static_text_2.id};
static_text_2.role = ax::mojom::Role::kStaticText;
static_text_2.AddState(ax::mojom::State::kEditable);
static_text_2.SetName("World");
static_text_2.child_ids = {inline_box_2.id};
inline_box_2.role = ax::mojom::Role::kInlineTextBox;
inline_box_2.AddState(ax::mojom::State::kEditable);
inline_box_2.SetName("World");
AXTreeUpdate initial_state;
initial_state.root_id = root.id;
initial_state.nodes = {root, button, generic_container,
static_text_1, inline_box_1, text_field,
static_text_2, inline_box_2};
initial_state.has_tree_data = true;
AXTreeData tree_data;
tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
tree_data.title = "Application";
initial_state.tree_data = tree_data;
AXTree tree;
ASSERT_TRUE(tree.Unserialize(initial_state)) << tree.error();
const AXNode* root_node = tree.root();
ASSERT_EQ(root.id, root_node->id());
EXPECT_EQ(root_node, root_node->GetLowestPlatformAncestor());
const AXNode* button_node = root_node->children()[0];
ASSERT_EQ(button.id, button_node->id());
EXPECT_EQ(button_node, button_node->GetLowestPlatformAncestor());
const AXNode* generic_container_node = button_node->children()[0];
ASSERT_EQ(generic_container.id, generic_container_node->id());
EXPECT_EQ(button_node, generic_container_node->GetLowestPlatformAncestor());
const AXNode* static_text_1_node = generic_container_node->children()[0];
ASSERT_EQ(static_text_1.id, static_text_1_node->id());
EXPECT_EQ(static_text_1_node,
static_text_1_node->GetLowestPlatformAncestor());
const AXNode* inline_box_1_node = static_text_1_node->children()[0];
ASSERT_EQ(inline_box_1.id, inline_box_1_node->id());
EXPECT_EQ(static_text_1_node, inline_box_1_node->GetLowestPlatformAncestor());
const AXNode* text_field_node = root_node->children()[1];
ASSERT_EQ(text_field.id, text_field_node->id());
EXPECT_EQ(text_field_node, text_field_node->GetLowestPlatformAncestor());
const AXNode* static_text_2_node = text_field_node->children()[0];
ASSERT_EQ(static_text_2.id, static_text_2_node->id());
EXPECT_EQ(text_field_node, static_text_2_node->GetLowestPlatformAncestor());
const AXNode* inline_box_2_node = static_text_2_node->children()[0];
ASSERT_EQ(inline_box_2.id, inline_box_2_node->id());
EXPECT_EQ(text_field_node, inline_box_2_node->GetLowestPlatformAncestor());
}
} // namespace ui