diff options
Diffstat (limited to 'chromium/ui/accessibility/ax_tree_unittest.cc')
-rw-r--r-- | chromium/ui/accessibility/ax_tree_unittest.cc | 708 |
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 |