1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/accessibility/ax_tree_combiner.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/accessibility/ax_enums.mojom.h"
9 
10 namespace ui {
11 
TEST(CombineAXTreesTest,RenumberOneTree)12 TEST(CombineAXTreesTest, RenumberOneTree) {
13   AXTreeID tree_id_1 = AXTreeID::CreateNewAXTreeID();
14 
15   AXTreeUpdate tree;
16   tree.has_tree_data = true;
17   tree.tree_data.tree_id = tree_id_1;
18   tree.root_id = 2;
19   tree.nodes.resize(3);
20   tree.nodes[0].id = 2;
21   tree.nodes[0].child_ids.push_back(4);
22   tree.nodes[0].child_ids.push_back(6);
23   tree.nodes[1].id = 4;
24   tree.nodes[2].id = 6;
25 
26   AXTreeCombiner combiner;
27   combiner.AddTree(tree, true);
28   combiner.Combine();
29 
30   const AXTreeUpdate& combined = combiner.combined();
31 
32   EXPECT_EQ(1, combined.root_id);
33   ASSERT_EQ(3U, combined.nodes.size());
34   EXPECT_EQ(1, combined.nodes[0].id);
35   ASSERT_EQ(2U, combined.nodes[0].child_ids.size());
36   EXPECT_EQ(2, combined.nodes[0].child_ids[0]);
37   EXPECT_EQ(3, combined.nodes[0].child_ids[1]);
38   EXPECT_EQ(2, combined.nodes[1].id);
39   EXPECT_EQ(3, combined.nodes[2].id);
40 }
41 
TEST(CombineAXTreesTest,EmbedChildTree)42 TEST(CombineAXTreesTest, EmbedChildTree) {
43   AXTreeID tree_id_1 = AXTreeID::CreateNewAXTreeID();
44   AXTreeID tree_id_2 = AXTreeID::CreateNewAXTreeID();
45 
46   AXTreeUpdate parent_tree;
47   parent_tree.root_id = 1;
48   parent_tree.has_tree_data = true;
49   parent_tree.tree_data.tree_id = tree_id_1;
50   parent_tree.nodes.resize(3);
51   parent_tree.nodes[0].id = 1;
52   parent_tree.nodes[0].child_ids.push_back(2);
53   parent_tree.nodes[0].child_ids.push_back(3);
54   parent_tree.nodes[1].id = 2;
55   parent_tree.nodes[1].role = ax::mojom::Role::kButton;
56   parent_tree.nodes[2].id = 3;
57   parent_tree.nodes[2].role = ax::mojom::Role::kIframe;
58   parent_tree.nodes[2].AddStringAttribute(
59       ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
60 
61   AXTreeUpdate child_tree;
62   child_tree.root_id = 1;
63   child_tree.has_tree_data = true;
64   child_tree.tree_data.parent_tree_id = tree_id_1;
65   child_tree.tree_data.tree_id = tree_id_2;
66   child_tree.nodes.resize(3);
67   child_tree.nodes[0].id = 1;
68   child_tree.nodes[0].child_ids.push_back(2);
69   child_tree.nodes[0].child_ids.push_back(3);
70   child_tree.nodes[1].id = 2;
71   child_tree.nodes[1].role = ax::mojom::Role::kCheckBox;
72   child_tree.nodes[2].id = 3;
73   child_tree.nodes[2].role = ax::mojom::Role::kRadioButton;
74 
75   AXTreeCombiner combiner;
76   combiner.AddTree(parent_tree, true);
77   combiner.AddTree(child_tree, false);
78   combiner.Combine();
79 
80   const AXTreeUpdate& combined = combiner.combined();
81 
82   EXPECT_EQ(1, combined.root_id);
83   ASSERT_EQ(6U, combined.nodes.size());
84   EXPECT_EQ(1, combined.nodes[0].id);
85   ASSERT_EQ(2U, combined.nodes[0].child_ids.size());
86   EXPECT_EQ(2, combined.nodes[0].child_ids[0]);
87   EXPECT_EQ(3, combined.nodes[0].child_ids[1]);
88   EXPECT_EQ(2, combined.nodes[1].id);
89   EXPECT_EQ(ax::mojom::Role::kButton, combined.nodes[1].role);
90   EXPECT_EQ(3, combined.nodes[2].id);
91   EXPECT_EQ(ax::mojom::Role::kIframe, combined.nodes[2].role);
92   EXPECT_EQ(1U, combined.nodes[2].child_ids.size());
93   EXPECT_EQ(4, combined.nodes[2].child_ids[0]);
94   EXPECT_EQ(4, combined.nodes[3].id);
95   EXPECT_EQ(5, combined.nodes[4].id);
96   EXPECT_EQ(ax::mojom::Role::kCheckBox, combined.nodes[4].role);
97   EXPECT_EQ(6, combined.nodes[5].id);
98   EXPECT_EQ(ax::mojom::Role::kRadioButton, combined.nodes[5].role);
99 }
100 
TEST(CombineAXTreesTest,MapAllIdAttributes)101 TEST(CombineAXTreesTest, MapAllIdAttributes) {
102   AXTreeID tree_id_1 = AXTreeID::CreateNewAXTreeID();
103 
104   // This is a nonsensical accessibility tree, the goal is to make sure
105   // that all attributes that reference IDs of other nodes are remapped.
106 
107   AXTreeUpdate tree;
108   tree.has_tree_data = true;
109   tree.tree_data.tree_id = tree_id_1;
110   tree.root_id = 11;
111   tree.nodes.resize(2);
112   tree.nodes[0].id = 11;
113   tree.nodes[0].child_ids.push_back(22);
114   tree.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTableHeaderId, 22);
115   tree.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTableRowHeaderId, 22);
116   tree.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTableColumnHeaderId,
117                                 22);
118   tree.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
119                                 22);
120   std::vector<int32_t> ids { 22 };
121   tree.nodes[0].AddIntListAttribute(
122       ax::mojom::IntListAttribute::kIndirectChildIds, ids);
123   tree.nodes[0].AddIntListAttribute(ax::mojom::IntListAttribute::kControlsIds,
124                                     ids);
125   tree.nodes[0].AddIntListAttribute(
126       ax::mojom::IntListAttribute::kDescribedbyIds, ids);
127   tree.nodes[0].AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds,
128                                     ids);
129   tree.nodes[0].AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds,
130                                     ids);
131   tree.nodes[1].id = 22;
132 
133   AXTreeCombiner combiner;
134   combiner.AddTree(tree, true);
135   combiner.Combine();
136 
137   const AXTreeUpdate& combined = combiner.combined();
138 
139   EXPECT_EQ(1, combined.root_id);
140   ASSERT_EQ(2U, combined.nodes.size());
141   EXPECT_EQ(1, combined.nodes[0].id);
142   ASSERT_EQ(1U, combined.nodes[0].child_ids.size());
143   EXPECT_EQ(2, combined.nodes[0].child_ids[0]);
144   EXPECT_EQ(2, combined.nodes[1].id);
145 
146   EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(
147                    ax::mojom::IntAttribute::kTableHeaderId));
148   EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(
149                    ax::mojom::IntAttribute::kTableRowHeaderId));
150   EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(
151                    ax::mojom::IntAttribute::kTableColumnHeaderId));
152   EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(
153                    ax::mojom::IntAttribute::kActivedescendantId));
154   EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute(
155                    ax::mojom::IntListAttribute::kIndirectChildIds)[0]);
156   EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute(
157                    ax::mojom::IntListAttribute::kControlsIds)[0]);
158   EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute(
159                    ax::mojom::IntListAttribute::kDescribedbyIds)[0]);
160   EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute(
161                    ax::mojom::IntListAttribute::kFlowtoIds)[0]);
162   EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute(
163                    ax::mojom::IntListAttribute::kLabelledbyIds)[0]);
164 }
165 
TEST(CombineAXTreesTest,FocusedTree)166 TEST(CombineAXTreesTest, FocusedTree) {
167   AXTreeID tree_id_1 = AXTreeID::CreateNewAXTreeID();
168   AXTreeID tree_id_2 = AXTreeID::CreateNewAXTreeID();
169 
170   AXTreeUpdate parent_tree;
171   parent_tree.has_tree_data = true;
172   parent_tree.tree_data.tree_id = tree_id_1;
173   parent_tree.tree_data.focused_tree_id = tree_id_2;
174   parent_tree.tree_data.focus_id = 2;
175   parent_tree.root_id = 1;
176   parent_tree.nodes.resize(3);
177   parent_tree.nodes[0].id = 1;
178   parent_tree.nodes[0].child_ids.push_back(2);
179   parent_tree.nodes[0].child_ids.push_back(3);
180   parent_tree.nodes[1].id = 2;
181   parent_tree.nodes[1].role = ax::mojom::Role::kButton;
182   parent_tree.nodes[2].id = 3;
183   parent_tree.nodes[2].role = ax::mojom::Role::kIframe;
184   parent_tree.nodes[2].AddStringAttribute(
185       ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
186 
187   AXTreeUpdate child_tree;
188   child_tree.has_tree_data = true;
189   child_tree.tree_data.parent_tree_id = tree_id_1;
190   child_tree.tree_data.tree_id = tree_id_2;
191   child_tree.tree_data.focus_id = 3;
192   child_tree.root_id = 1;
193   child_tree.nodes.resize(3);
194   child_tree.nodes[0].id = 1;
195   child_tree.nodes[0].child_ids.push_back(2);
196   child_tree.nodes[0].child_ids.push_back(3);
197   child_tree.nodes[1].id = 2;
198   child_tree.nodes[1].role = ax::mojom::Role::kCheckBox;
199   child_tree.nodes[2].id = 3;
200   child_tree.nodes[2].role = ax::mojom::Role::kRadioButton;
201 
202   AXTreeCombiner combiner;
203   combiner.AddTree(parent_tree, true);
204   combiner.AddTree(child_tree, false);
205   combiner.Combine();
206 
207   const AXTreeUpdate& combined = combiner.combined();
208 
209   ASSERT_EQ(6U, combined.nodes.size());
210   EXPECT_EQ(6, combined.tree_data.focus_id);
211 }
212 
TEST(CombineAXTreesTest,EmptyTree)213 TEST(CombineAXTreesTest, EmptyTree) {
214   AXTreeUpdate tree;
215 
216   AXTreeCombiner combiner;
217   combiner.AddTree(tree, true);
218   combiner.Combine();
219 
220   const AXTreeUpdate& combined = combiner.combined();
221   ASSERT_EQ(0U, combined.nodes.size());
222 }
223 
224 }  // namespace ui
225