summaryrefslogtreecommitdiff
path: root/chromium/ui/accessibility/ax_tree_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/accessibility/ax_tree_unittest.cc')
-rw-r--r--chromium/ui/accessibility/ax_tree_unittest.cc708
1 files changed, 516 insertions, 192 deletions
diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc
index 109eda97e8f..d92c2a6ac0c 100644
--- a/chromium/ui/accessibility/ax_tree_unittest.cc
+++ b/chromium/ui/accessibility/ax_tree_unittest.cc
@@ -16,6 +16,7 @@
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_serializable_tree.h"
+#include "ui/accessibility/ax_tree_observer.h"
#include "ui/accessibility/ax_tree_serializer.h"
#include "ui/gfx/transform.h"
@@ -54,11 +55,13 @@ bool IsNodeOffscreen(const AXTree& tree, int32_t id) {
return result;
}
-class FakeAXTreeDelegate : public AXTreeDelegate {
+class TestAXTreeObserver : public AXTreeObserver {
public:
- FakeAXTreeDelegate()
- : tree_data_changed_(false),
- root_changed_(false) {}
+ TestAXTreeObserver(AXTree* tree)
+ : tree_(tree), tree_data_changed_(false), root_changed_(false) {
+ tree_->AddObserver(this);
+ }
+ ~TestAXTreeObserver() final { tree_->RemoveObserver(this); }
void OnNodeDataWillChange(AXTree* tree,
const AXNodeData& old_node_data,
@@ -210,6 +213,7 @@ class FakeAXTreeDelegate : public AXTreeDelegate {
}
private:
+ AXTree* tree_;
bool tree_data_changed_;
bool root_changed_;
std::vector<int32_t> deleted_ids_;
@@ -431,29 +435,26 @@ TEST(AXTreeTest, NoReparentingOfRootIfNoNewRoot) {
update.node_id_to_clear = root.id;
update.nodes = {root, child1, child2};
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
ASSERT_TRUE(tree.Unserialize(update));
- EXPECT_EQ(0U, fake_delegate.deleted_ids().size());
- EXPECT_EQ(0U, fake_delegate.subtree_deleted_ids().size());
- EXPECT_EQ(0U, fake_delegate.created_ids().size());
-
- EXPECT_EQ(0U, fake_delegate.node_creation_finished_ids().size());
- EXPECT_EQ(0U, fake_delegate.subtree_creation_finished_ids().size());
- EXPECT_EQ(0U, fake_delegate.node_reparented_finished_ids().size());
+ EXPECT_EQ(0U, test_observer.deleted_ids().size());
+ EXPECT_EQ(0U, test_observer.subtree_deleted_ids().size());
+ EXPECT_EQ(0U, test_observer.created_ids().size());
- ASSERT_EQ(2U, fake_delegate.subtree_reparented_finished_ids().size());
- EXPECT_EQ(child1.id, fake_delegate.subtree_reparented_finished_ids()[0]);
- EXPECT_EQ(child2.id, fake_delegate.subtree_reparented_finished_ids()[1]);
+ EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
+ EXPECT_EQ(0U, test_observer.subtree_creation_finished_ids().size());
+ EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
- ASSERT_EQ(1U, fake_delegate.change_finished_ids().size());
- EXPECT_EQ(root.id, fake_delegate.change_finished_ids()[0]);
+ ASSERT_EQ(2U, test_observer.subtree_reparented_finished_ids().size());
+ EXPECT_EQ(child1.id, test_observer.subtree_reparented_finished_ids()[0]);
+ EXPECT_EQ(child2.id, test_observer.subtree_reparented_finished_ids()[1]);
- EXPECT_FALSE(fake_delegate.root_changed());
- EXPECT_FALSE(fake_delegate.tree_data_changed());
+ ASSERT_EQ(1U, test_observer.change_finished_ids().size());
+ EXPECT_EQ(root.id, test_observer.change_finished_ids()[0]);
- tree.SetDelegate(nullptr);
+ EXPECT_FALSE(test_observer.root_changed());
+ EXPECT_FALSE(test_observer.tree_data_changed());
}
TEST(AXTreeTest, ReparentRootIfRootChanged) {
@@ -484,39 +485,36 @@ TEST(AXTreeTest, ReparentRootIfRootChanged) {
update.node_id_to_clear = root.id;
update.nodes = {root2, child1, child2};
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
ASSERT_TRUE(tree.Unserialize(update));
- ASSERT_EQ(1U, fake_delegate.deleted_ids().size());
- EXPECT_EQ(root.id, fake_delegate.deleted_ids()[0]);
-
- ASSERT_EQ(1U, fake_delegate.subtree_deleted_ids().size());
- EXPECT_EQ(root.id, fake_delegate.subtree_deleted_ids()[0]);
+ ASSERT_EQ(1U, test_observer.deleted_ids().size());
+ EXPECT_EQ(root.id, test_observer.deleted_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.created_ids().size());
- EXPECT_EQ(root2.id, fake_delegate.created_ids()[0]);
+ ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
+ EXPECT_EQ(root.id, test_observer.subtree_deleted_ids()[0]);
- EXPECT_EQ(0U, fake_delegate.node_creation_finished_ids().size());
+ ASSERT_EQ(1U, test_observer.created_ids().size());
+ EXPECT_EQ(root2.id, test_observer.created_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.subtree_creation_finished_ids().size());
- EXPECT_EQ(root2.id, fake_delegate.subtree_creation_finished_ids()[0]);
+ EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
- ASSERT_EQ(2U, fake_delegate.node_reparented_finished_ids().size());
- EXPECT_EQ(child1.id, fake_delegate.node_reparented_finished_ids()[0]);
- EXPECT_EQ(child2.id, fake_delegate.node_reparented_finished_ids()[1]);
+ ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
+ EXPECT_EQ(root2.id, test_observer.subtree_creation_finished_ids()[0]);
- EXPECT_EQ(0U, fake_delegate.subtree_reparented_finished_ids().size());
+ ASSERT_EQ(2U, test_observer.node_reparented_finished_ids().size());
+ EXPECT_EQ(child1.id, test_observer.node_reparented_finished_ids()[0]);
+ EXPECT_EQ(child2.id, test_observer.node_reparented_finished_ids()[1]);
- EXPECT_EQ(0U, fake_delegate.change_finished_ids().size());
+ EXPECT_EQ(0U, test_observer.subtree_reparented_finished_ids().size());
- EXPECT_TRUE(fake_delegate.root_changed());
- EXPECT_FALSE(fake_delegate.tree_data_changed());
+ EXPECT_EQ(0U, test_observer.change_finished_ids().size());
- tree.SetDelegate(nullptr);
+ EXPECT_TRUE(test_observer.root_changed());
+ EXPECT_FALSE(test_observer.tree_data_changed());
}
-TEST(AXTreeTest, TreeDelegateIsCalled) {
+TEST(AXTreeTest, TreeObserverIsCalled) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(2);
@@ -533,34 +531,30 @@ TEST(AXTreeTest, TreeDelegateIsCalled) {
update.nodes[0].child_ids.push_back(4);
update.nodes[1].id = 4;
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
-
+ TestAXTreeObserver test_observer(&tree);
ASSERT_TRUE(tree.Unserialize(update));
- ASSERT_EQ(2U, fake_delegate.deleted_ids().size());
- EXPECT_EQ(1, fake_delegate.deleted_ids()[0]);
- EXPECT_EQ(2, fake_delegate.deleted_ids()[1]);
-
- ASSERT_EQ(1U, fake_delegate.subtree_deleted_ids().size());
- EXPECT_EQ(1, fake_delegate.subtree_deleted_ids()[0]);
+ ASSERT_EQ(2U, test_observer.deleted_ids().size());
+ EXPECT_EQ(1, test_observer.deleted_ids()[0]);
+ EXPECT_EQ(2, test_observer.deleted_ids()[1]);
- ASSERT_EQ(2U, fake_delegate.created_ids().size());
- EXPECT_EQ(3, fake_delegate.created_ids()[0]);
- EXPECT_EQ(4, fake_delegate.created_ids()[1]);
+ ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
+ EXPECT_EQ(1, test_observer.subtree_deleted_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.subtree_creation_finished_ids().size());
- EXPECT_EQ(3, fake_delegate.subtree_creation_finished_ids()[0]);
+ ASSERT_EQ(2U, test_observer.created_ids().size());
+ EXPECT_EQ(3, test_observer.created_ids()[0]);
+ EXPECT_EQ(4, test_observer.created_ids()[1]);
- ASSERT_EQ(1U, fake_delegate.node_creation_finished_ids().size());
- EXPECT_EQ(4, fake_delegate.node_creation_finished_ids()[0]);
+ ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
+ EXPECT_EQ(3, test_observer.subtree_creation_finished_ids()[0]);
- ASSERT_TRUE(fake_delegate.root_changed());
+ ASSERT_EQ(1U, test_observer.node_creation_finished_ids().size());
+ EXPECT_EQ(4, test_observer.node_creation_finished_ids()[0]);
- tree.SetDelegate(nullptr);
+ ASSERT_TRUE(test_observer.root_changed());
}
-TEST(AXTreeTest, TreeDelegateIsCalledForTreeDataChanges) {
+TEST(AXTreeTest, TreeObserverIsCalledForTreeDataChanges) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(1);
@@ -569,13 +563,12 @@ TEST(AXTreeTest, TreeDelegateIsCalledForTreeDataChanges) {
initial_state.tree_data.title = "Initial";
AXTree tree(initial_state);
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
// An empty update shouldn't change tree data.
AXTreeUpdate empty_update;
EXPECT_TRUE(tree.Unserialize(empty_update));
- EXPECT_FALSE(fake_delegate.tree_data_changed());
+ EXPECT_FALSE(test_observer.tree_data_changed());
EXPECT_EQ("Initial", tree.data().title);
// An update with tree data shouldn't change tree data if
@@ -583,7 +576,7 @@ TEST(AXTreeTest, TreeDelegateIsCalledForTreeDataChanges) {
AXTreeUpdate ignored_tree_data_update;
ignored_tree_data_update.tree_data.title = "Ignore Me";
EXPECT_TRUE(tree.Unserialize(ignored_tree_data_update));
- EXPECT_FALSE(fake_delegate.tree_data_changed());
+ EXPECT_FALSE(test_observer.tree_data_changed());
EXPECT_EQ("Initial", tree.data().title);
// An update with |has_tree_data| set should update the tree data.
@@ -591,10 +584,8 @@ TEST(AXTreeTest, TreeDelegateIsCalledForTreeDataChanges) {
tree_data_update.has_tree_data = true;
tree_data_update.tree_data.title = "New Title";
EXPECT_TRUE(tree.Unserialize(tree_data_update));
- EXPECT_TRUE(fake_delegate.tree_data_changed());
+ EXPECT_TRUE(test_observer.tree_data_changed());
EXPECT_EQ("New Title", tree.data().title);
-
- tree.SetDelegate(nullptr);
}
TEST(AXTreeTest, ReparentingDoesNotTriggerNodeCreated) {
@@ -607,9 +598,8 @@ TEST(AXTreeTest, ReparentingDoesNotTriggerNodeCreated) {
initial_state.nodes[1].child_ids.push_back(3);
initial_state.nodes[2].id = 3;
- FakeAXTreeDelegate fake_delegate;
AXTree tree(initial_state);
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
AXTreeUpdate update;
update.nodes.resize(2);
@@ -619,17 +609,17 @@ TEST(AXTreeTest, ReparentingDoesNotTriggerNodeCreated) {
update.nodes[0].child_ids.push_back(3);
update.nodes[1].id = 3;
EXPECT_TRUE(tree.Unserialize(update)) << tree.error();
- std::vector<int> created = fake_delegate.node_creation_finished_ids();
+ std::vector<int> created = test_observer.node_creation_finished_ids();
std::vector<int> subtree_reparented =
- fake_delegate.subtree_reparented_finished_ids();
+ test_observer.subtree_reparented_finished_ids();
std::vector<int> node_reparented =
- fake_delegate.node_reparented_finished_ids();
+ test_observer.node_reparented_finished_ids();
ASSERT_FALSE(base::ContainsValue(created, 3));
ASSERT_TRUE(base::ContainsValue(subtree_reparented, 3));
ASSERT_FALSE(base::ContainsValue(node_reparented, 3));
}
-TEST(AXTreeTest, TreeDelegateIsNotCalledForReparenting) {
+TEST(AXTreeTest, TreeObserverIsNotCalledForReparenting) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(2);
@@ -646,32 +636,29 @@ TEST(AXTreeTest, TreeDelegateIsNotCalledForReparenting) {
update.nodes[0].child_ids.push_back(4);
update.nodes[1].id = 4;
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
EXPECT_TRUE(tree.Unserialize(update));
- ASSERT_EQ(1U, fake_delegate.deleted_ids().size());
- EXPECT_EQ(1, fake_delegate.deleted_ids()[0]);
-
- ASSERT_EQ(1U, fake_delegate.subtree_deleted_ids().size());
- EXPECT_EQ(1, fake_delegate.subtree_deleted_ids()[0]);
+ ASSERT_EQ(1U, test_observer.deleted_ids().size());
+ EXPECT_EQ(1, test_observer.deleted_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.created_ids().size());
- EXPECT_EQ(4, fake_delegate.created_ids()[0]);
+ ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
+ EXPECT_EQ(1, test_observer.subtree_deleted_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.subtree_creation_finished_ids().size());
- EXPECT_EQ(4, fake_delegate.subtree_creation_finished_ids()[0]);
+ ASSERT_EQ(1U, test_observer.created_ids().size());
+ EXPECT_EQ(4, test_observer.created_ids()[0]);
- ASSERT_EQ(1U, fake_delegate.subtree_reparented_finished_ids().size());
- EXPECT_EQ(2, fake_delegate.subtree_reparented_finished_ids()[0]);
+ ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
+ EXPECT_EQ(4, test_observer.subtree_creation_finished_ids()[0]);
- EXPECT_EQ(0U, fake_delegate.node_creation_finished_ids().size());
- EXPECT_EQ(0U, fake_delegate.node_reparented_finished_ids().size());
+ ASSERT_EQ(1U, test_observer.subtree_reparented_finished_ids().size());
+ EXPECT_EQ(2, test_observer.subtree_reparented_finished_ids()[0]);
- ASSERT_TRUE(fake_delegate.root_changed());
+ EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
+ EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
- tree.SetDelegate(nullptr);
+ ASSERT_TRUE(test_observer.root_changed());
}
// UAF caught by ax_tree_fuzzer
@@ -728,8 +715,7 @@ TEST(AXTreeTest, RoleAndStateChangeCallbacks) {
initial_state.nodes[0].AddState(ax::mojom::State::kFocusable);
AXTree tree(initial_state);
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
// Change the role and state.
AXTreeUpdate update;
@@ -743,13 +729,11 @@ TEST(AXTreeTest, RoleAndStateChangeCallbacks) {
EXPECT_TRUE(tree.Unserialize(update));
const std::vector<std::string>& change_log =
- fake_delegate.attribute_change_log();
+ test_observer.attribute_change_log();
ASSERT_EQ(3U, change_log.size());
EXPECT_EQ("Role changed from button to checkBox", change_log[0]);
EXPECT_EQ("visited changed to true", change_log[1]);
EXPECT_EQ("checkedState changed from 2 to 1", change_log[2]);
-
- tree.SetDelegate(nullptr);
}
TEST(AXTreeTest, AttributeChangeCallbacks) {
@@ -776,8 +760,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) {
1);
AXTree tree(initial_state);
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
// Change existing attributes.
AXTreeUpdate update0;
@@ -801,7 +784,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) {
EXPECT_TRUE(tree.Unserialize(update0));
const std::vector<std::string>& change_log =
- fake_delegate.attribute_change_log();
+ test_observer.attribute_change_log();
ASSERT_EQ(9U, change_log.size());
EXPECT_EQ("name changed from N1 to N2", change_log[0]);
EXPECT_EQ("description changed from D1 to D2", change_log[1]);
@@ -813,8 +796,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) {
EXPECT_EQ("scrollX changed from 5 to 6", change_log[7]);
EXPECT_EQ("scrollXMin changed from 1 to 2", change_log[8]);
- FakeAXTreeDelegate fake_delegate2;
- tree.SetDelegate(&fake_delegate2);
+ TestAXTreeObserver test_observer2(&tree);
// Add and remove attributes.
AXTreeUpdate update1;
@@ -834,7 +816,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) {
EXPECT_TRUE(tree.Unserialize(update1));
const std::vector<std::string>& change_log2 =
- fake_delegate2.attribute_change_log();
+ test_observer2.attribute_change_log();
ASSERT_EQ(11U, change_log2.size());
EXPECT_EQ("name changed from N2 to ", change_log2[0]);
EXPECT_EQ("description changed from D2 to D3", change_log2[1]);
@@ -847,8 +829,6 @@ TEST(AXTreeTest, AttributeChangeCallbacks) {
EXPECT_EQ("scrollXMin changed from 2 to 0", change_log2[8]);
EXPECT_EQ("scrollX changed from 6 to 7", change_log2[9]);
EXPECT_EQ("scrollXMax changed from 0 to 10", change_log2[10]);
-
- tree.SetDelegate(nullptr);
}
TEST(AXTreeTest, IntListChangeCallbacks) {
@@ -872,8 +852,7 @@ TEST(AXTreeTest, IntListChangeCallbacks) {
ax::mojom::IntListAttribute::kRadioGroupIds, two);
AXTree tree(initial_state);
- FakeAXTreeDelegate fake_delegate;
- tree.SetDelegate(&fake_delegate);
+ TestAXTreeObserver test_observer(&tree);
// Change existing attributes.
AXTreeUpdate update0;
@@ -887,13 +866,12 @@ TEST(AXTreeTest, IntListChangeCallbacks) {
EXPECT_TRUE(tree.Unserialize(update0));
const std::vector<std::string>& change_log =
- fake_delegate.attribute_change_log();
+ test_observer.attribute_change_log();
ASSERT_EQ(2U, change_log.size());
EXPECT_EQ("controlsIds changed from 1 to 2,2", change_log[0]);
EXPECT_EQ("radioGroupIds changed from 2,2 to 3", change_log[1]);
- FakeAXTreeDelegate fake_delegate2;
- tree.SetDelegate(&fake_delegate2);
+ TestAXTreeObserver test_observer2(&tree);
// Add and remove attributes.
AXTreeUpdate update1;
@@ -907,13 +885,11 @@ TEST(AXTreeTest, IntListChangeCallbacks) {
EXPECT_TRUE(tree.Unserialize(update1));
const std::vector<std::string>& change_log2 =
- fake_delegate2.attribute_change_log();
+ test_observer2.attribute_change_log();
ASSERT_EQ(3U, change_log2.size());
EXPECT_EQ("controlsIds changed from 2,2 to ", change_log2[0]);
EXPECT_EQ("radioGroupIds changed from 3 to 2,2", change_log2[1]);
EXPECT_EQ("flowtoIds changed from to 3", change_log2[2]);
-
- tree.SetDelegate(nullptr);
}
// Create a very simple tree and make sure that we can get the bounds of
@@ -1420,6 +1396,10 @@ TEST(AXTreeTest, SkipIgnoredNodes) {
}
TEST(AXTreeTest, ChildTreeIds) {
+ ui::AXTreeID tree_id_1 = ui::AXTreeID::CreateNewAXTreeID();
+ ui::AXTreeID tree_id_2 = ui::AXTreeID::CreateNewAXTreeID();
+ ui::AXTreeID tree_id_3 = ui::AXTreeID::CreateNewAXTreeID();
+
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(4);
@@ -1429,50 +1409,45 @@ TEST(AXTreeTest, ChildTreeIds) {
initial_state.nodes[0].child_ids.push_back(4);
initial_state.nodes[1].id = 2;
initial_state.nodes[1].AddStringAttribute(
- ax::mojom::StringAttribute::kChildTreeId, "92");
+ ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
initial_state.nodes[2].id = 3;
initial_state.nodes[2].AddStringAttribute(
- ax::mojom::StringAttribute::kChildTreeId, "93");
+ ax::mojom::StringAttribute::kChildTreeId, tree_id_3.ToString());
initial_state.nodes[3].id = 4;
initial_state.nodes[3].AddStringAttribute(
- ax::mojom::StringAttribute::kChildTreeId, "93");
+ ax::mojom::StringAttribute::kChildTreeId, tree_id_3.ToString());
AXTree tree(initial_state);
- auto child_tree_91_nodes =
- tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("91"));
- EXPECT_EQ(0U, child_tree_91_nodes.size());
+ auto child_tree_1_nodes = tree.GetNodeIdsForChildTreeId(tree_id_1);
+ EXPECT_EQ(0U, child_tree_1_nodes.size());
- auto child_tree_92_nodes =
- tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("92"));
- EXPECT_EQ(1U, child_tree_92_nodes.size());
- EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 2));
+ auto child_tree_2_nodes = tree.GetNodeIdsForChildTreeId(tree_id_2);
+ EXPECT_EQ(1U, child_tree_2_nodes.size());
+ EXPECT_TRUE(base::ContainsKey(child_tree_2_nodes, 2));
- auto child_tree_93_nodes =
- tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("93"));
- EXPECT_EQ(2U, child_tree_93_nodes.size());
- EXPECT_TRUE(base::ContainsKey(child_tree_93_nodes, 3));
- EXPECT_TRUE(base::ContainsKey(child_tree_93_nodes, 4));
+ auto child_tree_3_nodes = tree.GetNodeIdsForChildTreeId(tree_id_3);
+ EXPECT_EQ(2U, child_tree_3_nodes.size());
+ EXPECT_TRUE(base::ContainsKey(child_tree_3_nodes, 3));
+ EXPECT_TRUE(base::ContainsKey(child_tree_3_nodes, 4));
AXTreeUpdate update = initial_state;
update.nodes[2].string_attributes.clear();
update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
- "92");
+ tree_id_2.ToString());
update.nodes[3].string_attributes.clear();
EXPECT_TRUE(tree.Unserialize(update));
- child_tree_92_nodes =
- tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("92"));
- EXPECT_EQ(2U, child_tree_92_nodes.size());
- EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 2));
- EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 3));
+ child_tree_2_nodes = tree.GetNodeIdsForChildTreeId(tree_id_2);
+ EXPECT_EQ(2U, child_tree_2_nodes.size());
+ EXPECT_TRUE(base::ContainsKey(child_tree_2_nodes, 2));
+ EXPECT_TRUE(base::ContainsKey(child_tree_2_nodes, 3));
- child_tree_93_nodes =
- tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("93"));
- EXPECT_EQ(0U, child_tree_93_nodes.size());
+ child_tree_3_nodes = tree.GetNodeIdsForChildTreeId(tree_id_3);
+ EXPECT_EQ(0U, child_tree_3_nodes.size());
}
-// Tests PosInSet and SetSize int attributes work if assigned.
+// Tests GetPosInSet and GetSetSize return the assigned int attribute values.
TEST(AXTreeTest, TestSetSizePosInSetAssigned) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1505,7 +1480,7 @@ TEST(AXTreeTest, TestSetSizePosInSetAssigned) {
EXPECT_EQ(item3->GetSetSize(), 12);
}
-// Tests that PosInSet and SetSize can be calculated if not assigned.
+// Tests that pos_in_set and set_size can be calculated if not assigned.
TEST(AXTreeTest, TestSetSizePosInSetUnassigned) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1532,7 +1507,8 @@ TEST(AXTreeTest, TestSetSizePosInSetUnassigned) {
EXPECT_EQ(item3->GetSetSize(), 3);
}
-// Tests PosInSet unassigned, while SetSize assigned in container.
+// Tests pos_in_set can be calculated if unassigned, and set_size can be
+// assigned on the outerlying ordered set.
TEST(AXTreeTest, TestSetSizeAssignedInContainer) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1549,7 +1525,7 @@ TEST(AXTreeTest, TestSetSizeAssignedInContainer) {
tree_update.nodes[3].role = ax::mojom::Role::kListItem;
AXTree tree(tree_update);
- // Items should inherit SetSize from container if not specified.
+ // Items should inherit set_size from ordered set if not specified.
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetSetSize(), 7);
AXNode* item2 = tree.GetFromId(3);
@@ -1558,60 +1534,48 @@ TEST(AXTreeTest, TestSetSizeAssignedInContainer) {
EXPECT_EQ(item3->GetSetSize(), 7);
}
-// Tests PosInSet and SetSize on a list containing various roles.
-// Roles for items and associated container should match up.
+// Tests GetPosInSet and GetSetSize on a list containing various roles.
+// Roles for items and associated ordered set should match up.
TEST(AXTreeTest, TestSetSizePosInSetDiverseList) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
- tree_update.nodes.resize(9);
+ tree_update.nodes.resize(6);
tree_update.nodes[0].id = 1;
- tree_update.nodes[0].role = ax::mojom::Role::kList;
- tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6, 7, 8, 9};
+ tree_update.nodes[0].role = ax::mojom::Role::kMenu;
+ tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
tree_update.nodes[1].id = 2;
- tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
+ tree_update.nodes[1].role = ax::mojom::Role::kMenuItem; // 1 of 4
tree_update.nodes[2].id = 3;
- tree_update.nodes[2].role = ax::mojom::Role::kMenuItem; // 0 of 0
+ tree_update.nodes[2].role = ax::mojom::Role::kMenuItemCheckBox; // 2 of 4
tree_update.nodes[3].id = 4;
- tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 2 of 3
+ tree_update.nodes[3].role = ax::mojom::Role::kMenuItemRadio; // 3 of 4
tree_update.nodes[4].id = 5;
- tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 0 of 0
+ tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 4 of 4
tree_update.nodes[5].id = 6;
- tree_update.nodes[5].role = ax::mojom::Role::kArticle; // 0 of 0
- tree_update.nodes[6].id = 7;
- tree_update.nodes[6].role = ax::mojom::Role::kArticle; // 0 of 0
- tree_update.nodes[7].id = 8;
- tree_update.nodes[7].role = ax::mojom::Role::kListItem; // 3 of 3
- tree_update.nodes[8].id = 9;
- tree_update.nodes[8].role = ax::mojom::Role::kImage; // 0 of 0
+ tree_update.nodes[5].role = ax::mojom::Role::kTab; // 0 of 0
AXTree tree(tree_update);
- AXNode* listitem1 = tree.GetFromId(2);
- EXPECT_EQ(listitem1->GetPosInSet(), 1);
- EXPECT_EQ(listitem1->GetSetSize(), 3);
- AXNode* menuitem1 = tree.GetFromId(3);
- EXPECT_EQ(menuitem1->GetPosInSet(), 0);
- EXPECT_EQ(menuitem1->GetSetSize(), 0);
- AXNode* listitem2 = tree.GetFromId(4);
- EXPECT_EQ(listitem2->GetPosInSet(), 2);
- EXPECT_EQ(listitem2->GetSetSize(), 3);
- AXNode* menuitem2 = tree.GetFromId(5);
- EXPECT_EQ(menuitem2->GetPosInSet(), 0);
- EXPECT_EQ(menuitem2->GetSetSize(), 0);
- AXNode* article1 = tree.GetFromId(6);
- EXPECT_EQ(article1->GetPosInSet(), 0);
- EXPECT_EQ(article1->GetSetSize(), 0);
- AXNode* article2 = tree.GetFromId(7);
- EXPECT_EQ(article2->GetPosInSet(), 0);
- EXPECT_EQ(article2->GetSetSize(), 0);
- AXNode* listitem3 = tree.GetFromId(8);
- EXPECT_EQ(listitem3->GetPosInSet(), 3);
- EXPECT_EQ(listitem3->GetSetSize(), 3);
- AXNode* image = tree.GetFromId(9);
+ // kMenu is allowed to contain: kMenuItem, kMenuItemCheckbox,
+ // and kMenuItemRadio. For PosInSet and SetSize purposes, these items
+ // are treated as the same role.
+ AXNode* item1 = tree.GetFromId(2);
+ EXPECT_EQ(item1->GetPosInSet(), 1);
+ EXPECT_EQ(item1->GetSetSize(), 4);
+ AXNode* checkbox = tree.GetFromId(3);
+ EXPECT_EQ(checkbox->GetPosInSet(), 2);
+ EXPECT_EQ(checkbox->GetSetSize(), 4);
+ AXNode* radio = tree.GetFromId(4);
+ EXPECT_EQ(radio->GetPosInSet(), 3);
+ EXPECT_EQ(radio->GetSetSize(), 4);
+ AXNode* item3 = tree.GetFromId(5);
+ EXPECT_EQ(item3->GetPosInSet(), 4);
+ EXPECT_EQ(item3->GetSetSize(), 4);
+ AXNode* image = tree.GetFromId(6);
EXPECT_EQ(image->GetPosInSet(), 0);
EXPECT_EQ(image->GetSetSize(), 0);
}
-// Tests PosInSet and SetSize on a nested list.
+// Tests GetPosInSet and GetSetSize on a nested list.
TEST(AXTreeTest, TestSetSizePosInSetNestedList) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1641,12 +1605,6 @@ TEST(AXTreeTest, TestSetSizePosInSetNestedList) {
EXPECT_EQ(outer_item2->GetPosInSet(), 2);
EXPECT_EQ(outer_item2->GetSetSize(), 3);
- // List object itself should not report posinset or setsize.
- // TODO (akihiroota): Lists should report setsize in the future.
- AXNode* inner_list = tree.GetFromId(4);
- EXPECT_EQ(inner_list->GetPosInSet(), 0);
- EXPECT_EQ(inner_list->GetSetSize(), 0);
-
AXNode* inner_item1 = tree.GetFromId(5);
EXPECT_EQ(inner_item1->GetPosInSet(), 1);
EXPECT_EQ(inner_item1->GetSetSize(), 2);
@@ -1659,8 +1617,8 @@ TEST(AXTreeTest, TestSetSizePosInSetNestedList) {
EXPECT_EQ(outer_item3->GetSetSize(), 3);
}
-// Tests PosInSet can be calculated if one item specifies PosInSet, but others
-// are missing.
+// Tests pos_in_set can be calculated if one item specifies pos_in_set, but
+// other assignments are missing.
TEST(AXTreeTest, TestPosInSetMissing) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1691,7 +1649,7 @@ TEST(AXTreeTest, TestPosInSetMissing) {
EXPECT_EQ(item3->GetSetSize(), 20);
}
-// A more difficult test that invovles missing PosInSet and SetSize values.
+// A more difficult test that invovles missing pos_in_set and set_size values.
TEST(AXTreeTest, TestSetSizePosInSetMissingDifficult) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1732,7 +1690,7 @@ TEST(AXTreeTest, TestSetSizePosInSetMissingDifficult) {
EXPECT_EQ(item5->GetSetSize(), 11);
}
-// Tests that code overwrites decreasing SetSize assignments to largest of
+// Tests that code overwrites decreasing set_size assignments to largest of
// assigned values.
TEST(AXTreeTest, TestSetSizeDecreasing) {
AXTreeUpdate tree_update;
@@ -1762,7 +1720,7 @@ TEST(AXTreeTest, TestSetSizeDecreasing) {
EXPECT_EQ(item3->GetSetSize(), 5);
}
-// Tests that code overwrites decreasing PosInSet values.
+// Tests that code overwrites decreasing pos_in_set values.
TEST(AXTreeTest, TestPosInSetDecreasing) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
@@ -1791,7 +1749,7 @@ TEST(AXTreeTest, TestPosInSetDecreasing) {
EXPECT_EQ(item3->GetSetSize(), 8);
}
-// Tests that code overwrites duplicate PosInSet values. Note this case is
+// Tests that code overwrites duplicate pos_in_set values. Note this case is
// tricky; an update to the second element causes an update to the third
// element.
TEST(AXTreeTest, TestPosInSetDuplicates) {
@@ -1823,7 +1781,7 @@ TEST(AXTreeTest, TestPosInSetDuplicates) {
EXPECT_EQ(item3->GetSetSize(), 8);
}
-// Tests PosInSet and SetSize when some list items are nested in a generic
+// Tests GetPosInSet and GetSetSize when some list items are nested in a generic
// container.
TEST(AXTreeTest, TestSetSizePosInSetNestedContainer) {
AXTreeUpdate tree_update;
@@ -1868,4 +1826,370 @@ TEST(AXTreeTest, TestSetSizePosInSetNestedContainer) {
EXPECT_EQ(item4->GetSetSize(), 4);
}
+// Tests GetSetSize and GetPosInSet are correct, even when list items change.
+// This test is directed at the caching functionality of pos_in_set and
+// set_size. Tests that previously calculated values are not used after
+// tree is updated.
+TEST(AXTreeTest, TestSetSizePosInSetDeleteItem) {
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(4);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kList;
+ initial_state.nodes[0].child_ids = {2, 3, 4};
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 3
+ AXTree tree(initial_state);
+
+ AXNode* item1 = tree.GetFromId(2);
+ EXPECT_EQ(item1->GetPosInSet(), 1);
+ EXPECT_EQ(item1->GetSetSize(), 3);
+ AXNode* item2 = tree.GetFromId(3);
+ EXPECT_EQ(item2->GetPosInSet(), 2);
+ EXPECT_EQ(item2->GetSetSize(), 3);
+ AXNode* item3 = tree.GetFromId(4);
+ EXPECT_EQ(item3->GetPosInSet(), 3);
+ EXPECT_EQ(item3->GetSetSize(), 3);
+
+ // TreeUpdates only need to describe what changed in tree.
+ AXTreeUpdate update = initial_state;
+ update.nodes.resize(1);
+ update.nodes[0].child_ids = {2, 4}; // Delete item 2 of 3 from list.
+ EXPECT_TRUE(tree.Unserialize(update));
+
+ AXNode* new_item1 = tree.GetFromId(2);
+ EXPECT_EQ(new_item1->GetPosInSet(), 1);
+ EXPECT_EQ(new_item1->GetSetSize(), 2);
+ AXNode* new_item2 = tree.GetFromId(4);
+ EXPECT_EQ(new_item2->GetPosInSet(), 2);
+ EXPECT_EQ(new_item2->GetSetSize(), 2);
+}
+
+// Tests GetSetSize and GetPosInSet are correct, even when list items change.
+// This test adds an item to the front of a list, which invalidates previously
+// calculated pos_in_set and set_size values. Tests that old values are not
+// used after tree is updated.
+TEST(AXTreeTest, TestSetSizePosInSetAddItem) {
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(4);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kList;
+ initial_state.nodes[0].child_ids = {2, 3, 4};
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 3
+ AXTree tree(initial_state);
+
+ AXNode* item1 = tree.GetFromId(2);
+ EXPECT_EQ(item1->GetPosInSet(), 1);
+ EXPECT_EQ(item1->GetSetSize(), 3);
+ AXNode* item2 = tree.GetFromId(3);
+ EXPECT_EQ(item2->GetPosInSet(), 2);
+ EXPECT_EQ(item2->GetSetSize(), 3);
+ AXNode* item3 = tree.GetFromId(4);
+ EXPECT_EQ(item3->GetPosInSet(), 3);
+ EXPECT_EQ(item3->GetSetSize(), 3);
+
+ // Insert item at beginning of list
+ AXTreeUpdate update = initial_state;
+ update.nodes.resize(2);
+ update.nodes[0].id = 1;
+ update.nodes[0].child_ids = {5, 2, 3, 4};
+ update.nodes[1].id = 5;
+ update.nodes[1].role = ax::mojom::Role::kListItem;
+ EXPECT_TRUE(tree.Unserialize(update));
+
+ AXNode* new_item1 = tree.GetFromId(5);
+ EXPECT_EQ(new_item1->GetPosInSet(), 1);
+ EXPECT_EQ(new_item1->GetSetSize(), 4);
+ AXNode* new_item2 = tree.GetFromId(2);
+ EXPECT_EQ(new_item2->GetPosInSet(), 2);
+ EXPECT_EQ(new_item2->GetSetSize(), 4);
+ AXNode* new_item3 = tree.GetFromId(3);
+ EXPECT_EQ(new_item3->GetPosInSet(), 3);
+ EXPECT_EQ(new_item3->GetSetSize(), 4);
+ AXNode* new_item4 = tree.GetFromId(4);
+ EXPECT_EQ(new_item4->GetPosInSet(), 4);
+ EXPECT_EQ(new_item4->GetSetSize(), 4);
+}
+
+// Tests that the outerlying ordered set reports a set_size. Ordered sets
+// should not report a pos_in_set value other than 0, since they are not
+// considered to be items within a set (even when nested).
+TEST(AXTreeTest, TestOrderedSetReportsSetSize) {
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(12);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].role = ax::mojom::Role::kList; // set_size = 3
+ tree_update.nodes[0].child_ids = {2, 3, 4, 7, 8, 9, 12};
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
+ tree_update.nodes[2].id = 3;
+ tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
+ tree_update.nodes[3].id = 4;
+ tree_update.nodes[3].role = ax::mojom::Role::kList; // set_size = 2
+ tree_update.nodes[3].child_ids = {5, 6};
+ tree_update.nodes[4].id = 5;
+ tree_update.nodes[4].role = ax::mojom::Role::kListItem; // 1 of 2
+ tree_update.nodes[5].id = 6;
+ tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 2 of 2
+ tree_update.nodes[6].id = 7;
+ tree_update.nodes[6].role = ax::mojom::Role::kListItem; // 3 of 3
+ tree_update.nodes[7].id = 8;
+ tree_update.nodes[7].role = ax::mojom::Role::kList; // set_size = 0
+ tree_update.nodes[8].id = 9;
+ tree_update.nodes[8].role =
+ ax::mojom::Role::kList; // set_size = 1 because only 1 item whose role
+ // matches
+ tree_update.nodes[8].child_ids = {10, 11};
+ tree_update.nodes[9].id = 10;
+ tree_update.nodes[9].role = ax::mojom::Role::kArticle;
+ tree_update.nodes[10].id = 11;
+ tree_update.nodes[10].role = ax::mojom::Role::kListItem;
+ tree_update.nodes[11].id = 12;
+ tree_update.nodes[11].role = ax::mojom::Role::kList;
+ tree_update.nodes[11].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
+ AXTree tree(tree_update);
+
+ AXNode* outer_list = tree.GetFromId(1);
+ EXPECT_EQ(outer_list->GetPosInSet(), 0); // Ordered sets have pos of 0
+ EXPECT_EQ(outer_list->GetSetSize(), 3);
+ AXNode* outer_list_item1 = tree.GetFromId(2);
+ EXPECT_EQ(outer_list_item1->GetPosInSet(), 1);
+ EXPECT_EQ(outer_list_item1->GetSetSize(), 3);
+ AXNode* outer_list_item2 = tree.GetFromId(3);
+ EXPECT_EQ(outer_list_item2->GetPosInSet(), 2);
+ EXPECT_EQ(outer_list_item2->GetSetSize(), 3);
+ AXNode* outer_list_item3 = tree.GetFromId(7);
+ EXPECT_EQ(outer_list_item3->GetPosInSet(), 3);
+ EXPECT_EQ(outer_list_item3->GetSetSize(), 3);
+
+ AXNode* inner_list1 = tree.GetFromId(4);
+ EXPECT_EQ(inner_list1->GetPosInSet(),
+ 0); // Ordered sets have pos of 0, even when nested
+ EXPECT_EQ(inner_list1->GetSetSize(), 2);
+ AXNode* inner_list1_item1 = tree.GetFromId(5);
+ EXPECT_EQ(inner_list1_item1->GetPosInSet(), 1);
+ EXPECT_EQ(inner_list1_item1->GetSetSize(), 2);
+ AXNode* inner_list1_item2 = tree.GetFromId(6);
+ EXPECT_EQ(inner_list1_item2->GetPosInSet(), 2);
+ EXPECT_EQ(inner_list1_item2->GetSetSize(), 2);
+
+ AXNode* inner_list2 = tree.GetFromId(8); // Empty list
+ EXPECT_EQ(inner_list2->GetPosInSet(), 0);
+ EXPECT_EQ(inner_list2->GetSetSize(), 0);
+
+ AXNode* inner_list3 = tree.GetFromId(9);
+ EXPECT_EQ(inner_list3->GetPosInSet(), 0);
+ EXPECT_EQ(inner_list3->GetSetSize(), 1); // Only 1 item whose role matches
+ AXNode* inner_list3_article1 = tree.GetFromId(10);
+ EXPECT_EQ(inner_list3_article1->GetPosInSet(), 0);
+ EXPECT_EQ(inner_list3_article1->GetSetSize(), 0);
+ AXNode* inner_list3_item1 = tree.GetFromId(11);
+ EXPECT_EQ(inner_list3_item1->GetPosInSet(), 1);
+ EXPECT_EQ(inner_list3_item1->GetSetSize(), 1);
+
+ AXNode* inner_list4 = tree.GetFromId(12);
+ EXPECT_EQ(inner_list4->GetPosInSet(), 0);
+ // Even though list is empty, kSetSize attribute was set, so it takes
+ // precedence
+ EXPECT_EQ(inner_list4->GetSetSize(), 5);
+}
+
+// Tests GetPosInSet and GetSetSize code on invalid input.
+TEST(AXTreeTest, TestSetSizePosInSetInvalid) {
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(3);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].role = ax::mojom::Role::kListItem; // 0 of 0
+ tree_update.nodes[0].child_ids = {2, 3};
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kListItem;
+ tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
+ 4); // 0 of 0
+ tree_update.nodes[2].id = 3;
+ tree_update.nodes[2].role = ax::mojom::Role::kListItem;
+ AXTree tree(tree_update);
+
+ AXNode* item1 = tree.GetFromId(1);
+ EXPECT_EQ(item1->GetPosInSet(), 0);
+ EXPECT_EQ(item1->GetSetSize(), 0);
+ AXNode* item2 = tree.GetFromId(2);
+ EXPECT_EQ(item2->GetPosInSet(), 0);
+ EXPECT_EQ(item2->GetSetSize(), 0);
+ AXNode* item3 = tree.GetFromId(3);
+ EXPECT_EQ(item3->GetPosInSet(), 0);
+ EXPECT_EQ(item3->GetSetSize(), 0);
+}
+
+// Tests GetPosInSet and GetSetSize code on kRadioButtons. Radio buttons
+// behave differently than other item-like elements; most notably, they do not
+// need to be contained within an ordered set to report a PosInSet or SetSize.
+TEST(AXTreeTest, TestSetSizePosInSetRadioButtons) {
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(13);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].child_ids = {2, 3, 4, 10, 13};
+
+ // Radio buttons are not required to be contained within an ordered set.
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kRadioButton; // 1 of 5
+ tree_update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "sports");
+ tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 1);
+ tree_update.nodes[2].id = 3;
+ tree_update.nodes[2].role = ax::mojom::Role::kRadioButton; // 2 of 5
+ tree_update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "books");
+ tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 2);
+ tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
+
+ // Radio group with nested generic container.
+ tree_update.nodes[3].id = 4;
+ tree_update.nodes[3].role = ax::mojom::Role::kRadioGroup; // setsize = 4
+ tree_update.nodes[3].child_ids = {5, 6, 7};
+ tree_update.nodes[4].id = 5;
+ tree_update.nodes[4].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[4].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "recipes"); // 1 of 4
+ tree_update.nodes[5].id = 6;
+ tree_update.nodes[5].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[5].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "recipes"); // 2 of 4
+ tree_update.nodes[6].id = 7;
+ tree_update.nodes[6].role = ax::mojom::Role::kGenericContainer;
+ tree_update.nodes[6].child_ids = {8, 9};
+ tree_update.nodes[7].id = 8;
+ tree_update.nodes[7].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[7].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "recipes"); // 3 of 4
+ tree_update.nodes[8].id = 9;
+ tree_update.nodes[8].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[8].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "recipes"); // 4 of 4
+
+ // Radio buttons are allowed to be contained within forms.
+ tree_update.nodes[9].id = 10;
+ tree_update.nodes[9].role = ax::mojom::Role::kForm;
+ tree_update.nodes[9].child_ids = {11, 12};
+ tree_update.nodes[10].id = 11;
+ tree_update.nodes[10].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[10].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "cities"); // 1 of 2
+ tree_update.nodes[11].id = 12;
+ tree_update.nodes[11].role = ax::mojom::Role::kRadioButton;
+ tree_update.nodes[11].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "cities"); // 2 of 2
+ tree_update.nodes[12].id = 13;
+ tree_update.nodes[12].role = ax::mojom::Role::kRadioButton; // 4 of 5
+ tree_update.nodes[12].AddStringAttribute(ax::mojom::StringAttribute::kName,
+ "sports");
+ tree_update.nodes[12].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 4);
+
+ AXTree tree(tree_update);
+
+ AXNode* sports_button1 = tree.GetFromId(2);
+ EXPECT_EQ(sports_button1->GetPosInSet(), 1);
+ EXPECT_EQ(sports_button1->GetSetSize(), 5);
+ AXNode* books_button = tree.GetFromId(3);
+ EXPECT_EQ(books_button->GetPosInSet(), 2);
+ EXPECT_EQ(books_button->GetSetSize(), 5);
+
+ AXNode* radiogroup1 = tree.GetFromId(4);
+ EXPECT_EQ(radiogroup1->GetPosInSet(), 0);
+ EXPECT_EQ(radiogroup1->GetSetSize(), 4);
+ AXNode* recipes_button1 = tree.GetFromId(5);
+ EXPECT_EQ(recipes_button1->GetPosInSet(), 1);
+ EXPECT_EQ(recipes_button1->GetSetSize(), 4);
+ AXNode* recipes_button2 = tree.GetFromId(6);
+ EXPECT_EQ(recipes_button2->GetPosInSet(), 2);
+ EXPECT_EQ(recipes_button2->GetSetSize(), 4);
+
+ AXNode* generic_container = tree.GetFromId(7);
+ EXPECT_EQ(generic_container->GetPosInSet(), 0);
+ EXPECT_EQ(generic_container->GetSetSize(), 0);
+ AXNode* recipes_button3 = tree.GetFromId(8);
+ EXPECT_EQ(recipes_button3->GetPosInSet(), 3);
+ EXPECT_EQ(recipes_button3->GetSetSize(), 4);
+ AXNode* recipes_button4 = tree.GetFromId(9);
+ EXPECT_EQ(recipes_button4->GetPosInSet(), 4);
+ EXPECT_EQ(recipes_button4->GetSetSize(), 4);
+
+ // Elements with role kForm shouldn't report posinset or setsize
+ AXNode* form = tree.GetFromId(10);
+ EXPECT_EQ(form->GetPosInSet(), 0);
+ EXPECT_EQ(form->GetSetSize(), 0);
+ AXNode* cities_button1 = tree.GetFromId(11);
+ EXPECT_EQ(cities_button1->GetPosInSet(), 1);
+ EXPECT_EQ(cities_button1->GetSetSize(), 2);
+ AXNode* cities_button2 = tree.GetFromId(12);
+ EXPECT_EQ(cities_button2->GetPosInSet(), 2);
+ EXPECT_EQ(cities_button2->GetSetSize(), 2);
+
+ AXNode* sports_button2 = tree.GetFromId(13);
+ EXPECT_EQ(sports_button2->GetPosInSet(), 4);
+ EXPECT_EQ(sports_button2->GetSetSize(), 5);
+}
+
+// Tests GetPosInSet and GetSetSize on a list that includes radio buttons.
+// Note that radio buttons do not contribute to the SetSize of the outerlying
+// list.
+TEST(AXTreeTest, TestSetSizePosInSetRadioButtonsInList) {
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(6);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].role =
+ ax::mojom::Role::kList; // set_size = 2, since only contains 2 ListItems
+ tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
+
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kRadioButton; // 1 of 3
+ tree_update.nodes[2].id = 3;
+ tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 1 of 2
+ tree_update.nodes[3].id = 4;
+ tree_update.nodes[3].role = ax::mojom::Role::kRadioButton; // 2 of 3
+ tree_update.nodes[4].id = 5;
+ tree_update.nodes[4].role = ax::mojom::Role::kListItem; // 2 of 2
+ tree_update.nodes[5].id = 6;
+ tree_update.nodes[5].role = ax::mojom::Role::kRadioButton; // 3 of 3
+ AXTree tree(tree_update);
+
+ AXNode* list = tree.GetFromId(1);
+ EXPECT_EQ(list->GetPosInSet(), 0);
+ EXPECT_EQ(list->GetSetSize(), 2);
+
+ AXNode* radiobutton1 = tree.GetFromId(2);
+ EXPECT_EQ(radiobutton1->GetPosInSet(), 1);
+ EXPECT_EQ(radiobutton1->GetSetSize(), 3);
+ AXNode* item1 = tree.GetFromId(3);
+ EXPECT_EQ(item1->GetPosInSet(), 1);
+ EXPECT_EQ(item1->GetSetSize(), 2);
+ AXNode* radiobutton2 = tree.GetFromId(4);
+ EXPECT_EQ(radiobutton2->GetPosInSet(), 2);
+ EXPECT_EQ(radiobutton2->GetSetSize(), 3);
+ AXNode* item2 = tree.GetFromId(5);
+ EXPECT_EQ(item2->GetPosInSet(), 2);
+ EXPECT_EQ(item2->GetSetSize(), 2);
+ AXNode* radiobutton3 = tree.GetFromId(6);
+ EXPECT_EQ(radiobutton3->GetPosInSet(), 3);
+ EXPECT_EQ(radiobutton3->GetSetSize(), 3);
+
+ // Ensure that the setsize of list was not modified after calling GetPosInSet
+ // and GetSetSize on kRadioButtons.
+ EXPECT_EQ(list->GetPosInSet(), 0);
+ EXPECT_EQ(list->GetSetSize(), 2);
+}
+
} // namespace ui