Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ax_tree_unittest.cc
Go to the documentation of this file.
1// Copyright 2013 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 "ax_tree.h"
6
7#include <cstddef>
8#include <cstdint>
9#include <memory>
10
11#include "ax_enum_util.h"
12#include "ax_node.h"
13#include "ax_node_position.h"
14#include "ax_tree_data.h"
15#include "ax_tree_id.h"
16#include "ax_tree_observer.h"
17#include "base/string_utils.h"
18#include "gtest/gtest.h"
20
21// Helper macro for testing selection values and maintain
22// correct stack tracing and failure causality.
23#define TEST_SELECTION(tree_update, tree, input, expected) \
24 { \
25 tree_update.has_tree_data = true; \
26 tree_update.tree_data.sel_anchor_object_id = input.anchor_id; \
27 tree_update.tree_data.sel_anchor_offset = input.anchor_offset; \
28 tree_update.tree_data.sel_focus_object_id = input.focus_id; \
29 tree_update.tree_data.sel_focus_offset = input.focus_offset; \
30 EXPECT_TRUE(tree->Unserialize(tree_update)); \
31 AXTree::Selection actual = tree->GetUnignoredSelection(); \
32 EXPECT_EQ(expected.anchor_id, actual.anchor_object_id); \
33 EXPECT_EQ(expected.anchor_offset, actual.anchor_offset); \
34 EXPECT_EQ(expected.focus_id, actual.focus_object_id); \
35 EXPECT_EQ(expected.focus_offset, actual.focus_offset); \
36 }
37
38namespace ui {
39
40namespace {
41
42std::string IntVectorToString(const std::vector<int>& items) {
43 std::string str;
44 for (size_t i = 0; i < items.size(); ++i) {
45 if (i > 0)
46 str += ",";
47 str += base::NumberToString(items[i]);
48 }
49 return str;
50}
51
52std::string GetBoundsAsString(const AXTree& tree, int32_t id) {
53 AXNode* node = tree.GetFromId(id);
54 gfx::RectF bounds = tree.GetTreeBounds(node);
55 return base::StringPrintf("(%.0f, %.0f) size (%.0f x %.0f)", bounds.x(),
56 bounds.y(), bounds.width(), bounds.height());
57}
58
59std::string GetUnclippedBoundsAsString(const AXTree& tree, int32_t id) {
60 AXNode* node = tree.GetFromId(id);
61 gfx::RectF bounds = tree.GetTreeBounds(node, nullptr, false);
62 return base::StringPrintf("(%.0f, %.0f) size (%.0f x %.0f)", bounds.x(),
63 bounds.y(), bounds.width(), bounds.height());
64}
65
66bool IsNodeOffscreen(const AXTree& tree, int32_t id) {
67 AXNode* node = tree.GetFromId(id);
68 bool result = false;
69 tree.GetTreeBounds(node, &result);
70 return result;
71}
72
73class TestAXTreeObserver : public AXTreeObserver {
74 public:
75 TestAXTreeObserver(AXTree* tree)
76 : tree_(tree), tree_data_changed_(false), root_changed_(false) {
77 tree_->AddObserver(this);
78 }
79 ~TestAXTreeObserver() { tree_->RemoveObserver(this); }
80
81 void OnNodeDataWillChange(AXTree* tree,
82 const AXNodeData& old_node_data,
83 const AXNodeData& new_node_data) override {}
84 void OnNodeDataChanged(AXTree* tree,
85 const AXNodeData& old_node_data,
86 const AXNodeData& new_node_data) override {}
87 void OnTreeDataChanged(AXTree* tree,
88 const ui::AXTreeData& old_data,
89 const ui::AXTreeData& new_data) override {
90 tree_data_changed_ = true;
91 }
92
93 std::optional<AXNode::AXID> unignored_parent_id_before_node_deleted;
94 void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override {
95 // When this observer function is called in an update, the actual node
96 // deletion has not happened yet. Verify that node still exists in the tree.
97 ASSERT_NE(nullptr, tree->GetFromId(node->id()));
98 node_will_be_deleted_ids_.push_back(node->id());
99
100 if (unignored_parent_id_before_node_deleted) {
101 ASSERT_NE(nullptr, node->GetUnignoredParent());
102 ASSERT_EQ(*unignored_parent_id_before_node_deleted,
103 node->GetUnignoredParent()->id());
104 }
105 }
106
107 void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) override {
108 subtree_deleted_ids_.push_back(node->id());
109 }
110
111 void OnNodeWillBeReparented(AXTree* tree, AXNode* node) override {
112 node_will_be_reparented_ids_.push_back(node->id());
113 }
114
115 void OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) override {
116 subtree_will_be_reparented_ids_.push_back(node->id());
117 }
118
119 void OnNodeCreated(AXTree* tree, AXNode* node) override {
120 created_ids_.push_back(node->id());
121 }
122
123 void OnNodeDeleted(AXTree* tree, int32_t node_id) override {
124 // When this observer function is called in an update, node has already been
125 // deleted from the tree. Verify that the node is absent from the tree.
126 ASSERT_EQ(nullptr, tree->GetFromId(node_id));
127 deleted_ids_.push_back(node_id);
128 }
129
130 void OnNodeReparented(AXTree* tree, AXNode* node) override {
131 node_reparented_ids_.push_back(node->id());
132 }
133
134 void OnNodeChanged(AXTree* tree, AXNode* node) override {
135 changed_ids_.push_back(node->id());
136 }
137
138 void OnAtomicUpdateFinished(AXTree* tree,
139 bool root_changed,
140 const std::vector<Change>& changes) override {
141 root_changed_ = root_changed;
142
143 for (size_t i = 0; i < changes.size(); ++i) {
144 int id = changes[i].node->id();
145 switch (changes[i].type) {
146 case NODE_CREATED:
147 node_creation_finished_ids_.push_back(id);
148 break;
149 case SUBTREE_CREATED:
150 subtree_creation_finished_ids_.push_back(id);
151 break;
152 case NODE_REPARENTED:
153 node_reparented_finished_ids_.push_back(id);
154 break;
155 case SUBTREE_REPARENTED:
156 subtree_reparented_finished_ids_.push_back(id);
157 break;
158 case NODE_CHANGED:
159 change_finished_ids_.push_back(id);
160 break;
161 }
162 }
163 }
164
165 void OnRoleChanged(AXTree* tree,
166 AXNode* node,
167 ax::mojom::Role old_role,
168 ax::mojom::Role new_role) override {
169 attribute_change_log_.push_back(base::StringPrintf(
170 "Role changed from %s to %s", ToString(old_role), ToString(new_role)));
171 }
172
173 void OnStateChanged(AXTree* tree,
174 AXNode* node,
176 bool new_value) override {
177 attribute_change_log_.push_back(base::StringPrintf(
178 "%s changed to %s", ToString(state), new_value ? "true" : "false"));
179 }
180
181 void OnStringAttributeChanged(AXTree* tree,
182 AXNode* node,
184 const std::string& old_value,
185 const std::string& new_value) override {
186 attribute_change_log_.push_back(
187 base::StringPrintf("%s changed from %s to %s", ToString(attr),
188 old_value.c_str(), new_value.c_str()));
189 }
190
191 void OnIntAttributeChanged(AXTree* tree,
192 AXNode* node,
194 int32_t old_value,
195 int32_t new_value) override {
196 attribute_change_log_.push_back(base::StringPrintf(
197 "%s changed from %d to %d", ToString(attr), old_value, new_value));
198 }
199
200 void OnFloatAttributeChanged(AXTree* tree,
201 AXNode* node,
203 float old_value,
204 float new_value) override {
205 attribute_change_log_.push_back(base::StringPrintf(
206 "%s changed from %.1f to %.1f", ToString(attr), old_value, new_value));
207 }
208
209 void OnBoolAttributeChanged(AXTree* tree,
210 AXNode* node,
212 bool new_value) override {
213 attribute_change_log_.push_back(base::StringPrintf(
214 "%s changed to %s", ToString(attr), new_value ? "true" : "false"));
215 }
216
217 void OnIntListAttributeChanged(
218 AXTree* tree,
219 AXNode* node,
221 const std::vector<int32_t>& old_value,
222 const std::vector<int32_t>& new_value) override {
223 attribute_change_log_.push_back(
224 base::StringPrintf("%s changed from %s to %s", ToString(attr),
225 IntVectorToString(old_value).c_str(),
226 IntVectorToString(new_value).c_str()));
227 }
228
229 bool tree_data_changed() const { return tree_data_changed_; }
230 bool root_changed() const { return root_changed_; }
231 const std::vector<int32_t>& deleted_ids() { return deleted_ids_; }
232 const std::vector<int32_t>& subtree_deleted_ids() {
233 return subtree_deleted_ids_;
234 }
235 const std::vector<int32_t>& created_ids() { return created_ids_; }
236 const std::vector<int32_t>& node_creation_finished_ids() {
237 return node_creation_finished_ids_;
238 }
239 const std::vector<int32_t>& subtree_creation_finished_ids() {
240 return subtree_creation_finished_ids_;
241 }
242 const std::vector<int32_t>& node_reparented_finished_ids() {
243 return node_reparented_finished_ids_;
244 }
245 const std::vector<int32_t>& subtree_will_be_reparented_ids() {
246 return subtree_will_be_reparented_ids_;
247 }
248 const std::vector<int32_t>& node_will_be_reparented_ids() {
249 return node_will_be_reparented_ids_;
250 }
251 const std::vector<int32_t>& node_will_be_deleted_ids() {
252 return node_will_be_deleted_ids_;
253 }
254 const std::vector<int32_t>& node_reparented_ids() {
255 return node_reparented_ids_;
256 }
257 const std::vector<int32_t>& subtree_reparented_finished_ids() {
258 return subtree_reparented_finished_ids_;
259 }
260 const std::vector<int32_t>& change_finished_ids() {
261 return change_finished_ids_;
262 }
263 const std::vector<std::string>& attribute_change_log() {
264 return attribute_change_log_;
265 }
266
267 private:
268 AXTree* tree_;
269 bool tree_data_changed_;
270 bool root_changed_;
271 std::vector<int32_t> deleted_ids_;
272 std::vector<int32_t> subtree_deleted_ids_;
273 std::vector<int32_t> created_ids_;
274 std::vector<int32_t> changed_ids_;
275 std::vector<int32_t> subtree_will_be_reparented_ids_;
276 std::vector<int32_t> node_will_be_reparented_ids_;
277 std::vector<int32_t> node_will_be_deleted_ids_;
278 std::vector<int32_t> node_creation_finished_ids_;
279 std::vector<int32_t> subtree_creation_finished_ids_;
280 std::vector<int32_t> node_reparented_ids_;
281 std::vector<int32_t> node_reparented_finished_ids_;
282 std::vector<int32_t> subtree_reparented_finished_ids_;
283 std::vector<int32_t> change_finished_ids_;
284 std::vector<std::string> attribute_change_log_;
285};
286
287} // namespace
288
289// A macro for testing that a std::optional has both a value and that its value
290// is set to a particular expectation.
291#define EXPECT_OPTIONAL_EQ(expected, actual) \
292 EXPECT_TRUE(actual.has_value()); \
293 if (actual) { \
294 EXPECT_EQ(expected, actual.value()); \
295 }
296
297TEST(AXTreeTest, SerializeAXTreeUpdate) {
298 AXNodeData list;
299 list.id = 3;
301 list.child_ids.push_back(4);
302 list.child_ids.push_back(5);
303 list.child_ids.push_back(6);
304
305 AXNodeData list_item_2;
306 list_item_2.id = 5;
307 list_item_2.role = ax::mojom::Role::kListItem;
308
309 AXNodeData list_item_3;
310 list_item_3.id = 6;
311 list_item_3.role = ax::mojom::Role::kListItem;
312
313 AXNodeData button;
314 button.id = 7;
316
318 update.root_id = 3;
319 update.nodes.push_back(list);
320 update.nodes.push_back(list_item_2);
321 update.nodes.push_back(list_item_3);
322 update.nodes.push_back(button);
323
324 EXPECT_EQ(
325 "AXTreeUpdate: root id 3\n"
326 "id=3 list (0, 0)-(0, 0) child_ids=4,5,6\n"
327 " id=5 listItem (0, 0)-(0, 0)\n"
328 " id=6 listItem (0, 0)-(0, 0)\n"
329 "id=7 button (0, 0)-(0, 0)\n",
330 update.ToString());
331}
332
333TEST(AXTreeTest, LeaveOrphanedDeletedSubtreeFails) {
334 AXTreeUpdate initial_state;
335 initial_state.root_id = 1;
336 initial_state.nodes.resize(3);
337 initial_state.nodes[0].id = 1;
338 initial_state.nodes[0].child_ids.push_back(2);
339 initial_state.nodes[0].child_ids.push_back(3);
340 initial_state.nodes[1].id = 2;
341 initial_state.nodes[2].id = 3;
342 AXTree tree(initial_state);
343
344 // This should fail because we delete a subtree rooted at id=2
345 // but never update it.
347 update.node_id_to_clear = 2;
348 update.nodes.resize(1);
349 update.nodes[0].id = 3;
350 EXPECT_FALSE(tree.Unserialize(update));
351 ASSERT_EQ("Nodes left pending by the update: 2", tree.error());
352}
353
354TEST(AXTreeTest, LeaveOrphanedNewChildFails) {
355 AXTreeUpdate initial_state;
356 initial_state.root_id = 1;
357 initial_state.nodes.resize(1);
358 initial_state.nodes[0].id = 1;
359 AXTree tree(initial_state);
360
361 // This should fail because we add a new child to the root node
362 // but never update it.
364 update.nodes.resize(1);
365 update.nodes[0].id = 1;
366 update.nodes[0].child_ids.push_back(2);
367 EXPECT_FALSE(tree.Unserialize(update));
368 ASSERT_EQ("Nodes left pending by the update: 2", tree.error());
369}
370
371TEST(AXTreeTest, DuplicateChildIdFails) {
372 AXTreeUpdate initial_state;
373 initial_state.root_id = 1;
374 initial_state.nodes.resize(1);
375 initial_state.nodes[0].id = 1;
376 AXTree tree(initial_state);
377
378 // This should fail because a child id appears twice.
380 update.nodes.resize(2);
381 update.nodes[0].id = 1;
382 update.nodes[0].child_ids.push_back(2);
383 update.nodes[0].child_ids.push_back(2);
384 update.nodes[1].id = 2;
385 EXPECT_FALSE(tree.Unserialize(update));
386 ASSERT_EQ("Node 1 has duplicate child id 2", tree.error());
387}
388
389TEST(AXTreeTest, InvalidReparentingFails) {
390 AXTreeUpdate initial_state;
391 initial_state.root_id = 1;
392 initial_state.nodes.resize(3);
393 initial_state.nodes[0].id = 1;
394 initial_state.nodes[0].child_ids.push_back(2);
395 initial_state.nodes[1].id = 2;
396 initial_state.nodes[1].child_ids.push_back(3);
397 initial_state.nodes[2].id = 3;
398
399 AXTree tree(initial_state);
400
401 // This should fail because node 3 is reparented from node 2 to node 1
402 // without deleting node 1's subtree first.
404 update.nodes.resize(3);
405 update.nodes[0].id = 1;
406 update.nodes[0].child_ids.push_back(3);
407 update.nodes[0].child_ids.push_back(2);
408 update.nodes[1].id = 2;
409 update.nodes[2].id = 3;
410 EXPECT_FALSE(tree.Unserialize(update));
411 ASSERT_EQ("Node 3 is not marked for destruction, would be reparented to 1",
412 tree.error());
413}
414
415TEST(AXTreeTest, NoReparentingOfRootIfNoNewRoot) {
416 AXNodeData root;
417 root.id = 1;
418 AXNodeData child1;
419 child1.id = 2;
420 AXNodeData child2;
421 child2.id = 3;
422
423 root.child_ids = {child1.id};
424 child1.child_ids = {child2.id};
425
426 AXTreeUpdate initial_state;
427 initial_state.root_id = root.id;
428 initial_state.nodes = {root, child1, child2};
429
430 AXTree tree(initial_state);
431
432 // Update the root but don't change it by reparenting |child2| to be a child
433 // of the root.
434 root.child_ids = {child1.id, child2.id};
435 child1.child_ids = {};
436
438 update.root_id = root.id;
439 update.node_id_to_clear = root.id;
440 update.nodes = {root, child1, child2};
441
442 TestAXTreeObserver test_observer(&tree);
443 ASSERT_TRUE(tree.Unserialize(update));
444
445 EXPECT_EQ(0U, test_observer.deleted_ids().size());
446 EXPECT_EQ(0U, test_observer.subtree_deleted_ids().size());
447 EXPECT_EQ(0U, test_observer.created_ids().size());
448
449 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
450 EXPECT_EQ(0U, test_observer.subtree_creation_finished_ids().size());
451 EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
452
453 ASSERT_EQ(2U, test_observer.subtree_reparented_finished_ids().size());
454 EXPECT_EQ(child1.id, test_observer.subtree_reparented_finished_ids()[0]);
455 EXPECT_EQ(child2.id, test_observer.subtree_reparented_finished_ids()[1]);
456
457 ASSERT_EQ(1U, test_observer.change_finished_ids().size());
458 EXPECT_EQ(root.id, test_observer.change_finished_ids()[0]);
459
460 EXPECT_FALSE(test_observer.root_changed());
461 EXPECT_FALSE(test_observer.tree_data_changed());
462}
463
464TEST(AXTreeTest, NoReparentingIfOnlyRemovedAndChangedNotReAdded) {
465 AXNodeData root;
466 root.id = 1;
467 AXNodeData child1;
468 child1.id = 2;
469 AXNodeData child2;
470 child2.id = 3;
471
472 root.child_ids = {child1.id};
473 child1.child_ids = {child2.id};
474
475 AXTreeUpdate initial_state;
476 initial_state.root_id = root.id;
477 initial_state.nodes = {root, child1, child2};
478
479 AXTree tree(initial_state);
480
481 // Change existing attributes.
483 update.nodes.resize(2);
484 update.nodes[0].id = 2;
486 3);
487 update.nodes[1].id = 1;
488
489 TestAXTreeObserver test_observer(&tree);
490 EXPECT_TRUE(tree.Unserialize(update)) << tree.error();
491
492 EXPECT_EQ(2U, test_observer.deleted_ids().size());
493 EXPECT_EQ(2U, test_observer.subtree_deleted_ids().size());
494 EXPECT_EQ(0U, test_observer.created_ids().size());
495
496 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
497 EXPECT_EQ(0U, test_observer.subtree_creation_finished_ids().size());
498 EXPECT_EQ(0U, test_observer.node_will_be_reparented_ids().size());
499 EXPECT_EQ(2U, test_observer.node_will_be_deleted_ids().size());
500 EXPECT_EQ(0U, test_observer.subtree_will_be_reparented_ids().size());
501 EXPECT_EQ(0U, test_observer.node_reparented_ids().size());
502 EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
503 EXPECT_EQ(0U, test_observer.subtree_reparented_finished_ids().size());
504
505 EXPECT_FALSE(test_observer.root_changed());
506 EXPECT_FALSE(test_observer.tree_data_changed());
507}
508
509// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
510// Make sure that when a node is reparented then removed from the tree
511// that it notifies OnNodeDeleted rather than OnNodeReparented.
512TEST(AXTreeTest, NoReparentingIfRemovedMultipleTimesAndNotInFinalTree) {
513 AXTreeUpdate initial_state;
514 initial_state.root_id = 1;
515 initial_state.nodes.resize(4);
516 initial_state.nodes[0].id = 1;
517 initial_state.nodes[0].child_ids = {2, 4};
518 initial_state.nodes[1].id = 2;
519 initial_state.nodes[1].child_ids = {3};
520 initial_state.nodes[2].id = 3;
521 initial_state.nodes[3].id = 4;
522
523 AXTree tree(initial_state);
524
526 update.nodes.resize(4);
527 // Delete AXID 3
528 update.nodes[0].id = 2;
529 // Reparent AXID 3 onto AXID 4
530 update.nodes[1].id = 4;
531 update.nodes[1].child_ids = {3};
532 update.nodes[2].id = 3;
533 // Delete AXID 3
534 update.nodes[3].id = 4;
535
536 TestAXTreeObserver test_observer(&tree);
537 ASSERT_TRUE(tree.Unserialize(update)) << tree.error();
538
539 EXPECT_EQ(1U, test_observer.deleted_ids().size());
540 EXPECT_EQ(1U, test_observer.subtree_deleted_ids().size());
541 EXPECT_EQ(0U, test_observer.created_ids().size());
542
543 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
544 EXPECT_EQ(0U, test_observer.subtree_creation_finished_ids().size());
545 EXPECT_EQ(0U, test_observer.node_will_be_reparented_ids().size());
546 EXPECT_EQ(1U, test_observer.node_will_be_deleted_ids().size());
547 EXPECT_EQ(0U, test_observer.subtree_will_be_reparented_ids().size());
548 EXPECT_EQ(0U, test_observer.node_reparented_ids().size());
549 EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
550 EXPECT_EQ(0U, test_observer.subtree_reparented_finished_ids().size());
551
552 EXPECT_FALSE(test_observer.root_changed());
553 EXPECT_FALSE(test_observer.tree_data_changed());
554}
555
556// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
557// Make sure that when a node is reparented multiple times and exists in the
558// final tree that it notifies OnNodeReparented rather than OnNodeDeleted.
559TEST(AXTreeTest, ReparentIfRemovedMultipleTimesButExistsInFinalTree) {
560 AXTreeUpdate initial_state;
561 initial_state.root_id = 1;
562 initial_state.nodes.resize(4);
563 initial_state.nodes[0].id = 1;
564 initial_state.nodes[0].child_ids = {2, 4};
565 initial_state.nodes[1].id = 2;
566 initial_state.nodes[1].child_ids = {3};
567 initial_state.nodes[2].id = 3;
568 initial_state.nodes[3].id = 4;
569
570 AXTree tree(initial_state);
571
573 update.nodes.resize(6);
574 // Delete AXID 3
575 update.nodes[0].id = 2;
576 // Reparent AXID 3 onto AXID 4
577 update.nodes[1].id = 4;
578 update.nodes[1].child_ids = {3};
579 update.nodes[2].id = 3;
580 // Delete AXID 3
581 update.nodes[3].id = 4;
582 // Reparent AXID 3 onto AXID 2
583 update.nodes[4].id = 2;
584 update.nodes[4].child_ids = {3};
585 update.nodes[5].id = 3;
586
587 TestAXTreeObserver test_observer(&tree);
588 ASSERT_TRUE(tree.Unserialize(update)) << tree.error();
589
590 EXPECT_EQ(0U, test_observer.deleted_ids().size());
591 EXPECT_EQ(0U, test_observer.subtree_deleted_ids().size());
592 EXPECT_EQ(0U, test_observer.created_ids().size());
593
594 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
595 EXPECT_EQ(0U, test_observer.subtree_creation_finished_ids().size());
596 EXPECT_EQ(1U, test_observer.node_will_be_reparented_ids().size());
597 EXPECT_EQ(0U, test_observer.node_will_be_deleted_ids().size());
598 EXPECT_EQ(1U, test_observer.subtree_will_be_reparented_ids().size());
599 EXPECT_EQ(1U, test_observer.node_reparented_ids().size());
600 EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
601 EXPECT_EQ(1U, test_observer.subtree_reparented_finished_ids().size());
602
603 EXPECT_FALSE(test_observer.root_changed());
604 EXPECT_FALSE(test_observer.tree_data_changed());
605}
606
607TEST(AXTreeTest, ReparentRootIfRootChanged) {
608 AXNodeData root;
609 root.id = 1;
610 AXNodeData child1;
611 child1.id = 2;
612 AXNodeData child2;
613 child2.id = 3;
614
615 root.child_ids = {child1.id};
616 child1.child_ids = {child2.id};
617
618 AXTreeUpdate initial_state;
619 initial_state.root_id = root.id;
620 initial_state.nodes = {root, child1, child2};
621
622 AXTree tree(initial_state);
623
624 // Create a new root and reparent |child2| to be a child of the new root.
625 AXNodeData root2;
626 root2.id = 4;
627 root2.child_ids = {child1.id, child2.id};
628 child1.child_ids = {};
629
631 update.root_id = root2.id;
632 update.node_id_to_clear = root.id;
633 update.nodes = {root2, child1, child2};
634
635 TestAXTreeObserver test_observer(&tree);
636 ASSERT_TRUE(tree.Unserialize(update));
637
638 ASSERT_EQ(1U, test_observer.deleted_ids().size());
639 EXPECT_EQ(root.id, test_observer.deleted_ids()[0]);
640
641 ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
642 EXPECT_EQ(root.id, test_observer.subtree_deleted_ids()[0]);
643
644 ASSERT_EQ(1U, test_observer.created_ids().size());
645 EXPECT_EQ(root2.id, test_observer.created_ids()[0]);
646
647 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
648
649 ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
650 EXPECT_EQ(root2.id, test_observer.subtree_creation_finished_ids()[0]);
651
652 ASSERT_EQ(2U, test_observer.node_reparented_finished_ids().size());
653 EXPECT_EQ(child1.id, test_observer.node_reparented_finished_ids()[0]);
654 EXPECT_EQ(child2.id, test_observer.node_reparented_finished_ids()[1]);
655
656 EXPECT_EQ(0U, test_observer.subtree_reparented_finished_ids().size());
657
658 EXPECT_EQ(0U, test_observer.change_finished_ids().size());
659
660 EXPECT_TRUE(test_observer.root_changed());
661 EXPECT_FALSE(test_observer.tree_data_changed());
662}
663
664TEST(AXTreeTest, ImplicitChildrenDelete) {
665 // This test covers the case where an AXTreeUpdate includes a node without
666 // mentioning that node's children, this should cause a delete of those child
667 // nodes.
668
669 // Setup initial tree state
670 // Tree:
671 // 1
672 // 2 3
673 AXTreeUpdate initial_state;
674 initial_state.root_id = 1;
675 initial_state.nodes.resize(3);
676 initial_state.nodes[0].id = 1;
677 initial_state.nodes[0].child_ids.resize(2);
678 initial_state.nodes[0].child_ids[0] = 2;
679 initial_state.nodes[0].child_ids[1] = 3;
680 initial_state.nodes[1].id = 2;
681 initial_state.nodes[2].id = 3;
682 AXTree tree(initial_state);
683
684 EXPECT_NE(tree.GetFromId(1), nullptr);
685 EXPECT_NE(tree.GetFromId(2), nullptr);
686 EXPECT_NE(tree.GetFromId(3), nullptr);
687
688 // Perform a no-op update of node 1 but omit any mention of its children. This
689 // should delete all of the node's children.
691 update.nodes.resize(1);
692 update.nodes[0].id = 1;
693
694 ASSERT_TRUE(tree.Unserialize(update));
695
696 // Check that nodes 2 and 3 have been deleted.
697 EXPECT_NE(tree.GetFromId(1), nullptr);
698 EXPECT_EQ(tree.GetFromId(2), nullptr);
699 EXPECT_EQ(tree.GetFromId(3), nullptr);
700}
701
702TEST(AXTreeTest, IndexInParentAfterReorder) {
703 // This test covers the case where an AXTreeUpdate includes
704 // reordered children. The unignored index in parent
705 // values should be updated.
706
707 // Setup initial tree state.
708 // Tree:
709 // 1
710 // 2 3 4
711 AXTreeUpdate initial_state;
712 initial_state.root_id = 1;
713 initial_state.nodes.resize(4);
714 initial_state.nodes[0].id = 1;
715 initial_state.nodes[0].child_ids.resize(3);
716 initial_state.nodes[0].child_ids[0] = 2;
717 initial_state.nodes[0].child_ids[1] = 3;
718 initial_state.nodes[0].child_ids[2] = 4;
719 initial_state.nodes[1].id = 2;
720 initial_state.nodes[2].id = 3;
721 initial_state.nodes[3].id = 4;
722 AXTree tree(initial_state);
723
724 // Index in parent correct.
725 EXPECT_EQ(0U, tree.GetFromId(2)->GetUnignoredIndexInParent());
726 EXPECT_EQ(1U, tree.GetFromId(3)->GetUnignoredIndexInParent());
727 EXPECT_EQ(2U, tree.GetFromId(4)->GetUnignoredIndexInParent());
728
729 // Perform an update where we reorder children to [ 4 3 2 ]
731 update.nodes.resize(4);
732 update.root_id = 1;
733 update.nodes[0].id = 1;
734 update.nodes[0].child_ids.resize(3);
735 update.nodes[0].child_ids[0] = 4;
736 update.nodes[0].child_ids[1] = 3;
737 update.nodes[0].child_ids[2] = 2;
738 update.nodes[1].id = 2;
739 update.nodes[2].id = 3;
740 update.nodes[3].id = 4;
741
742 ASSERT_TRUE(tree.Unserialize(update));
743
744 // Index in parent should have changed as well.
745 EXPECT_EQ(0U, tree.GetFromId(4)->GetUnignoredIndexInParent());
746 EXPECT_EQ(1U, tree.GetFromId(3)->GetUnignoredIndexInParent());
747 EXPECT_EQ(2U, tree.GetFromId(2)->GetUnignoredIndexInParent());
748}
749
750TEST(AXTreeTest, IndexInParentAfterReorderIgnoredNode) {
751 // This test covers another case where an AXTreeUpdate includes
752 // reordered children. If one of the reordered nodes is ignored, its
753 // children's unignored index in parent should also be updated.
754
755 // Setup initial tree state.
756 // Tree:
757 // 1
758 // 2 3i 4
759 // 5 6
760 AXTreeUpdate initial_state;
761 initial_state.root_id = 1;
762 initial_state.nodes.resize(6);
763 initial_state.nodes[0].id = 1;
764 initial_state.nodes[0].child_ids.resize(3);
765 initial_state.nodes[0].child_ids[0] = 2;
766 initial_state.nodes[0].child_ids[1] = 3;
767 initial_state.nodes[0].child_ids[2] = 4;
768 initial_state.nodes[1].id = 2;
769 initial_state.nodes[2].id = 3;
770 initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
771 initial_state.nodes[2].child_ids.resize(2);
772 initial_state.nodes[2].child_ids[0] = 5;
773 initial_state.nodes[2].child_ids[1] = 6;
774 initial_state.nodes[3].id = 4;
775 initial_state.nodes[4].id = 5;
776 initial_state.nodes[5].id = 6;
777 AXTree tree(initial_state);
778
779 // Index in parent correct.
780 EXPECT_EQ(0U, tree.GetFromId(2)->GetUnignoredIndexInParent());
781 EXPECT_EQ(1U, tree.GetFromId(5)->GetUnignoredIndexInParent());
782 EXPECT_EQ(2U, tree.GetFromId(6)->GetUnignoredIndexInParent());
783 EXPECT_EQ(3U, tree.GetFromId(4)->GetUnignoredIndexInParent());
784
785 // Perform an update where we reorder children to [ 3i 2 4 ]. The
786 // unignored index in parent for the children of the ignored node (3) should
787 // be updated.
789 update.root_id = 1;
790 update.nodes.resize(6);
791 update.nodes[0].id = 1;
792 update.nodes[0].child_ids.resize(3);
793 update.nodes[0].child_ids[0] = 3;
794 update.nodes[0].child_ids[1] = 2;
795 update.nodes[0].child_ids[2] = 4;
796 update.nodes[1].id = 2;
797 update.nodes[2].id = 3;
798 update.nodes[2].AddState(ax::mojom::State::kIgnored);
799 update.nodes[2].child_ids.resize(2);
800 update.nodes[2].child_ids[0] = 5;
801 update.nodes[2].child_ids[1] = 6;
802 update.nodes[3].id = 4;
803 update.nodes[4].id = 5;
804 update.nodes[5].id = 6;
805
806 ASSERT_TRUE(tree.Unserialize(update));
807
808 EXPECT_EQ(2U, tree.GetFromId(2)->GetUnignoredIndexInParent());
809 EXPECT_EQ(0U, tree.GetFromId(5)->GetUnignoredIndexInParent());
810 EXPECT_EQ(1U, tree.GetFromId(6)->GetUnignoredIndexInParent());
811 EXPECT_EQ(3U, tree.GetFromId(4)->GetUnignoredIndexInParent());
812}
813
814TEST(AXTreeTest, ImplicitAttributeDelete) {
815 // This test covers the case where an AXTreeUpdate includes a node without
816 // mentioning one of that node's attributes, this should cause a delete of any
817 // unmentioned attribute that was previously set on the node.
818
819 AXTreeUpdate initial_state;
820 initial_state.root_id = 1;
821 initial_state.nodes.resize(1);
822 initial_state.nodes[0].id = 1;
823 initial_state.nodes[0].SetName("Node 1 name");
824 AXTree tree(initial_state);
825
826 EXPECT_NE(tree.GetFromId(1), nullptr);
827 EXPECT_EQ(
829 "Node 1 name");
830
831 // Perform a no-op update of node 1 but omit any mention of the name
832 // attribute. This should delete the name attribute.
834 update.nodes.resize(1);
835 update.nodes[0].id = 1;
836 ASSERT_TRUE(tree.Unserialize(update));
837
838 // Check that the name attribute is no longer present.
839 EXPECT_NE(tree.GetFromId(1), nullptr);
840 EXPECT_FALSE(
842}
843
844TEST(AXTreeTest, TreeObserverIsCalled) {
845 AXTreeUpdate initial_state;
846 initial_state.root_id = 1;
847 initial_state.nodes.resize(2);
848 initial_state.nodes[0].id = 1;
849 initial_state.nodes[0].child_ids.push_back(2);
850 initial_state.nodes[1].id = 2;
851
852 AXTree tree(initial_state);
854 update.root_id = 3;
855 update.node_id_to_clear = 1;
856 update.nodes.resize(2);
857 update.nodes[0].id = 3;
858 update.nodes[0].child_ids.push_back(4);
859 update.nodes[1].id = 4;
860
861 TestAXTreeObserver test_observer(&tree);
862 ASSERT_TRUE(tree.Unserialize(update));
863
864 ASSERT_EQ(2U, test_observer.deleted_ids().size());
865 EXPECT_EQ(1, test_observer.deleted_ids()[0]);
866 EXPECT_EQ(2, test_observer.deleted_ids()[1]);
867
868 ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
869 EXPECT_EQ(1, test_observer.subtree_deleted_ids()[0]);
870
871 ASSERT_EQ(2U, test_observer.created_ids().size());
872 EXPECT_EQ(3, test_observer.created_ids()[0]);
873 EXPECT_EQ(4, test_observer.created_ids()[1]);
874
875 ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
876 EXPECT_EQ(3, test_observer.subtree_creation_finished_ids()[0]);
877
878 ASSERT_EQ(1U, test_observer.node_creation_finished_ids().size());
879 EXPECT_EQ(4, test_observer.node_creation_finished_ids()[0]);
880
881 ASSERT_TRUE(test_observer.root_changed());
882}
883
884TEST(AXTreeTest, TreeObserverIsCalledForTreeDataChanges) {
885 AXTreeUpdate initial_state;
886 initial_state.root_id = 1;
887 initial_state.nodes.resize(1);
888 initial_state.nodes[0].id = 1;
889 initial_state.has_tree_data = true;
890 initial_state.tree_data.title = "Initial";
891 AXTree tree(initial_state);
892
893 TestAXTreeObserver test_observer(&tree);
894
895 // An empty update shouldn't change tree data.
896 AXTreeUpdate empty_update;
897 EXPECT_TRUE(tree.Unserialize(empty_update));
898 EXPECT_FALSE(test_observer.tree_data_changed());
899 EXPECT_EQ("Initial", tree.data().title);
900
901 // An update with tree data shouldn't change tree data if
902 // |has_tree_data| isn't set.
903 AXTreeUpdate ignored_tree_data_update;
904 ignored_tree_data_update.tree_data.title = "Ignore Me";
905 EXPECT_TRUE(tree.Unserialize(ignored_tree_data_update));
906 EXPECT_FALSE(test_observer.tree_data_changed());
907 EXPECT_EQ("Initial", tree.data().title);
908
909 // An update with |has_tree_data| set should update the tree data.
910 AXTreeUpdate tree_data_update;
911 tree_data_update.has_tree_data = true;
912 tree_data_update.tree_data.title = "New Title";
913 EXPECT_TRUE(tree.Unserialize(tree_data_update));
914 EXPECT_TRUE(test_observer.tree_data_changed());
915 EXPECT_EQ("New Title", tree.data().title);
916}
917
918TEST(AXTreeTest, ReparentingDoesNotTriggerNodeCreated) {
919 AXTreeUpdate initial_state;
920 initial_state.root_id = 1;
921 initial_state.nodes.resize(3);
922 initial_state.nodes[0].id = 1;
923 initial_state.nodes[0].child_ids.push_back(2);
924 initial_state.nodes[1].id = 2;
925 initial_state.nodes[1].child_ids.push_back(3);
926 initial_state.nodes[2].id = 3;
927
928 AXTree tree(initial_state);
929 TestAXTreeObserver test_observer(&tree);
930
932 update.nodes.resize(2);
933 update.node_id_to_clear = 2;
934 update.root_id = 1;
935 update.nodes[0].id = 1;
936 update.nodes[0].child_ids.push_back(3);
937 update.nodes[1].id = 3;
938 EXPECT_TRUE(tree.Unserialize(update)) << tree.error();
939 std::vector<int> created = test_observer.node_creation_finished_ids();
940 std::vector<int> subtree_reparented =
941 test_observer.subtree_reparented_finished_ids();
942 std::vector<int> node_reparented =
943 test_observer.node_reparented_finished_ids();
944 ASSERT_FALSE(base::Contains(created, 3));
945 ASSERT_TRUE(base::Contains(subtree_reparented, 3));
946 ASSERT_FALSE(base::Contains(node_reparented, 3));
947}
948
949TEST(AXTreeTest, MultipleIgnoredChangesDoesNotBreakCache) {
950 AXTreeUpdate initial_state;
951 initial_state.root_id = 1;
952 initial_state.nodes.resize(3);
953 initial_state.nodes[0].id = 1;
954 initial_state.nodes[0].child_ids.push_back(2);
955
956 initial_state.nodes[1].id = 2;
957 initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
958 initial_state.nodes[1].child_ids.push_back(3);
959
960 initial_state.nodes[2].id = 3;
961
962 AXTree tree(initial_state);
963 TestAXTreeObserver test_observer(&tree);
964 EXPECT_EQ(1u, tree.GetFromId(2)->GetUnignoredChildCount());
965
967 update.nodes.resize(2);
968 update.nodes[0].id = 3;
969 update.nodes[0].AddState(ax::mojom::State::kIgnored);
970
971 update.nodes[1].id = 2;
972 update.nodes[1].child_ids.push_back(3);
973
974 EXPECT_TRUE(tree.Unserialize(update)) << tree.error();
975 EXPECT_EQ(0u, tree.GetFromId(2)->GetUnignoredChildCount());
976 EXPECT_FALSE(tree.GetFromId(2)->data().HasState(ax::mojom::State::kIgnored));
978}
979
980TEST(AXTreeTest, NodeToClearUpdatesParentUnignoredCount) {
981 AXTreeUpdate initial_state;
982 initial_state.root_id = 1;
983 initial_state.nodes.resize(4);
984 initial_state.nodes[0].id = 1;
985 initial_state.nodes[0].child_ids.push_back(2);
986 initial_state.nodes[1].id = 2;
987 initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
988 initial_state.nodes[1].child_ids.push_back(3);
989 initial_state.nodes[1].child_ids.push_back(4);
990 initial_state.nodes[2].id = 3;
991 initial_state.nodes[3].id = 4;
992
993 AXTree tree(initial_state);
994 EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
995 EXPECT_EQ(2u, tree.GetFromId(2)->GetUnignoredChildCount());
996
998 update.nodes.resize(1);
999 update.node_id_to_clear = 2;
1000 update.root_id = 1;
1001 update.nodes[0] = initial_state.nodes[1];
1002 update.nodes[0].state = 0;
1003 update.nodes[0].child_ids.resize(0);
1004 EXPECT_TRUE(tree.Unserialize(update)) << tree.error();
1005
1006 EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount());
1007}
1008
1009TEST(AXTreeTest, TreeObserverIsNotCalledForReparenting) {
1010 AXTreeUpdate initial_state;
1011 initial_state.root_id = 1;
1012 initial_state.nodes.resize(2);
1013 initial_state.nodes[0].id = 1;
1014 initial_state.nodes[0].child_ids.push_back(2);
1015 initial_state.nodes[1].id = 2;
1016
1017 AXTree tree(initial_state);
1019 update.node_id_to_clear = 1;
1020 update.root_id = 2;
1021 update.nodes.resize(2);
1022 update.nodes[0].id = 2;
1023 update.nodes[0].child_ids.push_back(4);
1024 update.nodes[1].id = 4;
1025
1026 TestAXTreeObserver test_observer(&tree);
1027
1029
1030 ASSERT_EQ(1U, test_observer.deleted_ids().size());
1031 EXPECT_EQ(1, test_observer.deleted_ids()[0]);
1032
1033 ASSERT_EQ(1U, test_observer.subtree_deleted_ids().size());
1034 EXPECT_EQ(1, test_observer.subtree_deleted_ids()[0]);
1035
1036 ASSERT_EQ(1U, test_observer.created_ids().size());
1037 EXPECT_EQ(4, test_observer.created_ids()[0]);
1038
1039 ASSERT_EQ(1U, test_observer.subtree_creation_finished_ids().size());
1040 EXPECT_EQ(4, test_observer.subtree_creation_finished_ids()[0]);
1041
1042 ASSERT_EQ(1U, test_observer.subtree_reparented_finished_ids().size());
1043 EXPECT_EQ(2, test_observer.subtree_reparented_finished_ids()[0]);
1044
1045 EXPECT_EQ(0U, test_observer.node_creation_finished_ids().size());
1046 EXPECT_EQ(0U, test_observer.node_reparented_finished_ids().size());
1047
1048 ASSERT_TRUE(test_observer.root_changed());
1049}
1050
1051// UAF caught by ax_tree_fuzzer
1052TEST(AXTreeTest, BogusAXTree) {
1053 AXTreeUpdate initial_state;
1054 AXNodeData node;
1055 node.id = 0;
1056 initial_state.nodes.push_back(node);
1057 initial_state.nodes.push_back(node);
1058 ui::AXTree tree;
1059 tree.Unserialize(initial_state);
1060}
1061
1062// UAF caught by ax_tree_fuzzer
1063TEST(AXTreeTest, BogusAXTree2) {
1064 AXTreeUpdate initial_state;
1065 AXNodeData node;
1066 node.id = 0;
1067 initial_state.nodes.push_back(node);
1068 AXNodeData node2;
1069 node2.id = 0;
1070 node2.child_ids.push_back(0);
1071 node2.child_ids.push_back(0);
1072 initial_state.nodes.push_back(node2);
1073 ui::AXTree tree;
1074 tree.Unserialize(initial_state);
1075}
1076
1077// UAF caught by ax_tree_fuzzer
1078TEST(AXTreeTest, BogusAXTree3) {
1079 AXTreeUpdate initial_state;
1080 AXNodeData node;
1081 node.id = 0;
1082 node.child_ids.push_back(1);
1083 initial_state.nodes.push_back(node);
1084
1085 AXNodeData node2;
1086 node2.id = 1;
1087 node2.child_ids.push_back(1);
1088 node2.child_ids.push_back(1);
1089 initial_state.nodes.push_back(node2);
1090
1091 ui::AXTree tree;
1092 tree.Unserialize(initial_state);
1093}
1094
1095TEST(AXTreeTest, RoleAndStateChangeCallbacks) {
1096 AXTreeUpdate initial_state;
1097 initial_state.root_id = 1;
1098 initial_state.nodes.resize(1);
1099 initial_state.nodes[0].id = 1;
1100 initial_state.nodes[0].role = ax::mojom::Role::kButton;
1101 initial_state.nodes[0].SetCheckedState(ax::mojom::CheckedState::kTrue);
1102 initial_state.nodes[0].AddState(ax::mojom::State::kFocusable);
1103 AXTree tree(initial_state);
1104
1105 TestAXTreeObserver test_observer(&tree);
1106
1107 // Change the role and state.
1109 update.root_id = 1;
1110 update.nodes.resize(1);
1111 update.nodes[0].id = 1;
1112 update.nodes[0].role = ax::mojom::Role::kCheckBox;
1113 update.nodes[0].SetCheckedState(ax::mojom::CheckedState::kFalse);
1114 update.nodes[0].AddState(ax::mojom::State::kFocusable);
1115 update.nodes[0].AddState(ax::mojom::State::kVisited);
1117
1118 const std::vector<std::string>& change_log =
1119 test_observer.attribute_change_log();
1120 ASSERT_EQ(3U, change_log.size());
1121 EXPECT_EQ("Role changed from button to checkBox", change_log[0]);
1122 EXPECT_EQ("visited changed to true", change_log[1]);
1123 EXPECT_EQ("checkedState changed from 2 to 1", change_log[2]);
1124}
1125
1126TEST(AXTreeTest, AttributeChangeCallbacks) {
1127 AXTreeUpdate initial_state;
1128 initial_state.root_id = 1;
1129 initial_state.nodes.resize(1);
1130 initial_state.nodes[0].id = 1;
1131 initial_state.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kName,
1132 "N1");
1133 initial_state.nodes[0].AddStringAttribute(
1135 initial_state.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kLiveAtomic,
1136 true);
1137 initial_state.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kBusy,
1138 false);
1139 initial_state.nodes[0].AddFloatAttribute(
1141 initial_state.nodes[0].AddFloatAttribute(
1143 initial_state.nodes[0].AddFloatAttribute(
1145 initial_state.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 5);
1146 initial_state.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin,
1147 1);
1148 AXTree tree(initial_state);
1149
1150 TestAXTreeObserver test_observer(&tree);
1151
1152 // Change existing attributes.
1153 AXTreeUpdate update0;
1154 update0.root_id = 1;
1155 update0.nodes.resize(1);
1156 update0.nodes[0].id = 1;
1157 update0.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kName, "N2");
1158 update0.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kDescription,
1159 "D2");
1160 update0.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kLiveAtomic,
1161 false);
1162 update0.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kBusy, true);
1163 update0.nodes[0].AddFloatAttribute(
1165 update0.nodes[0].AddFloatAttribute(
1167 update0.nodes[0].AddFloatAttribute(
1169 update0.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 6);
1170 update0.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin, 2);
1171 EXPECT_TRUE(tree.Unserialize(update0));
1172
1173 const std::vector<std::string>& change_log =
1174 test_observer.attribute_change_log();
1175 ASSERT_EQ(9U, change_log.size());
1176 EXPECT_EQ("name changed from N1 to N2", change_log[0]);
1177 EXPECT_EQ("description changed from D1 to D2", change_log[1]);
1178 EXPECT_EQ("liveAtomic changed to false", change_log[2]);
1179 EXPECT_EQ("busy changed to true", change_log[3]);
1180 EXPECT_EQ("minValueForRange changed from 1.0 to 2.0", change_log[4]);
1181 EXPECT_EQ("maxValueForRange changed from 10.0 to 9.0", change_log[5]);
1182 EXPECT_EQ("stepValueForRange changed from 3.0 to 0.5", change_log[6]);
1183 EXPECT_EQ("scrollX changed from 5 to 6", change_log[7]);
1184 EXPECT_EQ("scrollXMin changed from 1 to 2", change_log[8]);
1185
1186 TestAXTreeObserver test_observer2(&tree);
1187
1188 // Add and remove attributes.
1189 AXTreeUpdate update1;
1190 update1.root_id = 1;
1191 update1.nodes.resize(1);
1192 update1.nodes[0].id = 1;
1193 update1.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kDescription,
1194 "D3");
1195 update1.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue, "V3");
1196 update1.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true);
1197 update1.nodes[0].AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
1198 5.0);
1199 update1.nodes[0].AddFloatAttribute(
1201 update1.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 7);
1202 update1.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollXMax, 10);
1203 EXPECT_TRUE(tree.Unserialize(update1));
1204
1205 const std::vector<std::string>& change_log2 =
1206 test_observer2.attribute_change_log();
1207 ASSERT_EQ(11U, change_log2.size());
1208 EXPECT_EQ("name changed from N2 to ", change_log2[0]);
1209 EXPECT_EQ("description changed from D2 to D3", change_log2[1]);
1210 EXPECT_EQ("value changed from to V3", change_log2[2]);
1211 EXPECT_EQ("busy changed to false", change_log2[3]);
1212 EXPECT_EQ("modal changed to true", change_log2[4]);
1213 EXPECT_EQ("minValueForRange changed from 2.0 to 0.0", change_log2[5]);
1214 EXPECT_EQ("stepValueForRange changed from 3.0 to 0.5", change_log[6]);
1215 EXPECT_EQ("valueForRange changed from 0.0 to 5.0", change_log2[7]);
1216 EXPECT_EQ("scrollXMin changed from 2 to 0", change_log2[8]);
1217 EXPECT_EQ("scrollX changed from 6 to 7", change_log2[9]);
1218 EXPECT_EQ("scrollXMax changed from 0 to 10", change_log2[10]);
1219}
1220
1221TEST(AXTreeTest, IntListChangeCallbacks) {
1222 std::vector<int32_t> one;
1223 one.push_back(1);
1224
1225 std::vector<int32_t> two;
1226 two.push_back(2);
1227 two.push_back(2);
1228
1229 std::vector<int32_t> three;
1230 three.push_back(3);
1231
1232 AXTreeUpdate initial_state;
1233 initial_state.root_id = 1;
1234 initial_state.nodes.resize(1);
1235 initial_state.nodes[0].id = 1;
1236 initial_state.nodes[0].AddIntListAttribute(
1238 initial_state.nodes[0].AddIntListAttribute(
1240 AXTree tree(initial_state);
1241
1242 TestAXTreeObserver test_observer(&tree);
1243
1244 // Change existing attributes.
1245 AXTreeUpdate update0;
1246 update0.root_id = 1;
1247 update0.nodes.resize(1);
1248 update0.nodes[0].id = 1;
1249 update0.nodes[0].AddIntListAttribute(
1251 update0.nodes[0].AddIntListAttribute(
1253 EXPECT_TRUE(tree.Unserialize(update0));
1254
1255 const std::vector<std::string>& change_log =
1256 test_observer.attribute_change_log();
1257 ASSERT_EQ(2U, change_log.size());
1258 EXPECT_EQ("controlsIds changed from 1 to 2,2", change_log[0]);
1259 EXPECT_EQ("radioGroupIds changed from 2,2 to 3", change_log[1]);
1260
1261 TestAXTreeObserver test_observer2(&tree);
1262
1263 // Add and remove attributes.
1264 AXTreeUpdate update1;
1265 update1.root_id = 1;
1266 update1.nodes.resize(1);
1267 update1.nodes[0].id = 1;
1268 update1.nodes[0].AddIntListAttribute(
1270 update1.nodes[0].AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds,
1271 three);
1272 EXPECT_TRUE(tree.Unserialize(update1));
1273
1274 const std::vector<std::string>& change_log2 =
1275 test_observer2.attribute_change_log();
1276 ASSERT_EQ(3U, change_log2.size());
1277 EXPECT_EQ("controlsIds changed from 2,2 to ", change_log2[0]);
1278 EXPECT_EQ("radioGroupIds changed from 3 to 2,2", change_log2[1]);
1279 EXPECT_EQ("flowtoIds changed from to 3", change_log2[2]);
1280}
1281
1282// Create a very simple tree and make sure that we can get the bounds of
1283// any node.
1284TEST(AXTreeTest, GetBoundsBasic) {
1285 AXTreeUpdate tree_update;
1286 tree_update.root_id = 1;
1287 tree_update.nodes.resize(2);
1288 tree_update.nodes[0].id = 1;
1289 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1290 tree_update.nodes[0].child_ids.push_back(2);
1291 tree_update.nodes[1].id = 2;
1292 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(100, 10, 400, 300);
1293 AXTree tree(tree_update);
1294
1295 EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1));
1296 EXPECT_EQ("(100, 10) size (400 x 300)", GetBoundsAsString(tree, 2));
1297}
1298
1299// If a node doesn't specify its location but at least one child does have
1300// a location, its computed bounds should be the union of all child bounds.
1301TEST(AXTreeTest, EmptyNodeBoundsIsUnionOfChildren) {
1302 AXTreeUpdate tree_update;
1303 tree_update.root_id = 1;
1304 tree_update.nodes.resize(4);
1305 tree_update.nodes[0].id = 1;
1306 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1307 tree_update.nodes[0].child_ids.push_back(2);
1308 tree_update.nodes[1].id = 2;
1309 tree_update.nodes[1].relative_bounds.bounds =
1310 gfx::RectF(); // Deliberately empty.
1311 tree_update.nodes[1].child_ids.push_back(3);
1312 tree_update.nodes[1].child_ids.push_back(4);
1313 tree_update.nodes[2].id = 3;
1314 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(100, 10, 400, 20);
1315 tree_update.nodes[3].id = 4;
1316 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(200, 30, 400, 20);
1317
1318 AXTree tree(tree_update);
1319 EXPECT_EQ("(100, 10) size (500 x 40)", GetBoundsAsString(tree, 2));
1320}
1321
1322// If a node doesn't specify its location but at least one child does have
1323// a location, it will be offscreen if all of its children are offscreen.
1324TEST(AXTreeTest, EmptyNodeNotOffscreenEvenIfAllChildrenOffscreen) {
1325 AXTreeUpdate tree_update;
1326 tree_update.root_id = 1;
1327 tree_update.nodes.resize(4);
1328 tree_update.nodes[0].id = 1;
1329 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1330 tree_update.nodes[0].role = ax::mojom::Role::kRootWebArea;
1331 tree_update.nodes[0].AddBoolAttribute(
1333 tree_update.nodes[0].child_ids.push_back(2);
1334 tree_update.nodes[1].id = 2;
1335 tree_update.nodes[1].relative_bounds.bounds =
1336 gfx::RectF(); // Deliberately empty.
1337 tree_update.nodes[1].child_ids.push_back(3);
1338 tree_update.nodes[1].child_ids.push_back(4);
1339 // Both children are offscreen
1340 tree_update.nodes[2].id = 3;
1341 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(900, 10, 400, 20);
1342 tree_update.nodes[3].id = 4;
1343 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(1000, 30, 400, 20);
1344
1345 AXTree tree(tree_update);
1346 EXPECT_FALSE(IsNodeOffscreen(tree, 2));
1347 EXPECT_TRUE(IsNodeOffscreen(tree, 3));
1348 EXPECT_TRUE(IsNodeOffscreen(tree, 4));
1349}
1350
1351// Test that getting the bounds of a node works when there's a transform.
1352TEST(AXTreeTest, GetBoundsWithTransform) {
1353 AXTreeUpdate tree_update;
1354 tree_update.root_id = 1;
1355 tree_update.nodes.resize(3);
1356 tree_update.nodes[0].id = 1;
1357 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 400, 300);
1358 tree_update.nodes[0].relative_bounds.transform =
1359 std::make_unique<gfx::Transform>();
1360 tree_update.nodes[0].relative_bounds.transform->Scale(2.0, 2.0);
1361 tree_update.nodes[0].child_ids.push_back(2);
1362 tree_update.nodes[0].child_ids.push_back(3);
1363 tree_update.nodes[1].id = 2;
1364 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(20, 10, 50, 5);
1365 tree_update.nodes[2].id = 3;
1366 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(20, 30, 50, 5);
1367 tree_update.nodes[2].relative_bounds.transform =
1368 std::make_unique<gfx::Transform>();
1369 tree_update.nodes[2].relative_bounds.transform->Scale(2.0, 2.0);
1370
1371 AXTree tree(tree_update);
1372 EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1));
1373 EXPECT_EQ("(40, 20) size (100 x 10)", GetBoundsAsString(tree, 2));
1374 EXPECT_EQ("(80, 120) size (200 x 20)", GetBoundsAsString(tree, 3));
1375}
1376
1377// Test that getting the bounds of a node that's inside a container
1378// works correctly.
1379TEST(AXTreeTest, GetBoundsWithContainerId) {
1380 AXTreeUpdate tree_update;
1381 tree_update.root_id = 1;
1382 tree_update.nodes.resize(4);
1383 tree_update.nodes[0].id = 1;
1384 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1385 tree_update.nodes[0].child_ids.push_back(2);
1386 tree_update.nodes[1].id = 2;
1387 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(100, 50, 600, 500);
1388 tree_update.nodes[1].child_ids.push_back(3);
1389 tree_update.nodes[1].child_ids.push_back(4);
1390 tree_update.nodes[2].id = 3;
1391 tree_update.nodes[2].relative_bounds.offset_container_id = 2;
1392 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(20, 30, 50, 5);
1393 tree_update.nodes[3].id = 4;
1394 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(20, 30, 50, 5);
1395
1396 AXTree tree(tree_update);
1397 EXPECT_EQ("(120, 80) size (50 x 5)", GetBoundsAsString(tree, 3));
1398 EXPECT_EQ("(20, 30) size (50 x 5)", GetBoundsAsString(tree, 4));
1399}
1400
1401// Test that getting the bounds of a node that's inside a scrolling container
1402// works correctly.
1403TEST(AXTreeTest, GetBoundsWithScrolling) {
1404 AXTreeUpdate tree_update;
1405 tree_update.root_id = 1;
1406 tree_update.nodes.resize(3);
1407 tree_update.nodes[0].id = 1;
1408 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1409 tree_update.nodes[0].child_ids.push_back(2);
1410 tree_update.nodes[1].id = 2;
1411 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(100, 50, 600, 500);
1412 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 5);
1413 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kScrollY, 10);
1414 tree_update.nodes[1].child_ids.push_back(3);
1415 tree_update.nodes[2].id = 3;
1416 tree_update.nodes[2].relative_bounds.offset_container_id = 2;
1417 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(20, 30, 50, 5);
1418
1419 AXTree tree(tree_update);
1420 EXPECT_EQ("(115, 70) size (50 x 5)", GetBoundsAsString(tree, 3));
1421}
1422
1423// When a node has zero size, we try to get the bounds from an ancestor.
1424TEST(AXTreeTest, GetBoundsOfNodeWithZeroSize) {
1425 AXTreeUpdate tree_update;
1426 tree_update.root_id = 1;
1427 tree_update.nodes.resize(5);
1428 tree_update.nodes[0].id = 1;
1429 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1430 tree_update.nodes[0].child_ids = {2};
1431 tree_update.nodes[1].id = 2;
1432 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(100, 100, 300, 200);
1433 tree_update.nodes[1].child_ids = {3, 4, 5};
1434
1435 // This child has relative coordinates and no offset and no size.
1436 tree_update.nodes[2].id = 3;
1437 tree_update.nodes[2].relative_bounds.offset_container_id = 2;
1438 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(0, 0, 0, 0);
1439
1440 // This child has relative coordinates and an offset, but no size.
1441 tree_update.nodes[3].id = 4;
1442 tree_update.nodes[3].relative_bounds.offset_container_id = 2;
1443 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(20, 20, 0, 0);
1444
1445 // This child has absolute coordinates, an offset, and no size.
1446 tree_update.nodes[4].id = 5;
1447 tree_update.nodes[4].relative_bounds.bounds = gfx::RectF(120, 120, 0, 0);
1448
1449 AXTree tree(tree_update);
1450 EXPECT_EQ("(100, 100) size (300 x 200)", GetBoundsAsString(tree, 3));
1451 EXPECT_EQ("(120, 120) size (280 x 180)", GetBoundsAsString(tree, 4));
1452 EXPECT_EQ("(120, 120) size (280 x 180)", GetBoundsAsString(tree, 5));
1453}
1454
1455TEST(AXTreeTest, GetBoundsEmptyBoundsInheritsFromParent) {
1456 AXTreeUpdate tree_update;
1457 tree_update.root_id = 1;
1458 tree_update.nodes.resize(3);
1459 tree_update.nodes[0].id = 1;
1460 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1461 tree_update.nodes[1].AddBoolAttribute(
1463 tree_update.nodes[0].child_ids.push_back(2);
1464 tree_update.nodes[1].id = 2;
1465 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(300, 200, 100, 100);
1466 tree_update.nodes[1].child_ids.push_back(3);
1467 tree_update.nodes[2].id = 3;
1468 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF();
1469
1470 AXTree tree(tree_update);
1471 EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1));
1472 EXPECT_EQ("(300, 200) size (100 x 100)", GetBoundsAsString(tree, 2));
1473 EXPECT_EQ("(300, 200) size (100 x 100)", GetBoundsAsString(tree, 3));
1474 EXPECT_EQ("(0, 0) size (800 x 600)", GetUnclippedBoundsAsString(tree, 1));
1475 EXPECT_EQ("(300, 200) size (100 x 100)", GetUnclippedBoundsAsString(tree, 2));
1476 EXPECT_EQ("(300, 200) size (100 x 100)", GetUnclippedBoundsAsString(tree, 3));
1477 EXPECT_FALSE(IsNodeOffscreen(tree, 1));
1478 EXPECT_FALSE(IsNodeOffscreen(tree, 2));
1479 EXPECT_TRUE(IsNodeOffscreen(tree, 3));
1480}
1481
1482TEST(AXTreeTest, GetBoundsCropsChildToRoot) {
1483 AXTreeUpdate tree_update;
1484 tree_update.root_id = 1;
1485 tree_update.nodes.resize(5);
1486 tree_update.nodes[0].id = 1;
1487 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1488 tree_update.nodes[0].AddBoolAttribute(
1490 tree_update.nodes[0].child_ids.push_back(2);
1491 tree_update.nodes[0].child_ids.push_back(3);
1492 tree_update.nodes[0].child_ids.push_back(4);
1493 tree_update.nodes[0].child_ids.push_back(5);
1494 // Cropped in the top left
1495 tree_update.nodes[1].id = 2;
1496 tree_update.nodes[1].relative_bounds.bounds =
1497 gfx::RectF(-100, -100, 150, 150);
1498 // Cropped in the bottom right
1499 tree_update.nodes[2].id = 3;
1500 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(700, 500, 150, 150);
1501 // Offscreen on the top
1502 tree_update.nodes[3].id = 4;
1503 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(50, -200, 150, 150);
1504 // Offscreen on the bottom
1505 tree_update.nodes[4].id = 5;
1506 tree_update.nodes[4].relative_bounds.bounds = gfx::RectF(50, 700, 150, 150);
1507
1508 AXTree tree(tree_update);
1509 EXPECT_EQ("(0, 0) size (50 x 50)", GetBoundsAsString(tree, 2));
1510 EXPECT_EQ("(700, 500) size (100 x 100)", GetBoundsAsString(tree, 3));
1511 EXPECT_EQ("(50, 0) size (150 x 1)", GetBoundsAsString(tree, 4));
1512 EXPECT_EQ("(50, 599) size (150 x 1)", GetBoundsAsString(tree, 5));
1513
1514 // Check the unclipped bounds are as expected.
1515 EXPECT_EQ("(-100, -100) size (150 x 150)",
1516 GetUnclippedBoundsAsString(tree, 2));
1517 EXPECT_EQ("(700, 500) size (150 x 150)", GetUnclippedBoundsAsString(tree, 3));
1518 EXPECT_EQ("(50, -200) size (150 x 150)", GetUnclippedBoundsAsString(tree, 4));
1519 EXPECT_EQ("(50, 700) size (150 x 150)", GetUnclippedBoundsAsString(tree, 5));
1520}
1521
1522TEST(AXTreeTest, GetBoundsSetsOffscreenIfClipsChildren) {
1523 AXTreeUpdate tree_update;
1524 tree_update.root_id = 1;
1525 tree_update.nodes.resize(5);
1526 tree_update.nodes[0].id = 1;
1527 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1528 tree_update.nodes[0].AddBoolAttribute(
1530 tree_update.nodes[0].child_ids.push_back(2);
1531 tree_update.nodes[0].child_ids.push_back(3);
1532
1533 tree_update.nodes[1].id = 2;
1534 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(0, 0, 200, 200);
1535 tree_update.nodes[1].AddBoolAttribute(
1537 tree_update.nodes[1].child_ids.push_back(4);
1538
1539 tree_update.nodes[2].id = 3;
1540 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(0, 0, 200, 200);
1541 tree_update.nodes[2].child_ids.push_back(5);
1542
1543 // Clipped by its parent
1544 tree_update.nodes[3].id = 4;
1545 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(250, 250, 100, 100);
1546 tree_update.nodes[3].relative_bounds.offset_container_id = 2;
1547
1548 // Outside of its parent, but its parent does not clip children,
1549 // so it should not be offscreen.
1550 tree_update.nodes[4].id = 5;
1551 tree_update.nodes[4].relative_bounds.bounds = gfx::RectF(250, 250, 100, 100);
1552 tree_update.nodes[4].relative_bounds.offset_container_id = 3;
1553
1554 AXTree tree(tree_update);
1555 EXPECT_TRUE(IsNodeOffscreen(tree, 4));
1556 EXPECT_FALSE(IsNodeOffscreen(tree, 5));
1557}
1558
1559TEST(AXTreeTest, GetBoundsUpdatesOffscreen) {
1560 AXTreeUpdate tree_update;
1561 tree_update.root_id = 1;
1562 tree_update.nodes.resize(5);
1563 tree_update.nodes[0].id = 1;
1564 tree_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
1565 tree_update.nodes[0].role = ax::mojom::Role::kRootWebArea;
1566 tree_update.nodes[0].AddBoolAttribute(
1568 tree_update.nodes[0].child_ids.push_back(2);
1569 tree_update.nodes[0].child_ids.push_back(3);
1570 tree_update.nodes[0].child_ids.push_back(4);
1571 tree_update.nodes[0].child_ids.push_back(5);
1572 // Fully onscreen
1573 tree_update.nodes[1].id = 2;
1574 tree_update.nodes[1].relative_bounds.bounds = gfx::RectF(10, 10, 150, 150);
1575 // Cropped in the bottom right
1576 tree_update.nodes[2].id = 3;
1577 tree_update.nodes[2].relative_bounds.bounds = gfx::RectF(700, 500, 150, 150);
1578 // Offscreen on the top
1579 tree_update.nodes[3].id = 4;
1580 tree_update.nodes[3].relative_bounds.bounds = gfx::RectF(50, -200, 150, 150);
1581 // Offscreen on the bottom
1582 tree_update.nodes[4].id = 5;
1583 tree_update.nodes[4].relative_bounds.bounds = gfx::RectF(50, 700, 150, 150);
1584
1585 AXTree tree(tree_update);
1586 EXPECT_FALSE(IsNodeOffscreen(tree, 2));
1587 EXPECT_FALSE(IsNodeOffscreen(tree, 3));
1588 EXPECT_TRUE(IsNodeOffscreen(tree, 4));
1589 EXPECT_TRUE(IsNodeOffscreen(tree, 5));
1590}
1591
1592TEST(AXTreeTest, IntReverseRelations) {
1593 AXTreeUpdate initial_state;
1594 initial_state.root_id = 1;
1595 initial_state.nodes.resize(4);
1596 initial_state.nodes[0].id = 1;
1597 initial_state.nodes[0].AddIntAttribute(
1599 initial_state.nodes[0].child_ids.push_back(2);
1600 initial_state.nodes[0].child_ids.push_back(3);
1601 initial_state.nodes[0].child_ids.push_back(4);
1602 initial_state.nodes[1].id = 2;
1603 initial_state.nodes[2].id = 3;
1604 initial_state.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kMemberOfId,
1605 1);
1606 initial_state.nodes[3].id = 4;
1607 initial_state.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kMemberOfId,
1608 1);
1609 AXTree tree(initial_state);
1610
1611 auto reverse_active_descendant =
1613 ASSERT_EQ(1U, reverse_active_descendant.size());
1614 EXPECT_TRUE(base::Contains(reverse_active_descendant, 1));
1615
1616 reverse_active_descendant =
1618 ASSERT_EQ(0U, reverse_active_descendant.size());
1619
1620 auto reverse_errormessage =
1622 ASSERT_EQ(0U, reverse_errormessage.size());
1623
1624 auto reverse_member_of =
1626 ASSERT_EQ(2U, reverse_member_of.size());
1627 EXPECT_TRUE(base::Contains(reverse_member_of, 3));
1628 EXPECT_TRUE(base::Contains(reverse_member_of, 4));
1629
1630 AXTreeUpdate update = initial_state;
1631 update.nodes.resize(5);
1632 update.nodes[0].int_attributes.clear();
1634 5);
1635 update.nodes[0].child_ids.push_back(5);
1636 update.nodes[2].int_attributes.clear();
1637 update.nodes[4].id = 5;
1638 update.nodes[4].AddIntAttribute(ax::mojom::IntAttribute::kMemberOfId, 1);
1639
1641
1642 reverse_active_descendant =
1644 ASSERT_EQ(0U, reverse_active_descendant.size());
1645
1646 reverse_active_descendant =
1648 ASSERT_EQ(1U, reverse_active_descendant.size());
1649 EXPECT_TRUE(base::Contains(reverse_active_descendant, 1));
1650
1651 reverse_member_of =
1653 ASSERT_EQ(2U, reverse_member_of.size());
1654 EXPECT_TRUE(base::Contains(reverse_member_of, 4));
1655 EXPECT_TRUE(base::Contains(reverse_member_of, 5));
1656}
1657
1658TEST(AXTreeTest, IntListReverseRelations) {
1659 std::vector<int32_t> node_two;
1660 node_two.push_back(2);
1661
1662 std::vector<int32_t> nodes_two_three;
1663 nodes_two_three.push_back(2);
1664 nodes_two_three.push_back(3);
1665
1666 AXTreeUpdate initial_state;
1667 initial_state.root_id = 1;
1668 initial_state.nodes.resize(3);
1669 initial_state.nodes[0].id = 1;
1670 initial_state.nodes[0].AddIntListAttribute(
1672 initial_state.nodes[0].child_ids.push_back(2);
1673 initial_state.nodes[0].child_ids.push_back(3);
1674 initial_state.nodes[1].id = 2;
1675 initial_state.nodes[2].id = 3;
1676
1677 AXTree tree(initial_state);
1678
1679 auto reverse_labelled_by =
1681 ASSERT_EQ(1U, reverse_labelled_by.size());
1682 EXPECT_TRUE(base::Contains(reverse_labelled_by, 1));
1683
1684 reverse_labelled_by =
1686 ASSERT_EQ(0U, reverse_labelled_by.size());
1687
1688 // Change existing attributes.
1689 AXTreeUpdate update = initial_state;
1690 update.nodes[0].intlist_attributes.clear();
1691 update.nodes[0].AddIntListAttribute(
1694
1695 reverse_labelled_by =
1697 ASSERT_EQ(1U, reverse_labelled_by.size());
1698 EXPECT_TRUE(base::Contains(reverse_labelled_by, 1));
1699}
1700
1701TEST(AXTreeTest, DeletingNodeUpdatesReverseRelations) {
1702 AXTreeUpdate initial_state;
1703 initial_state.root_id = 1;
1704 initial_state.nodes.resize(3);
1705 initial_state.nodes[0].id = 1;
1706 initial_state.nodes[0].child_ids = {2, 3};
1707 initial_state.nodes[1].id = 2;
1708 initial_state.nodes[2].id = 3;
1709 initial_state.nodes[2].AddIntAttribute(
1711 AXTree tree(initial_state);
1712
1713 auto reverse_active_descendant =
1715 ASSERT_EQ(1U, reverse_active_descendant.size());
1716 EXPECT_TRUE(base::Contains(reverse_active_descendant, 3));
1717
1719 update.root_id = 1;
1720 update.nodes.resize(1);
1721 update.nodes[0].id = 1;
1722 update.nodes[0].child_ids = {2};
1724
1725 reverse_active_descendant =
1727 ASSERT_EQ(0U, reverse_active_descendant.size());
1728}
1729
1730TEST(AXTreeTest, ReverseRelationsDoNotKeepGrowing) {
1731 // The number of total entries in int_reverse_relations and
1732 // intlist_reverse_relations should not keep growing as the tree
1733 // changes.
1734
1735 AXTreeUpdate initial_state;
1736 initial_state.root_id = 1;
1737 initial_state.nodes.resize(2);
1738 initial_state.nodes[0].id = 1;
1739 initial_state.nodes[0].AddIntAttribute(
1741 initial_state.nodes[0].AddIntListAttribute(
1743 initial_state.nodes[0].child_ids.push_back(2);
1744 initial_state.nodes[1].id = 2;
1745 AXTree tree(initial_state);
1746
1747 for (int i = 0; i < 1000; ++i) {
1749 update.root_id = 1;
1750 update.nodes.resize(2);
1751 update.nodes[0].id = 1;
1752 update.nodes[1].id = i + 3;
1753 update.nodes[0].AddIntAttribute(
1755 update.nodes[0].AddIntListAttribute(
1757 update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kMemberOfId, 1);
1758 update.nodes[0].child_ids.push_back(update.nodes[1].id);
1760 }
1761
1762 size_t map_key_count = 0;
1763 size_t set_entry_count = 0;
1764 for (auto& iter : tree.int_reverse_relations()) {
1765 map_key_count += iter.second.size() + 1;
1766 for (auto it2 = iter.second.begin(); it2 != iter.second.end(); ++it2) {
1767 set_entry_count += it2->second.size();
1768 }
1769 }
1770
1771 // Note: 10 is arbitrary, the idea here is just that we mutated the tree
1772 // 1000 times, so if we have fewer than 10 entries in the maps / sets then
1773 // the map isn't growing / leaking. Same below.
1774 EXPECT_LT(map_key_count, 10U);
1775 EXPECT_LT(set_entry_count, 10U);
1776
1777 map_key_count = 0;
1778 set_entry_count = 0;
1779 for (auto& iter : tree.intlist_reverse_relations()) {
1780 map_key_count += iter.second.size() + 1;
1781 for (auto it2 = iter.second.begin(); it2 != iter.second.end(); ++it2) {
1782 set_entry_count += it2->second.size();
1783 }
1784 }
1785 EXPECT_LT(map_key_count, 10U);
1786 EXPECT_LT(set_entry_count, 10U);
1787}
1788
1789TEST(AXTreeTest, SkipIgnoredNodes) {
1790 AXTreeUpdate tree_update;
1791 tree_update.root_id = 1;
1792 tree_update.nodes.resize(5);
1793 tree_update.nodes[0].id = 1;
1794 tree_update.nodes[0].child_ids = {2, 3};
1795 tree_update.nodes[1].id = 2;
1796 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
1797 tree_update.nodes[1].child_ids = {4, 5};
1798 tree_update.nodes[2].id = 3;
1799 tree_update.nodes[3].id = 4;
1800 tree_update.nodes[4].id = 5;
1801
1802 AXTree tree(tree_update);
1803 AXNode* root = tree.root();
1804 ASSERT_EQ(2u, root->children().size());
1805 ASSERT_EQ(2, root->children()[0]->id());
1806 ASSERT_EQ(3, root->children()[1]->id());
1807
1808 EXPECT_EQ(3u, root->GetUnignoredChildCount());
1809 EXPECT_EQ(4, root->GetUnignoredChildAtIndex(0)->id());
1810 EXPECT_EQ(5, root->GetUnignoredChildAtIndex(1)->id());
1811 EXPECT_EQ(3, root->GetUnignoredChildAtIndex(2)->id());
1812 EXPECT_EQ(0u, root->GetUnignoredChildAtIndex(0)->GetUnignoredIndexInParent());
1813 EXPECT_EQ(1u, root->GetUnignoredChildAtIndex(1)->GetUnignoredIndexInParent());
1814 EXPECT_EQ(2u, root->GetUnignoredChildAtIndex(2)->GetUnignoredIndexInParent());
1815
1816 EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id());
1817}
1818
1819TEST(AXTreeTest, CachedUnignoredValues) {
1820 AXTreeUpdate initial_state;
1821 initial_state.root_id = 1;
1822 initial_state.nodes.resize(5);
1823 initial_state.nodes[0].id = 1;
1824 initial_state.nodes[0].child_ids = {2, 3};
1825 initial_state.nodes[1].id = 2;
1826 initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
1827 initial_state.nodes[1].child_ids = {4, 5};
1828 initial_state.nodes[2].id = 3;
1829 initial_state.nodes[3].id = 4;
1830 initial_state.nodes[4].id = 5;
1831
1832 AXTree tree(initial_state);
1833 AXNode* root = tree.root();
1834 ASSERT_EQ(2u, root->children().size());
1835 ASSERT_EQ(2, root->children()[0]->id());
1836 ASSERT_EQ(3, root->children()[1]->id());
1837
1838 EXPECT_EQ(3u, root->GetUnignoredChildCount());
1839 EXPECT_EQ(4, root->GetUnignoredChildAtIndex(0)->id());
1840 EXPECT_EQ(5, root->GetUnignoredChildAtIndex(1)->id());
1841 EXPECT_EQ(3, root->GetUnignoredChildAtIndex(2)->id());
1842 EXPECT_EQ(0u, root->GetUnignoredChildAtIndex(0)->GetUnignoredIndexInParent());
1843 EXPECT_EQ(1u, root->GetUnignoredChildAtIndex(1)->GetUnignoredIndexInParent());
1844 EXPECT_EQ(2u, root->GetUnignoredChildAtIndex(2)->GetUnignoredIndexInParent());
1845
1846 EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id());
1847
1848 // Ensure when a node goes from ignored to unignored, its children have their
1849 // unignored_index_in_parent updated.
1850 AXTreeUpdate update = initial_state;
1851 update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
1852
1854
1855 root = tree.root();
1856 EXPECT_EQ(2u, root->GetUnignoredChildCount());
1857 EXPECT_EQ(2, root->GetUnignoredChildAtIndex(0)->id());
1858 EXPECT_EQ(2u, tree.GetFromId(2)->GetUnignoredChildCount());
1859 EXPECT_EQ(0u, tree.GetFromId(4)->GetUnignoredIndexInParent());
1860 EXPECT_EQ(1u, tree.GetFromId(5)->GetUnignoredIndexInParent());
1861
1862 // Ensure when a node goes from unignored to unignored, siblings are correctly
1863 // updated.
1864 AXTreeUpdate update2 = update;
1865 update2.nodes[3].AddState(ax::mojom::State::kIgnored);
1866
1867 EXPECT_TRUE(tree.Unserialize(update2));
1868
1869 EXPECT_EQ(1u, tree.GetFromId(2)->GetUnignoredChildCount());
1870 EXPECT_EQ(0u, tree.GetFromId(5)->GetUnignoredIndexInParent());
1871
1872 // Ensure siblings of a deleted node are updated.
1873 AXTreeUpdate update3 = update2;
1874 update3.nodes.resize(1);
1875 update3.nodes[0].id = 1;
1876 update3.nodes[0].child_ids = {3};
1877
1878 EXPECT_TRUE(tree.Unserialize(update3));
1879
1880 EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount());
1881 EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
1882
1883 // Ensure new nodes are correctly updated.
1884 AXTreeUpdate update4 = update3;
1885 update4.nodes.resize(3);
1886 update4.nodes[0].id = 1;
1887 update4.nodes[0].child_ids = {3, 6};
1888 update4.nodes[1].id = 6;
1889 update4.nodes[1].child_ids = {7};
1890 update4.nodes[2].id = 7;
1891
1892 EXPECT_TRUE(tree.Unserialize(update4));
1893
1894 EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
1895 EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
1896 EXPECT_EQ(1u, tree.GetFromId(6)->GetUnignoredIndexInParent());
1897 EXPECT_EQ(0u, tree.GetFromId(7)->GetUnignoredIndexInParent());
1898
1899 // Ensure reparented nodes are correctly updated.
1900 AXTreeUpdate update5 = update4;
1901 update5.nodes.resize(2);
1902 update5.node_id_to_clear = 6;
1903 update5.nodes[0].id = 1;
1904 update5.nodes[0].child_ids = {3, 7};
1905 update5.nodes[1].id = 7;
1906 update5.nodes[1].child_ids = {};
1907
1908 EXPECT_TRUE(tree.Unserialize(update5));
1909
1910 EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
1911 EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
1912 EXPECT_EQ(1u, tree.GetFromId(7)->GetUnignoredIndexInParent());
1913
1914 AXTreeUpdate update6;
1915 update6.nodes.resize(1);
1916 update6.nodes[0].id = 7;
1917 update6.nodes[0].AddState(ax::mojom::State::kIgnored);
1918
1919 EXPECT_TRUE(tree.Unserialize(update6));
1920
1921 EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount());
1922 EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
1923
1924 AXTreeUpdate update7 = update6;
1925 update7.nodes.resize(2);
1926 update7.nodes[0].id = 7;
1927 update7.nodes[0].child_ids = {8};
1928 update7.nodes[1].id = 8;
1929
1930 EXPECT_TRUE(tree.Unserialize(update7));
1931
1932 EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount());
1933 EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent());
1934}
1935
1936TEST(AXTreeTest, TestRecursionUnignoredChildCount) {
1937 AXTreeUpdate tree_update;
1938 tree_update.root_id = 1;
1939 tree_update.nodes.resize(5);
1940 tree_update.nodes[0].id = 1;
1941 tree_update.nodes[0].child_ids = {2, 3};
1942 tree_update.nodes[1].id = 2;
1943 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
1944 tree_update.nodes[1].child_ids = {4};
1945 tree_update.nodes[2].id = 3;
1946 tree_update.nodes[2].AddState(ax::mojom::State::kIgnored);
1947 tree_update.nodes[3].id = 4;
1948 tree_update.nodes[3].child_ids = {5};
1949 tree_update.nodes[3].AddState(ax::mojom::State::kIgnored);
1950 tree_update.nodes[4].id = 5;
1951 AXTree tree(tree_update);
1952
1953 AXNode* root = tree.root();
1954 EXPECT_EQ(2u, root->children().size());
1955 EXPECT_EQ(1u, root->GetUnignoredChildCount());
1956 EXPECT_EQ(5, root->GetUnignoredChildAtIndex(0)->id());
1957 AXNode* unignored = tree.GetFromId(5);
1958 EXPECT_EQ(0u, unignored->GetUnignoredChildCount());
1959}
1960
1961TEST(AXTreeTest, NullUnignoredChildren) {
1962 AXTreeUpdate tree_update;
1963 tree_update.root_id = 1;
1964 tree_update.nodes.resize(3);
1965 tree_update.nodes[0].id = 1;
1966 tree_update.nodes[0].child_ids = {2, 3};
1967 tree_update.nodes[1].id = 2;
1968 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
1969 tree_update.nodes[2].id = 3;
1970 tree_update.nodes[2].AddState(ax::mojom::State::kIgnored);
1971 AXTree tree(tree_update);
1972
1973 AXNode* root = tree.root();
1974 EXPECT_EQ(2u, root->children().size());
1975 EXPECT_EQ(0u, root->GetUnignoredChildCount());
1976 EXPECT_EQ(nullptr, root->GetUnignoredChildAtIndex(0));
1977 EXPECT_EQ(nullptr, root->GetUnignoredChildAtIndex(1));
1978}
1979
1980TEST(AXTreeTest, UnignoredChildIteratorIncrementDecrementPastEnd) {
1981 AXTreeUpdate tree_update;
1982
1983 // RootWebArea #1
1984 // ++StaticText "text1" #2
1985
1986 tree_update.root_id = 1;
1987 tree_update.nodes.resize(2);
1988
1989 tree_update.nodes[0].id = 1;
1990 tree_update.nodes[0].role = ax::mojom::Role::kWebArea;
1991 tree_update.nodes[0].child_ids = {2};
1992
1993 tree_update.nodes[1].id = 2;
1994 tree_update.nodes[1].role = ax::mojom::Role::kStaticText;
1995 tree_update.nodes[1].SetName("text1");
1996
1997 AXTree tree(tree_update);
1998 AXNode* root = tree.root();
1999
2000 {
2001 {
2002 AXNode::UnignoredChildIterator root_unignored_iter =
2003 root->UnignoredChildrenBegin();
2004 EXPECT_EQ(2, root_unignored_iter->id());
2005 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2007
2008 // Call unignored child iterator on root and increment, we should reach
2009 // the end since there is only one iterator element.
2010 EXPECT_EQ(root->UnignoredChildrenEnd(), ++root_unignored_iter);
2011
2012 // We increment past the end, and we should still stay at the end.
2013 EXPECT_EQ(root->UnignoredChildrenEnd(), ++root_unignored_iter);
2014
2015 // When we decrement from the end, we should get the last iterator element
2016 // "text1".
2017 --root_unignored_iter;
2018 EXPECT_EQ(2, root_unignored_iter->id());
2019 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2021 }
2022
2023 {
2024 AXNode::UnignoredChildIterator root_unignored_iter =
2025 root->UnignoredChildrenBegin();
2026 EXPECT_EQ(2, root_unignored_iter->id());
2027 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2029
2030 // Call unignored child iterator on root and decrement from the beginning,
2031 // we should stay at the beginning.
2032 --root_unignored_iter;
2033 EXPECT_EQ(2, root_unignored_iter->id());
2034 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2036
2037 // When we decrement past the beginning, we should still stay at the
2038 // beginning.
2039 --root_unignored_iter;
2040 EXPECT_EQ(2, root_unignored_iter->id());
2041 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2043
2044 // We increment past the end, and we should still reach the end.
2045 EXPECT_EQ(root->UnignoredChildrenEnd(), ++root_unignored_iter);
2046 }
2047 }
2048}
2049
2050TEST(AXTreeTest, UnignoredChildIteratorIgnoredContainerSiblings) {
2051 AXTreeUpdate tree_update;
2052
2053 // RootWebArea #1
2054 // ++genericContainer IGNORED #2
2055 // ++++StaticText "text1" #3
2056 // ++genericContainer IGNORED #4
2057 // ++++StaticText "text2" #5
2058 // ++genericContainer IGNORED #6
2059 // ++++StaticText "text3" #7
2060
2061 tree_update.root_id = 1;
2062 tree_update.nodes.resize(7);
2063
2064 tree_update.nodes[0].id = 1;
2065 tree_update.nodes[0].role = ax::mojom::Role::kWebArea;
2066 tree_update.nodes[0].child_ids = {2, 4, 6};
2067
2068 tree_update.nodes[1].id = 2;
2069 tree_update.nodes[1].child_ids = {3};
2070 tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer;
2071 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2072
2073 tree_update.nodes[2].id = 3;
2074 tree_update.nodes[2].role = ax::mojom::Role::kStaticText;
2075 tree_update.nodes[2].SetName("text1");
2076
2077 tree_update.nodes[3].id = 4;
2078 tree_update.nodes[3].child_ids = {5};
2079 tree_update.nodes[3].role = ax::mojom::Role::kGenericContainer;
2080 tree_update.nodes[3].AddState(ax::mojom::State::kIgnored);
2081
2082 tree_update.nodes[4].id = 5;
2083 tree_update.nodes[4].role = ax::mojom::Role::kStaticText;
2084 tree_update.nodes[4].SetName("text2");
2085
2086 tree_update.nodes[5].id = 6;
2087 tree_update.nodes[5].child_ids = {7};
2088 tree_update.nodes[5].role = ax::mojom::Role::kGenericContainer;
2089 tree_update.nodes[5].AddState(ax::mojom::State::kIgnored);
2090
2091 tree_update.nodes[6].id = 7;
2092 tree_update.nodes[6].role = ax::mojom::Role::kStaticText;
2093 tree_update.nodes[6].SetName("text3");
2094
2095 AXTree tree(tree_update);
2096
2097 {
2098 // Call unignored child iterator on root and iterate till the end, we should
2099 // get "text1", "text2", "text3" respectively because the sibling text nodes
2100 // share the same parent (i.e. root) as |unignored_iter|.
2101 AXNode* root = tree.root();
2102 AXNode::UnignoredChildIterator root_unignored_iter =
2103 root->UnignoredChildrenBegin();
2104 EXPECT_EQ(3, root_unignored_iter->id());
2105 EXPECT_EQ("text1", root_unignored_iter->GetStringAttribute(
2107
2108 EXPECT_EQ(5, (++root_unignored_iter)->id());
2109 EXPECT_EQ("text2",
2110 (*root_unignored_iter)
2111 .GetStringAttribute(ax::mojom::StringAttribute::kName));
2112
2113 EXPECT_EQ(7, (++root_unignored_iter)->id());
2114 EXPECT_EQ("text3", root_unignored_iter->GetStringAttribute(
2116 EXPECT_EQ(root->UnignoredChildrenEnd(), ++root_unignored_iter);
2117 }
2118
2119 {
2120 // Call unignored child iterator on the ignored generic container of "text1"
2121 // (id=2), When we iterate to the next of "text1", we should
2122 // reach the end because the sibling text node "text2" does not share the
2123 // same parent as |unignored_iter| of "text1".
2124 AXNode* text1_ignored_container = tree.GetFromId(2);
2125 AXNode::UnignoredChildIterator unignored_iter =
2126 text1_ignored_container->UnignoredChildrenBegin();
2127 EXPECT_EQ(3, unignored_iter->id());
2128 EXPECT_EQ("text1", unignored_iter->GetStringAttribute(
2130 // The next child of "text1" should be the end.
2131 EXPECT_EQ(text1_ignored_container->UnignoredChildrenEnd(),
2132 ++unignored_iter);
2133
2134 // Call unignored child iterator on the ignored generic container of "text2"
2135 // (id=4), When we iterate to the previous of "text2", we should
2136 // reach the end because the sibling text node "text1" does not share the
2137 // same parent as |unignored_iter| of "text2".
2138 AXNode* text2_ignored_container = tree.GetFromId(4);
2139 unignored_iter = text2_ignored_container->UnignoredChildrenBegin();
2140 EXPECT_EQ(5, unignored_iter->id());
2141 EXPECT_EQ("text2", unignored_iter->GetStringAttribute(
2143 // Decrement the iterator of "text2" should still remain on "text2" since
2144 // the beginning of iterator is "text2."
2145 --unignored_iter;
2146 EXPECT_EQ(5, unignored_iter->id());
2147 EXPECT_EQ("text2", unignored_iter->GetStringAttribute(
2149 }
2150}
2151
2152TEST(AXTreeTest, UnignoredChildIterator) {
2153 AXTreeUpdate tree_update;
2154 // (i) => node is ignored
2155 // 1
2156 // |__________
2157 // | | |
2158 // 2(i) 3 4
2159 // |_______________________
2160 // | | | |
2161 // 5 6 7(i) 8(i)
2162 // | | |________
2163 // | | | |
2164 // 9 10(i) 11(i) 12
2165 // | |____
2166 // | | |
2167 // 13(i) 14 15
2168 tree_update.root_id = 1;
2169 tree_update.nodes.resize(15);
2170 tree_update.nodes[0].id = 1;
2171 tree_update.nodes[0].child_ids = {2, 3, 4};
2172
2173 tree_update.nodes[1].id = 2;
2174 tree_update.nodes[1].child_ids = {5, 6, 7, 8};
2175 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2176
2177 tree_update.nodes[2].id = 3;
2178 tree_update.nodes[3].id = 4;
2179
2180 tree_update.nodes[4].id = 5;
2181 tree_update.nodes[4].child_ids = {9};
2182
2183 tree_update.nodes[5].id = 6;
2184 tree_update.nodes[5].child_ids = {10};
2185
2186 tree_update.nodes[6].id = 7;
2187 tree_update.nodes[6].child_ids = {11, 12};
2188 tree_update.nodes[6].AddState(ax::mojom::State::kIgnored);
2189
2190 tree_update.nodes[7].id = 8;
2191 tree_update.nodes[7].AddState(ax::mojom::State::kIgnored);
2192
2193 tree_update.nodes[8].id = 9;
2194
2195 tree_update.nodes[9].id = 10;
2196 tree_update.nodes[9].child_ids = {13};
2197 tree_update.nodes[9].AddState(ax::mojom::State::kIgnored);
2198
2199 tree_update.nodes[10].id = 11;
2200 tree_update.nodes[10].child_ids = {14, 15};
2201 tree_update.nodes[10].AddState(ax::mojom::State::kIgnored);
2202
2203 tree_update.nodes[11].id = 12;
2204
2205 tree_update.nodes[12].id = 13;
2206 tree_update.nodes[12].AddState(ax::mojom::State::kIgnored);
2207
2208 tree_update.nodes[13].id = 14;
2209
2210 tree_update.nodes[14].id = 15;
2211
2212 AXTree tree(tree_update);
2213 AXNode* root = tree.root();
2214
2215 // Test traversal
2216 // UnignoredChildren(root) = {5, 6, 14, 15, 12, 3, 4}
2217 AXNode::UnignoredChildIterator unignored_iterator =
2218 root->UnignoredChildrenBegin();
2219 EXPECT_EQ(5, unignored_iterator->id());
2220
2221 EXPECT_EQ(6, (++unignored_iterator)->id());
2222
2223 EXPECT_EQ(14, (++unignored_iterator)->id());
2224
2225 EXPECT_EQ(15, (++unignored_iterator)->id());
2226
2227 EXPECT_EQ(14, (--unignored_iterator)->id());
2228
2229 EXPECT_EQ(6, (--unignored_iterator)->id());
2230
2231 EXPECT_EQ(14, (++unignored_iterator)->id());
2232
2233 EXPECT_EQ(15, (++unignored_iterator)->id());
2234
2235 EXPECT_EQ(12, (++unignored_iterator)->id());
2236
2237 EXPECT_EQ(3, (++unignored_iterator)->id());
2238
2239 EXPECT_EQ(4, (++unignored_iterator)->id());
2240
2241 EXPECT_EQ(root->UnignoredChildrenEnd(), ++unignored_iterator);
2242
2243 // test empty list
2244 // UnignoredChildren(3) = {}
2245 AXNode* node3 = tree.GetFromId(3);
2246 unignored_iterator = node3->UnignoredChildrenBegin();
2247 EXPECT_EQ(node3->UnignoredChildrenEnd(), unignored_iterator);
2248
2249 // empty list from ignored node with no children
2250 // UnignoredChildren(8) = {}
2251 AXNode* node8 = tree.GetFromId(8);
2252 unignored_iterator = node8->UnignoredChildrenBegin();
2253 EXPECT_EQ(node8->UnignoredChildrenEnd(), unignored_iterator);
2254
2255 // empty list from ignored node with unignored children
2256 // UnignoredChildren(11) = {}
2257 AXNode* node11 = tree.GetFromId(11);
2258 unignored_iterator = node11->UnignoredChildrenBegin();
2259 EXPECT_EQ(14, unignored_iterator->id());
2260
2261 // Two UnignoredChildIterators from the same parent at the same position
2262 // should be equivalent, even in end position.
2263 unignored_iterator = root->UnignoredChildrenBegin();
2264 AXNode::UnignoredChildIterator unignored_iterator2 =
2265 root->UnignoredChildrenBegin();
2266 auto end = root->UnignoredChildrenEnd();
2267 while (unignored_iterator != end) {
2268 ASSERT_EQ(unignored_iterator, unignored_iterator2);
2269 ++unignored_iterator;
2270 ++unignored_iterator2;
2271 }
2272 ASSERT_EQ(unignored_iterator, unignored_iterator2);
2273}
2274
2275TEST(AXTreeTest, UnignoredAccessors) {
2276 AXTreeUpdate tree_update;
2277 // (i) => node is ignored
2278 // 1
2279 // |__________
2280 // | | |
2281 // 2(i) 3 4
2282 // |_______________________
2283 // | | | |
2284 // 5 6 7(i) 8(i)
2285 // | | |________
2286 // | | | |
2287 // 9 10(i) 11(i) 12
2288 // | |____
2289 // | | |
2290 // 13(i) 14 15
2291 // | |
2292 // 16 17(i)
2293 tree_update.root_id = 1;
2294 tree_update.nodes.resize(17);
2295 tree_update.nodes[0].id = 1;
2296 tree_update.nodes[0].child_ids = {2, 3, 4};
2297
2298 tree_update.nodes[1].id = 2;
2299 tree_update.nodes[1].child_ids = {5, 6, 7, 8};
2300 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2301
2302 tree_update.nodes[2].id = 3;
2303 tree_update.nodes[3].id = 4;
2304
2305 tree_update.nodes[4].id = 5;
2306 tree_update.nodes[4].child_ids = {9};
2307
2308 tree_update.nodes[5].id = 6;
2309 tree_update.nodes[5].child_ids = {10};
2310
2311 tree_update.nodes[6].id = 7;
2312 tree_update.nodes[6].child_ids = {11, 12};
2313 tree_update.nodes[6].AddState(ax::mojom::State::kIgnored);
2314
2315 tree_update.nodes[7].id = 8;
2316 tree_update.nodes[7].AddState(ax::mojom::State::kIgnored);
2317
2318 tree_update.nodes[8].id = 9;
2319
2320 tree_update.nodes[9].id = 10;
2321 tree_update.nodes[9].child_ids = {13};
2322 tree_update.nodes[9].AddState(ax::mojom::State::kIgnored);
2323
2324 tree_update.nodes[10].id = 11;
2325 tree_update.nodes[10].child_ids = {14, 15};
2326 tree_update.nodes[10].AddState(ax::mojom::State::kIgnored);
2327
2328 tree_update.nodes[11].id = 12;
2329
2330 tree_update.nodes[12].id = 13;
2331 tree_update.nodes[12].child_ids = {16};
2332 tree_update.nodes[12].AddState(ax::mojom::State::kIgnored);
2333
2334 tree_update.nodes[13].id = 14;
2335 tree_update.nodes[13].child_ids = {17};
2336
2337 tree_update.nodes[14].id = 15;
2338
2339 tree_update.nodes[15].id = 16;
2340
2341 tree_update.nodes[16].id = 17;
2342 tree_update.nodes[16].AddState(ax::mojom::State::kIgnored);
2343
2344 AXTree tree(tree_update);
2345
2346 EXPECT_EQ(4, tree.GetFromId(1)->GetLastUnignoredChild()->id());
2347 EXPECT_EQ(12, tree.GetFromId(2)->GetLastUnignoredChild()->id());
2348 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetLastUnignoredChild());
2349 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetLastUnignoredChild());
2350 EXPECT_EQ(9, tree.GetFromId(5)->GetLastUnignoredChild()->id());
2351 EXPECT_EQ(16, tree.GetFromId(6)->GetLastUnignoredChild()->id());
2352 EXPECT_EQ(12, tree.GetFromId(7)->GetLastUnignoredChild()->id());
2353 EXPECT_EQ(nullptr, tree.GetFromId(8)->GetLastUnignoredChild());
2354 EXPECT_EQ(nullptr, tree.GetFromId(9)->GetLastUnignoredChild());
2355 EXPECT_EQ(16, tree.GetFromId(10)->GetLastUnignoredChild()->id());
2356 EXPECT_EQ(15, tree.GetFromId(11)->GetLastUnignoredChild()->id());
2357 EXPECT_EQ(nullptr, tree.GetFromId(12)->GetLastUnignoredChild());
2358 EXPECT_EQ(16, tree.GetFromId(13)->GetLastUnignoredChild()->id());
2359 EXPECT_EQ(nullptr, tree.GetFromId(14)->GetLastUnignoredChild());
2360 EXPECT_EQ(nullptr, tree.GetFromId(15)->GetLastUnignoredChild());
2361 EXPECT_EQ(nullptr, tree.GetFromId(16)->GetLastUnignoredChild());
2362 EXPECT_EQ(nullptr, tree.GetFromId(17)->GetLastUnignoredChild());
2363}
2364
2365TEST(AXTreeTest, UnignoredNextPreviousChild) {
2366 AXTreeUpdate tree_update;
2367 // (i) => node is ignored
2368 // 1
2369 // |__________
2370 // | | |
2371 // 2(i) 3 4
2372 // |_______________________
2373 // | | | |
2374 // 5 6 7(i) 8(i)
2375 // | | |________
2376 // | | | |
2377 // 9 10(i) 11(i) 12
2378 // | |____
2379 // | | |
2380 // 13(i) 14 15
2381 // |
2382 // 16
2383 tree_update.root_id = 1;
2384 tree_update.nodes.resize(16);
2385 tree_update.nodes[0].id = 1;
2386 tree_update.nodes[0].child_ids = {2, 3, 4};
2387
2388 tree_update.nodes[1].id = 2;
2389 tree_update.nodes[1].child_ids = {5, 6, 7, 8};
2390 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2391
2392 tree_update.nodes[2].id = 3;
2393 tree_update.nodes[3].id = 4;
2394
2395 tree_update.nodes[4].id = 5;
2396 tree_update.nodes[4].child_ids = {9};
2397
2398 tree_update.nodes[5].id = 6;
2399 tree_update.nodes[5].child_ids = {10};
2400
2401 tree_update.nodes[6].id = 7;
2402 tree_update.nodes[6].child_ids = {11, 12};
2403 tree_update.nodes[6].AddState(ax::mojom::State::kIgnored);
2404
2405 tree_update.nodes[7].id = 8;
2406 tree_update.nodes[7].AddState(ax::mojom::State::kIgnored);
2407
2408 tree_update.nodes[8].id = 9;
2409
2410 tree_update.nodes[9].id = 10;
2411 tree_update.nodes[9].child_ids = {13};
2412 tree_update.nodes[9].AddState(ax::mojom::State::kIgnored);
2413
2414 tree_update.nodes[10].id = 11;
2415 tree_update.nodes[10].child_ids = {14, 15};
2416 tree_update.nodes[10].AddState(ax::mojom::State::kIgnored);
2417
2418 tree_update.nodes[11].id = 12;
2419
2420 tree_update.nodes[12].id = 13;
2421 tree_update.nodes[12].child_ids = {16};
2422 tree_update.nodes[12].AddState(ax::mojom::State::kIgnored);
2423
2424 tree_update.nodes[13].id = 14;
2425
2426 tree_update.nodes[14].id = 15;
2427
2428 tree_update.nodes[15].id = 16;
2429
2430 AXTree tree(tree_update);
2431
2432 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextUnignoredSibling());
2433 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2434
2435 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling());
2436 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2437
2438 EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(3)->GetNextUnignoredSibling());
2439 EXPECT_EQ(tree.GetFromId(12),
2441
2442 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetNextUnignoredSibling());
2443 EXPECT_EQ(tree.GetFromId(3),
2445
2446 EXPECT_EQ(tree.GetFromId(6), tree.GetFromId(5)->GetNextUnignoredSibling());
2447 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetPreviousUnignoredSibling());
2448
2449 EXPECT_EQ(tree.GetFromId(14), tree.GetFromId(6)->GetNextUnignoredSibling());
2450 EXPECT_EQ(tree.GetFromId(5),
2452
2453 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(7)->GetNextUnignoredSibling());
2454 EXPECT_EQ(tree.GetFromId(6),
2456
2457 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(8)->GetNextUnignoredSibling());
2458 EXPECT_EQ(tree.GetFromId(12),
2460
2461 EXPECT_EQ(nullptr, tree.GetFromId(9)->GetNextUnignoredSibling());
2462 EXPECT_EQ(nullptr, tree.GetFromId(9)->GetPreviousUnignoredSibling());
2463
2464 EXPECT_EQ(nullptr, tree.GetFromId(10)->GetNextUnignoredSibling());
2465 EXPECT_EQ(nullptr, tree.GetFromId(10)->GetPreviousUnignoredSibling());
2466
2467 EXPECT_EQ(tree.GetFromId(12), tree.GetFromId(11)->GetNextUnignoredSibling());
2468 EXPECT_EQ(tree.GetFromId(6),
2470
2471 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(12)->GetNextUnignoredSibling());
2472 EXPECT_EQ(tree.GetFromId(15),
2474
2475 EXPECT_EQ(nullptr, tree.GetFromId(13)->GetNextUnignoredSibling());
2476 EXPECT_EQ(nullptr, tree.GetFromId(13)->GetPreviousUnignoredSibling());
2477
2478 EXPECT_EQ(tree.GetFromId(15), tree.GetFromId(14)->GetNextUnignoredSibling());
2479 EXPECT_EQ(tree.GetFromId(6),
2481
2482 EXPECT_EQ(tree.GetFromId(12), tree.GetFromId(15)->GetNextUnignoredSibling());
2483 EXPECT_EQ(tree.GetFromId(14),
2485
2486 EXPECT_EQ(nullptr, tree.GetFromId(16)->GetNextUnignoredSibling());
2487 EXPECT_EQ(nullptr, tree.GetFromId(16)->GetPreviousUnignoredSibling());
2488}
2489
2490TEST(AXTreeTest, GetSiblingsNoIgnored) {
2491 // Since this tree base::contains no ignored nodes, PreviousSibling and
2492 // NextSibling are equivalent to their unignored counterparts.
2493 //
2494 // 1
2495 // ├── 2
2496 // │ └── 4
2497 // └── 3
2498 AXTreeUpdate tree_update;
2499 tree_update.root_id = 1;
2500 tree_update.nodes.resize(4);
2501 tree_update.nodes[0].id = 1;
2502 tree_update.nodes[0].child_ids = {2, 3};
2503 tree_update.nodes[1].id = 2;
2504 tree_update.nodes[1].child_ids = {4};
2505 tree_update.nodes[2].id = 3;
2506 tree_update.nodes[3].id = 4;
2507
2508 AXTree tree(tree_update);
2509
2510 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousSibling());
2511 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2512 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextSibling());
2513 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextUnignoredSibling());
2514
2515 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousSibling());
2516 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2517 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextSibling());
2518 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling());
2519
2520 EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(3)->GetPreviousSibling());
2521 EXPECT_EQ(tree.GetFromId(2),
2523 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextSibling());
2524 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextUnignoredSibling());
2525
2526 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousSibling());
2527 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousUnignoredSibling());
2528 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetNextSibling());
2529 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetNextUnignoredSibling());
2530}
2531
2532TEST(AXTreeTest, GetUnignoredSiblingsChildrenPromoted) {
2533 // An ignored node has its' children considered as though they were promoted
2534 // to their parents place.
2535 //
2536 // (i) => node is ignored.
2537 //
2538 // 1
2539 // ├── 2(i)
2540 // │ ├── 4
2541 // │ └── 5
2542 // └── 3
2543 AXTreeUpdate tree_update;
2544 tree_update.root_id = 1;
2545 tree_update.nodes.resize(5);
2546 tree_update.nodes[0].id = 1;
2547 tree_update.nodes[0].child_ids = {2, 3};
2548 tree_update.nodes[1].id = 2;
2549 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2550 tree_update.nodes[1].child_ids = {4, 5};
2551 tree_update.nodes[2].id = 3;
2552 tree_update.nodes[3].id = 4;
2553 tree_update.nodes[4].id = 5;
2554
2555 AXTree tree(tree_update);
2556
2557 // Root node has no siblings.
2558 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2559 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2560
2561 // Node 2's view of siblings:
2562 // literal tree: null <-- [2(i)] --> 3
2563 // unignored tree: null <-- [2(i)] --> 3
2564 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousSibling());
2565 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2566 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextSibling());
2567 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling());
2568
2569 // Node 3's view of siblings:
2570 // literal tree: 2(i) <-- [3] --> null
2571 // unignored tree: 5 <-- [4] --> null
2572 EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(3)->GetPreviousSibling());
2573 EXPECT_EQ(tree.GetFromId(5),
2575 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextSibling());
2576 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextUnignoredSibling());
2577
2578 // Node 4's view of siblings:
2579 // literal tree: null <-- [4] --> 5
2580 // unignored tree: null <-- [4] --> 5
2581 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousSibling());
2582 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousUnignoredSibling());
2583 EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(4)->GetNextSibling());
2584 EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(4)->GetNextUnignoredSibling());
2585
2586 // Node 5's view of siblings:
2587 // literal tree: 4 <-- [5] --> null
2588 // unignored tree: 4 <-- [5] --> 3
2589 EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(5)->GetPreviousSibling());
2590 EXPECT_EQ(tree.GetFromId(4),
2592 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetNextSibling());
2593 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(5)->GetNextUnignoredSibling());
2594}
2595
2596TEST(AXTreeTest, GetUnignoredSiblingsIgnoredChildSkipped) {
2597 // Ignored children of ignored parents are skipped over.
2598 //
2599 // (i) => node is ignored.
2600 //
2601 // 1
2602 // ├── 2(i)
2603 // │ ├── 4
2604 // │ └── 5(i)
2605 // └── 3
2606 AXTreeUpdate tree_update;
2607 tree_update.root_id = 1;
2608 tree_update.nodes.resize(5);
2609 tree_update.nodes[0].id = 1;
2610 tree_update.nodes[0].child_ids = {2, 3};
2611 tree_update.nodes[1].id = 2;
2612 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2613 tree_update.nodes[1].child_ids = {4, 5};
2614 tree_update.nodes[2].id = 3;
2615 tree_update.nodes[3].id = 4;
2616 tree_update.nodes[4].id = 5;
2617 tree_update.nodes[4].AddState(ax::mojom::State::kIgnored);
2618
2619 AXTree tree(tree_update);
2620
2621 // Root node has no siblings.
2622 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2623 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextUnignoredSibling());
2624
2625 // Node 2's view of siblings:
2626 // literal tree: null <-- [2(i)] --> 3
2627 // unignored tree: null <-- [2(i)] --> 3
2628 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousSibling());
2629 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2630 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextSibling());
2631 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling());
2632
2633 // Node 3's view of siblings:
2634 // literal tree: 2(i) <-- [3] --> null
2635 // unignored tree: 4 <-- [3] --> null
2636 EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(3)->GetPreviousSibling());
2637 EXPECT_EQ(tree.GetFromId(4),
2639 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextSibling());
2640 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextUnignoredSibling());
2641
2642 // Node 4's view of siblings:
2643 // literal tree: null <-- [4] --> 5(i)
2644 // unignored tree: null <-- [4] --> 3
2645 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousSibling());
2646 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousUnignoredSibling());
2647 EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(4)->GetNextSibling());
2648 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(4)->GetNextUnignoredSibling());
2649
2650 // Node 5's view of siblings:
2651 // literal tree: 4 <-- [5(i)] --> null
2652 // unignored tree: 4 <-- [5(i)] --> 3
2653 EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(5)->GetPreviousSibling());
2654 EXPECT_EQ(tree.GetFromId(4),
2656 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetNextSibling());
2657 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(5)->GetNextUnignoredSibling());
2658}
2659
2660TEST(AXTreeTest, GetUnignoredSiblingIgnoredParentIrrelevant) {
2661 // An ignored parent is not relevant unless the search would need to continue
2662 // up through it.
2663 //
2664 // (i) => node is ignored.
2665 //
2666 // 1(i)
2667 // ├── 2
2668 // └── 3
2669 AXTreeUpdate tree_update;
2670 tree_update.root_id = 1;
2671 tree_update.nodes.resize(3);
2672 tree_update.nodes[0].id = 1;
2673 tree_update.nodes[0].AddState(ax::mojom::State::kIgnored);
2674 tree_update.nodes[0].child_ids = {2, 3};
2675 tree_update.nodes[1].id = 2;
2676 tree_update.nodes[2].id = 3;
2677
2678 AXTree tree(tree_update);
2679
2680 // Node 2 and 3 are each other's unignored siblings, the parent's ignored
2681 // status is not relevant for this search.
2682 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling());
2683 EXPECT_EQ(tree.GetFromId(2),
2685}
2686
2687TEST(AXTreeTest, GetUnignoredSiblingsAllIgnored) {
2688 // Test termination when all nodes, including the root node, are ignored.
2689 //
2690 // (i) => node is ignored.
2691 //
2692 // 1(i)
2693 // └── 2(i)
2694 AXTreeUpdate tree_update;
2695 tree_update.root_id = 1;
2696 tree_update.nodes.resize(2);
2697 tree_update.nodes[0].id = 1;
2698 tree_update.nodes[0].AddState(ax::mojom::State::kIgnored);
2699 tree_update.nodes[0].child_ids = {2};
2700 tree_update.nodes[1].id = 2;
2701 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2702
2703 AXTree tree(tree_update);
2704
2705 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2706 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextUnignoredSibling());
2707 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling());
2708 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetNextUnignoredSibling());
2709}
2710
2711TEST(AXTreeTest, GetUnignoredSiblingsNestedIgnored) {
2712 // Test promotion of children through multiple layers of ignored parents.
2713 // (i) => node is ignored.
2714 //
2715 // 1
2716 // ├── 2
2717 // ├── 3(i)
2718 // │ └── 5(i)
2719 // │ └── 6
2720 // └── 4
2721 AXTreeUpdate tree_update;
2722 tree_update.root_id = 1;
2723 tree_update.nodes.resize(6);
2724 tree_update.nodes[0].id = 1;
2725 tree_update.nodes[0].child_ids = {2, 3, 4};
2726 tree_update.nodes[1].id = 2;
2727 tree_update.nodes[2].id = 3;
2728 tree_update.nodes[2].AddState(ax::mojom::State::kIgnored);
2729 tree_update.nodes[2].child_ids = {5};
2730 tree_update.nodes[3].id = 4;
2731 tree_update.nodes[4].id = 5;
2732 tree_update.nodes[4].AddState(ax::mojom::State::kIgnored);
2733 tree_update.nodes[4].child_ids = {6};
2734 tree_update.nodes[5].id = 6;
2735
2736 AXTree tree(tree_update);
2737
2738 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling());
2739
2740 const AXNode* node2 = tree.GetFromId(2);
2741 const AXNode* node3 = tree.GetFromId(3);
2742 const AXNode* node4 = tree.GetFromId(4);
2743 const AXNode* node5 = tree.GetFromId(5);
2744 const AXNode* node6 = tree.GetFromId(6);
2745
2746 ASSERT_NE(nullptr, node2);
2747 ASSERT_NE(nullptr, node3);
2748 ASSERT_NE(nullptr, node4);
2749 ASSERT_NE(nullptr, node5);
2750 ASSERT_NE(nullptr, node6);
2751
2752 // Node 2's view of siblings:
2753 // literal tree: null <-- [2] --> 3
2754 // unignored tree: null <-- [2] --> 6
2755 EXPECT_EQ(nullptr, node2->GetPreviousSibling());
2756 EXPECT_EQ(nullptr, node2->GetPreviousUnignoredSibling());
2757 EXPECT_EQ(node3, node2->GetNextSibling());
2758 EXPECT_EQ(node6, node2->GetNextUnignoredSibling());
2759
2760 // Node 3's view of siblings:
2761 // literal tree: 2 <-- [3(i)] --> 4
2762 // unignored tree: 2 <-- [3(i)] --> 4
2763 EXPECT_EQ(node2, node3->GetPreviousSibling());
2764 EXPECT_EQ(node2, node3->GetPreviousUnignoredSibling());
2765 EXPECT_EQ(node4, node3->GetNextSibling());
2766 EXPECT_EQ(node4, node3->GetNextUnignoredSibling());
2767
2768 // Node 4's view of siblings:
2769 // literal tree: 3 <-- [4] --> null
2770 // unignored tree: 6 <-- [4] --> null
2771 EXPECT_EQ(node3, node4->GetPreviousSibling());
2772 EXPECT_EQ(node6, node4->GetPreviousUnignoredSibling());
2773 EXPECT_EQ(nullptr, node4->GetNextSibling());
2774 EXPECT_EQ(nullptr, node4->GetNextUnignoredSibling());
2775
2776 // Node 5's view of siblings:
2777 // literal tree: null <-- [5(i)] --> null
2778 // unignored tree: 2 <-- [5(i)] --> 4
2779 EXPECT_EQ(nullptr, node5->GetPreviousSibling());
2780 EXPECT_EQ(node2, node5->GetPreviousUnignoredSibling());
2781 EXPECT_EQ(nullptr, node5->GetNextSibling());
2782 EXPECT_EQ(node4, node5->GetNextUnignoredSibling());
2783
2784 // Node 6's view of siblings:
2785 // literal tree: null <-- [6] --> null
2786 // unignored tree: 2 <-- [6] --> 4
2787 EXPECT_EQ(nullptr, node6->GetPreviousSibling());
2788 EXPECT_EQ(node2, node6->GetPreviousUnignoredSibling());
2789 EXPECT_EQ(nullptr, node6->GetNextSibling());
2790 EXPECT_EQ(node4, node6->GetNextUnignoredSibling());
2791}
2792
2793TEST(AXTreeTest, UnignoredSelection) {
2794 AXTreeUpdate tree_update;
2795 // (i) => node is ignored
2796 // 1
2797 // |__________
2798 // | | |
2799 // 2(i) 3 4
2800 // |_______________________
2801 // | | | |
2802 // 5 6 7(i) 8(i)
2803 // | | |________
2804 // | | | |
2805 // 9 10(i) 11(i) 12
2806 // | |____
2807 // | | |
2808 // 13(i) 14 15
2809 // |
2810 // 16
2811 // Unignored Tree (conceptual)
2812 // 1
2813 // |______________________
2814 // | | | | | | |
2815 // 5 6 14 15 12 3 4
2816 // | |
2817 // 9 16
2818 tree_update.has_tree_data = true;
2820 tree_update.root_id = 1;
2821 tree_update.nodes.resize(16);
2822 tree_update.nodes[0].id = 1;
2823 tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
2824 tree_update.nodes[0].child_ids = {2, 3, 4};
2825
2826 tree_update.nodes[1].id = 2;
2827 tree_update.nodes[1].child_ids = {5, 6, 7, 8};
2828 tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer;
2829 tree_update.nodes[1].AddState(ax::mojom::State::kIgnored);
2830
2831 tree_update.nodes[2].id = 3;
2832 tree_update.nodes[2].role = ax::mojom::Role::kStaticText;
2833 tree_update.nodes[2].SetName("text");
2834
2835 tree_update.nodes[3].id = 4;
2836 tree_update.nodes[3].role = ax::mojom::Role::kStaticText;
2837 tree_update.nodes[3].SetName("text");
2838
2839 tree_update.nodes[4].id = 5;
2840 tree_update.nodes[4].role = ax::mojom::Role::kGenericContainer;
2841 tree_update.nodes[4].child_ids = {9};
2842
2843 tree_update.nodes[5].id = 6;
2844 tree_update.nodes[5].role = ax::mojom::Role::kGenericContainer;
2845 tree_update.nodes[5].child_ids = {10};
2846
2847 tree_update.nodes[6].id = 7;
2848 tree_update.nodes[6].child_ids = {11, 12};
2849 tree_update.nodes[6].role = ax::mojom::Role::kGenericContainer;
2850 tree_update.nodes[6].AddState(ax::mojom::State::kIgnored);
2851
2852 tree_update.nodes[7].id = 8;
2853 tree_update.nodes[7].role = ax::mojom::Role::kGenericContainer;
2854 tree_update.nodes[7].AddState(ax::mojom::State::kIgnored);
2855
2856 tree_update.nodes[8].id = 9;
2857 tree_update.nodes[8].role = ax::mojom::Role::kStaticText;
2858 tree_update.nodes[8].SetName("text");
2859
2860 tree_update.nodes[9].id = 10;
2861 tree_update.nodes[9].child_ids = {13};
2862 tree_update.nodes[9].role = ax::mojom::Role::kGenericContainer;
2863 tree_update.nodes[9].AddState(ax::mojom::State::kIgnored);
2864
2865 tree_update.nodes[10].id = 11;
2866 tree_update.nodes[10].child_ids = {14, 15};
2867 tree_update.nodes[10].role = ax::mojom::Role::kGenericContainer;
2868 tree_update.nodes[10].AddState(ax::mojom::State::kIgnored);
2869
2870 tree_update.nodes[11].id = 12;
2871 tree_update.nodes[11].role = ax::mojom::Role::kStaticText;
2872 tree_update.nodes[11].SetName("text");
2873
2874 tree_update.nodes[12].id = 13;
2875 tree_update.nodes[12].child_ids = {16};
2876 tree_update.nodes[12].role = ax::mojom::Role::kGenericContainer;
2877 tree_update.nodes[12].AddState(ax::mojom::State::kIgnored);
2878
2879 tree_update.nodes[13].id = 14;
2880 tree_update.nodes[13].role = ax::mojom::Role::kStaticText;
2881 tree_update.nodes[13].SetName("text");
2882
2883 tree_update.nodes[14].id = 15;
2884 tree_update.nodes[14].role = ax::mojom::Role::kStaticText;
2885 tree_update.nodes[14].SetName("text");
2886
2887 tree_update.nodes[15].id = 16;
2888 tree_update.nodes[15].role = ax::mojom::Role::kStaticText;
2889 tree_update.nodes[15].SetName("text");
2890
2891 TestAXTreeManager test_ax_tree_manager(std::make_unique<AXTree>(tree_update));
2892 AXTree::Selection unignored_selection =
2893 test_ax_tree_manager.GetTree()->GetUnignoredSelection();
2894
2895 EXPECT_EQ(AXNode::kInvalidAXID, unignored_selection.anchor_object_id);
2896 EXPECT_EQ(-1, unignored_selection.anchor_offset);
2897 EXPECT_EQ(AXNode::kInvalidAXID, unignored_selection.focus_object_id);
2898 EXPECT_EQ(-1, unignored_selection.focus_offset);
2899 struct SelectionData {
2900 int32_t anchor_id;
2901 int32_t anchor_offset;
2902 int32_t focus_id;
2903 int32_t focus_offset;
2904 };
2905
2906 SelectionData input = {1, 0, 1, 0};
2907 SelectionData expected = {9, 0, 9, 0};
2908 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2909
2910 input = {1, 0, 2, 2};
2911 expected = {9, 0, 14, 0};
2912 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2913
2914 input = {2, 1, 5, 0};
2915 expected = {16, 0, 5, 0};
2916 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2917
2918 input = {5, 0, 9, 0};
2919 expected = {5, 0, 9, 0};
2920 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2921
2922 input = {9, 0, 6, 0};
2923 expected = {9, 0, 16, 0};
2924 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2925
2926 input = {6, 0, 10, 0};
2927 expected = {16, 0, 16, 0};
2928 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2929
2930 input = {10, 0, 13, 0};
2931 expected = {16, 0, 16, 0};
2932 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2933
2934 input = {13, 0, 16, 0};
2935 expected = {16, 0, 16, 0};
2936 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2937
2938 input = {16, 0, 7, 0};
2939 expected = {16, 0, 14, 0};
2940 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2941
2942 input = {7, 0, 11, 0};
2943 expected = {14, 0, 14, 0};
2944 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2945
2946 input = {11, 1, 14, 2};
2947 expected = {15, 0, 14, 2};
2948 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2949
2950 input = {14, 2, 15, 3};
2951 expected = {14, 2, 15, 3};
2952 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2953
2954 input = {15, 0, 12, 0};
2955 expected = {15, 0, 12, 0};
2956 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2957
2958 input = {12, 0, 8, 0};
2959 expected = {12, 0, 3, 0};
2960 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2961
2962 input = {8, 0, 3, 0};
2963 expected = {12, 4, 3, 0};
2964 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2965
2966 input = {3, 0, 4, 0};
2967 expected = {3, 0, 4, 0};
2968 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2969
2970 input = {4, 0, 4, 0};
2971 expected = {4, 0, 4, 0};
2972 TEST_SELECTION(tree_update, test_ax_tree_manager.GetTree(), input, expected);
2973}
2974
2975TEST(AXTreeTest, GetChildrenOrSiblings) {
2976 // 1
2977 // ├── 2
2978 // │ └── 5
2979 // ├── 3
2980 // └── 4
2981 AXTreeUpdate tree_update;
2982 tree_update.root_id = 1;
2983 tree_update.nodes.resize(5);
2984 tree_update.nodes[0].id = 1;
2985 tree_update.nodes[0].child_ids = {2, 3, 4};
2986 tree_update.nodes[1].id = 2;
2987 tree_update.nodes[1].child_ids = {5};
2988 tree_update.nodes[2].id = 3;
2989 tree_update.nodes[3].id = 4;
2990 tree_update.nodes[4].id = 5;
2991
2992 AXTree tree(tree_update);
2993
2994 EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(1)->GetFirstChild());
2995 EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(2)->GetFirstChild());
2996 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetFirstChild());
2997 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetFirstChild());
2998 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetFirstChild());
2999
3000 EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(1)->GetLastChild());
3001 EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(2)->GetLastChild());
3002 EXPECT_EQ(nullptr, tree.GetFromId(3)->GetLastChild());
3003 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetLastChild());
3004 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetLastChild());
3005
3006 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousSibling());
3007 EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousSibling());
3008 EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(3)->GetPreviousSibling());
3009 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(4)->GetPreviousSibling());
3010 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetPreviousSibling());
3011
3012 EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextSibling());
3013 EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextSibling());
3014 EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(3)->GetNextSibling());
3015 EXPECT_EQ(nullptr, tree.GetFromId(4)->GetNextSibling());
3016 EXPECT_EQ(nullptr, tree.GetFromId(5)->GetNextSibling());
3017}
3018
3019// Tests GetPosInSet and GetSetSize return the assigned int attribute values.
3020TEST(AXTreeTest, SetSizePosInSetAssigned) {
3021 AXTreeUpdate tree_update;
3022 tree_update.root_id = 1;
3023 tree_update.nodes.resize(4);
3024 tree_update.nodes[0].id = 1;
3025 tree_update.nodes[0].role = ax::mojom::Role::kList;
3026 tree_update.nodes[0].child_ids = {2, 3, 4};
3027 tree_update.nodes[1].id = 2;
3028 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3029 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 2);
3030 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 12);
3031 tree_update.nodes[2].id = 3;
3032 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3033 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 5);
3034 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 12);
3035 tree_update.nodes[3].id = 4;
3036 tree_update.nodes[3].role = ax::mojom::Role::kListItem;
3037 tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 9);
3038 tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 12);
3039 AXTree tree(tree_update);
3040
3041 AXNode* item1 = tree.GetFromId(2);
3042 EXPECT_OPTIONAL_EQ(2, item1->GetPosInSet());
3043 EXPECT_OPTIONAL_EQ(12, item1->GetSetSize());
3044 AXNode* item2 = tree.GetFromId(3);
3045 EXPECT_OPTIONAL_EQ(5, item2->GetPosInSet());
3046 EXPECT_OPTIONAL_EQ(12, item2->GetSetSize());
3047 AXNode* item3 = tree.GetFromId(4);
3048 EXPECT_OPTIONAL_EQ(9, item3->GetPosInSet());
3049 EXPECT_OPTIONAL_EQ(12, item3->GetSetSize());
3050}
3051
3052// Tests that PosInSet and SetSize can be calculated if not assigned.
3053TEST(AXTreeTest, SetSizePosInSetUnassigned) {
3054 AXTreeUpdate tree_update;
3055 tree_update.root_id = 1;
3056 tree_update.nodes.resize(4);
3057 tree_update.nodes[0].id = 1;
3058 tree_update.nodes[0].role = ax::mojom::Role::kList;
3059 tree_update.nodes[0].child_ids = {2, 3, 4};
3060 tree_update.nodes[1].id = 2;
3061 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3062 tree_update.nodes[2].id = 3;
3063 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3064 tree_update.nodes[3].id = 4;
3065 tree_update.nodes[3].role = ax::mojom::Role::kListItem;
3066 AXTree tree(tree_update);
3067
3068 AXNode* item1 = tree.GetFromId(2);
3069 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3070 EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
3071 AXNode* item2 = tree.GetFromId(3);
3072 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3073 EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
3074 AXNode* item3 = tree.GetFromId(4);
3075 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3076 EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
3077}
3078
3079// Tests PosInSet can be calculated if unassigned, and SetSize can be
3080// assigned on the outerlying ordered set.
3081TEST(AXTreeTest, SetSizeAssignedOnContainer) {
3082 AXTreeUpdate tree_update;
3083 tree_update.root_id = 1;
3084 tree_update.nodes.resize(4);
3085 tree_update.nodes[0].id = 1;
3086 tree_update.nodes[0].role = ax::mojom::Role::kList;
3087 tree_update.nodes[0].child_ids = {2, 3, 4};
3088 tree_update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 7);
3089 tree_update.nodes[1].id = 2;
3090 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3091 tree_update.nodes[2].id = 3;
3092 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3093 tree_update.nodes[3].id = 4;
3094 tree_update.nodes[3].role = ax::mojom::Role::kListItem;
3095 AXTree tree(tree_update);
3096
3097 // Items should inherit SetSize from ordered set if not specified.
3098 AXNode* item1 = tree.GetFromId(2);
3099 EXPECT_OPTIONAL_EQ(7, item1->GetSetSize());
3100 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3101 AXNode* item2 = tree.GetFromId(3);
3102 EXPECT_OPTIONAL_EQ(7, item2->GetSetSize());
3103 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3104 AXNode* item3 = tree.GetFromId(4);
3105 EXPECT_OPTIONAL_EQ(7, item3->GetSetSize());
3106 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3107}
3108
3109// Tests GetPosInSet and GetSetSize on a list containing various roles.
3110// Roles for items and associated ordered set should match up.
3111TEST(AXTreeTest, SetSizePosInSetDiverseList) {
3112 AXTreeUpdate tree_update;
3113 tree_update.root_id = 1;
3114 tree_update.nodes.resize(6);
3115 tree_update.nodes[0].id = 1;
3116 tree_update.nodes[0].role = ax::mojom::Role::kMenu;
3117 tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
3118 tree_update.nodes[1].id = 2;
3119 tree_update.nodes[1].role = ax::mojom::Role::kMenuItem; // 1 of 4
3120 tree_update.nodes[2].id = 3;
3121 tree_update.nodes[2].role = ax::mojom::Role::kMenuItemCheckBox; // 2 of 4
3122 tree_update.nodes[3].id = 4;
3123 tree_update.nodes[3].role = ax::mojom::Role::kMenuItemRadio; // 3 of 4
3124 tree_update.nodes[4].id = 5;
3125 tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 4 of 4
3126 tree_update.nodes[5].id = 6;
3127 tree_update.nodes[5].role = ax::mojom::Role::kTab; // 0 of 0
3128 AXTree tree(tree_update);
3129
3130 // kMenu is allowed to contain: kMenuItem, kMenuItemCheckbox,
3131 // and kMenuItemRadio. For PosInSet and SetSize purposes, these items
3132 // are treated as the same role.
3133 AXNode* item1 = tree.GetFromId(2);
3134 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3135 EXPECT_OPTIONAL_EQ(4, item1->GetSetSize());
3136 AXNode* checkbox = tree.GetFromId(3);
3137 EXPECT_OPTIONAL_EQ(2, checkbox->GetPosInSet());
3138 EXPECT_OPTIONAL_EQ(4, checkbox->GetSetSize());
3139 AXNode* radio = tree.GetFromId(4);
3140 EXPECT_OPTIONAL_EQ(3, radio->GetPosInSet());
3141 EXPECT_OPTIONAL_EQ(4, radio->GetSetSize());
3142 AXNode* item3 = tree.GetFromId(5);
3143 EXPECT_OPTIONAL_EQ(4, item3->GetPosInSet());
3144 EXPECT_OPTIONAL_EQ(4, item3->GetSetSize());
3145 AXNode* tab = tree.GetFromId(6);
3146 EXPECT_FALSE(tab->GetPosInSet());
3147 EXPECT_FALSE(tab->GetSetSize());
3148}
3149
3150// Tests GetPosInSet and GetSetSize on a nested list.
3151TEST(AXTreeTest, SetSizePosInSetNestedList) {
3152 AXTreeUpdate tree_update;
3153 tree_update.root_id = 1;
3154 tree_update.nodes.resize(7);
3155 tree_update.nodes[0].id = 1;
3156 tree_update.nodes[0].role = ax::mojom::Role::kList;
3157 tree_update.nodes[0].child_ids = {2, 3, 4, 7};
3158 tree_update.nodes[1].id = 2;
3159 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3160 tree_update.nodes[2].id = 3;
3161 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3162 tree_update.nodes[3].id = 4;
3163 tree_update.nodes[3].role = ax::mojom::Role::kList;
3164 tree_update.nodes[3].child_ids = {5, 6};
3165 tree_update.nodes[4].id = 5;
3166 tree_update.nodes[4].role = ax::mojom::Role::kListItem;
3167 tree_update.nodes[5].id = 6;
3168 tree_update.nodes[5].role = ax::mojom::Role::kListItem;
3169 tree_update.nodes[6].id = 7;
3170 tree_update.nodes[6].role = ax::mojom::Role::kListItem;
3171 AXTree tree(tree_update);
3172
3173 AXNode* outer_item1 = tree.GetFromId(2);
3174 EXPECT_OPTIONAL_EQ(1, outer_item1->GetPosInSet());
3175 EXPECT_OPTIONAL_EQ(3, outer_item1->GetSetSize());
3176 AXNode* outer_item2 = tree.GetFromId(3);
3177 EXPECT_OPTIONAL_EQ(2, outer_item2->GetPosInSet());
3178 EXPECT_OPTIONAL_EQ(3, outer_item2->GetSetSize());
3179
3180 AXNode* inner_item1 = tree.GetFromId(5);
3181 EXPECT_OPTIONAL_EQ(1, inner_item1->GetPosInSet());
3182 EXPECT_OPTIONAL_EQ(2, inner_item1->GetSetSize());
3183 AXNode* inner_item2 = tree.GetFromId(6);
3184 EXPECT_OPTIONAL_EQ(2, inner_item2->GetPosInSet());
3185 EXPECT_OPTIONAL_EQ(2, inner_item2->GetSetSize());
3186
3187 AXNode* outer_item3 = tree.GetFromId(7);
3188 EXPECT_OPTIONAL_EQ(3, outer_item3->GetPosInSet());
3189 EXPECT_OPTIONAL_EQ(3, outer_item3->GetSetSize());
3190}
3191
3192// Tests PosInSet can be calculated if one item specifies PosInSet, but
3193// other assignments are missing.
3194TEST(AXTreeTest, PosInSetMissing) {
3195 AXTreeUpdate tree_update;
3196 tree_update.root_id = 1;
3197 tree_update.nodes.resize(4);
3198 tree_update.nodes[0].id = 1;
3199 tree_update.nodes[0].role = ax::mojom::Role::kList;
3200 tree_update.nodes[0].child_ids = {2, 3, 4};
3201 tree_update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 20);
3202 tree_update.nodes[1].id = 2;
3203 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3204 tree_update.nodes[2].id = 3;
3205 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3206 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 13);
3207 tree_update.nodes[3].id = 4;
3208 tree_update.nodes[3].role = ax::mojom::Role::kListItem;
3209 AXTree tree(tree_update);
3210
3211 // Item1 should have pos of 12, since item2 is assigned a pos of 13.
3212 AXNode* item1 = tree.GetFromId(2);
3213 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3214 EXPECT_OPTIONAL_EQ(20, item1->GetSetSize());
3215 AXNode* item2 = tree.GetFromId(3);
3216 EXPECT_OPTIONAL_EQ(13, item2->GetPosInSet());
3217 EXPECT_OPTIONAL_EQ(20, item2->GetSetSize());
3218 // Item2 should have pos of 14, since item2 is assigned a pos of 13.
3219 AXNode* item3 = tree.GetFromId(4);
3220 EXPECT_OPTIONAL_EQ(14, item3->GetPosInSet());
3221 EXPECT_OPTIONAL_EQ(20, item3->GetSetSize());
3222}
3223
3224// A more difficult test that involves missing PosInSet and SetSize values.
3225TEST(AXTreeTest, SetSizePosInSetMissingDifficult) {
3226 AXTreeUpdate tree_update;
3227 tree_update.root_id = 1;
3228 tree_update.nodes.resize(6);
3229 tree_update.nodes[0].id = 1;
3230 tree_update.nodes[0].role = ax::mojom::Role::kList;
3231 tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
3232 tree_update.nodes[1].id = 2;
3233 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 11
3234 tree_update.nodes[2].id = 3;
3235 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3236 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
3237 5); // 5 of 11
3238 tree_update.nodes[3].id = 4;
3239 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 6 of 11
3240 tree_update.nodes[4].id = 5;
3241 tree_update.nodes[4].role = ax::mojom::Role::kListItem;
3242 tree_update.nodes[4].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
3243 10); // 10 of 11
3244 tree_update.nodes[5].id = 6;
3245 tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 11 of 11
3246 AXTree tree(tree_update);
3247
3248 AXNode* item1 = tree.GetFromId(2);
3249 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3250 EXPECT_OPTIONAL_EQ(11, item1->GetSetSize());
3251 AXNode* item2 = tree.GetFromId(3);
3252 EXPECT_OPTIONAL_EQ(5, item2->GetPosInSet());
3253 EXPECT_OPTIONAL_EQ(11, item2->GetSetSize());
3254 AXNode* item3 = tree.GetFromId(4);
3255 EXPECT_OPTIONAL_EQ(6, item3->GetPosInSet());
3256 EXPECT_OPTIONAL_EQ(11, item3->GetSetSize());
3257 AXNode* item4 = tree.GetFromId(5);
3258 EXPECT_OPTIONAL_EQ(10, item4->GetPosInSet());
3259 EXPECT_OPTIONAL_EQ(11, item4->GetSetSize());
3260 AXNode* item5 = tree.GetFromId(6);
3261 EXPECT_OPTIONAL_EQ(11, item5->GetPosInSet());
3262 EXPECT_OPTIONAL_EQ(11, item5->GetSetSize());
3263}
3264
3265// Tests that code overwrites decreasing SetSize assignments to largest of
3266// assigned values.
3267TEST(AXTreeTest, SetSizeDecreasing) {
3268 AXTreeUpdate tree_update;
3269 tree_update.root_id = 1;
3270 tree_update.nodes.resize(4);
3271 tree_update.nodes[0].id = 1;
3272 tree_update.nodes[0].role = ax::mojom::Role::kList;
3273 tree_update.nodes[0].child_ids = {2, 3, 4};
3274 tree_update.nodes[1].id = 2;
3275 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 5
3276 tree_update.nodes[2].id = 3;
3277 tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 5
3278 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
3279 tree_update.nodes[3].id = 4;
3280 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 5
3281 tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 4);
3282 AXTree tree(tree_update);
3283
3284 AXNode* item1 = tree.GetFromId(2);
3285 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3286 EXPECT_OPTIONAL_EQ(5, item1->GetSetSize());
3287 AXNode* item2 = tree.GetFromId(3);
3288 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3289 EXPECT_OPTIONAL_EQ(5, item2->GetSetSize());
3290 AXNode* item3 = tree.GetFromId(4);
3291 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3292 EXPECT_OPTIONAL_EQ(5, item3->GetSetSize());
3293}
3294
3295// Tests that code overwrites decreasing PosInSet values.
3296TEST(AXTreeTest, PosInSetDecreasing) {
3297 AXTreeUpdate tree_update;
3298 tree_update.root_id = 1;
3299 tree_update.nodes.resize(4);
3300 tree_update.nodes[0].id = 1;
3301 tree_update.nodes[0].role = ax::mojom::Role::kList;
3302 tree_update.nodes[0].child_ids = {2, 3, 4};
3303 tree_update.nodes[1].id = 2;
3304 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 8
3305 tree_update.nodes[2].id = 3;
3306 tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 7 of 8
3307 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 7);
3308 tree_update.nodes[3].id = 4;
3309 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 8 of 8
3310 tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 3);
3311 AXTree tree(tree_update);
3312
3313 AXNode* item1 = tree.GetFromId(2);
3314 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3315 EXPECT_OPTIONAL_EQ(8, item1->GetSetSize());
3316 AXNode* item2 = tree.GetFromId(3);
3317 EXPECT_OPTIONAL_EQ(7, item2->GetPosInSet());
3318 EXPECT_OPTIONAL_EQ(8, item2->GetSetSize());
3319 AXNode* item3 = tree.GetFromId(4);
3320 EXPECT_OPTIONAL_EQ(8, item3->GetPosInSet());
3321 EXPECT_OPTIONAL_EQ(8, item3->GetSetSize());
3322}
3323
3324// Tests that code overwrites duplicate PosInSet values. Note this case is
3325// tricky; an update to the second element causes an update to the third
3326// element.
3327TEST(AXTreeTest, PosInSetDuplicates) {
3328 AXTreeUpdate tree_update;
3329 tree_update.root_id = 1;
3330 tree_update.nodes.resize(4);
3331 tree_update.nodes[0].id = 1;
3332 tree_update.nodes[0].role = ax::mojom::Role::kList;
3333 tree_update.nodes[0].child_ids = {2, 3, 4};
3334 tree_update.nodes[1].id = 2;
3335 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 6 of 8
3336 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 6);
3337 tree_update.nodes[2].id = 3;
3338 tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 7 of 8
3339 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 6);
3340 tree_update.nodes[3].id = 4;
3341 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 8 of 8
3342 tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 7);
3343 AXTree tree(tree_update);
3344
3345 AXNode* item1 = tree.GetFromId(2);
3346 EXPECT_OPTIONAL_EQ(6, item1->GetPosInSet());
3347 EXPECT_OPTIONAL_EQ(8, item1->GetSetSize());
3348 AXNode* item2 = tree.GetFromId(3);
3349 EXPECT_OPTIONAL_EQ(7, item2->GetPosInSet());
3350 EXPECT_OPTIONAL_EQ(8, item2->GetSetSize());
3351 AXNode* item3 = tree.GetFromId(4);
3352 EXPECT_OPTIONAL_EQ(8, item3->GetPosInSet());
3353 EXPECT_OPTIONAL_EQ(8, item3->GetSetSize());
3354}
3355
3356// Tests GetPosInSet and GetSetSize when some list items are nested in a generic
3357// container.
3358TEST(AXTreeTest, SetSizePosInSetNestedContainer) {
3359 AXTreeUpdate tree_update;
3360 tree_update.root_id = 1;
3361 tree_update.nodes.resize(7);
3362 tree_update.nodes[0].id = 1;
3363 tree_update.nodes[0].role = ax::mojom::Role::kList;
3364 tree_update.nodes[0].child_ids = {2, 3, 7};
3365 tree_update.nodes[1].id = 2;
3366 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 4
3367 tree_update.nodes[2].id = 3;
3368 tree_update.nodes[2].role = ax::mojom::Role::kGenericContainer;
3369 tree_update.nodes[2].child_ids = {4, 5};
3370 tree_update.nodes[3].id = 4;
3371 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 2 of 4
3372 tree_update.nodes[4].id = 5;
3373 tree_update.nodes[4].role = ax::mojom::Role::kIgnored;
3374 tree_update.nodes[4].child_ids = {6};
3375 tree_update.nodes[5].id = 6;
3376 tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 3 of 4
3377 tree_update.nodes[6].id = 7;
3378 tree_update.nodes[6].role = ax::mojom::Role::kListItem; // 4 of 4
3379 AXTree tree(tree_update);
3380
3381 AXNode* item1 = tree.GetFromId(2);
3382 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3383 EXPECT_OPTIONAL_EQ(4, item1->GetSetSize());
3384 AXNode* g_container = tree.GetFromId(3);
3385 EXPECT_FALSE(g_container->GetPosInSet());
3386 EXPECT_FALSE(g_container->GetSetSize());
3387 AXNode* item2 = tree.GetFromId(4);
3388 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3389 EXPECT_OPTIONAL_EQ(4, item2->GetSetSize());
3390 AXNode* ignored = tree.GetFromId(5);
3391 EXPECT_FALSE(ignored->GetPosInSet());
3392 EXPECT_FALSE(ignored->GetSetSize());
3393 AXNode* item3 = tree.GetFromId(6);
3394 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3395 EXPECT_OPTIONAL_EQ(4, item3->GetSetSize());
3396 AXNode* item4 = tree.GetFromId(7);
3397 EXPECT_OPTIONAL_EQ(4, item4->GetPosInSet());
3398 EXPECT_OPTIONAL_EQ(4, item4->GetSetSize());
3399}
3400
3401// Tests GetSetSize and GetPosInSet are correct, even when list items change.
3402// Tests that previously calculated values are not used after tree is updated.
3403TEST(AXTreeTest, SetSizePosInSetDeleteItem) {
3404 AXTreeUpdate initial_state;
3405 initial_state.root_id = 1;
3406 initial_state.nodes.resize(4);
3407 initial_state.nodes[0].id = 1;
3408 initial_state.nodes[0].role = ax::mojom::Role::kList;
3409 initial_state.nodes[0].child_ids = {2, 3, 4};
3410 initial_state.nodes[1].id = 2;
3411 initial_state.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
3412 initial_state.nodes[2].id = 3;
3413 initial_state.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
3414 initial_state.nodes[3].id = 4;
3415 initial_state.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 3
3416 AXTree tree(initial_state);
3417
3418 AXNode* item1 = tree.GetFromId(2);
3419 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3420 EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
3421 AXNode* item2 = tree.GetFromId(3);
3422 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3423 EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
3424 AXNode* item3 = tree.GetFromId(4);
3425 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3426 EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
3427
3428 // TreeUpdates only need to describe what changed in tree.
3429 AXTreeUpdate update = initial_state;
3430 update.nodes.resize(1);
3431 update.nodes[0].child_ids = {2, 4}; // Delete item 2 of 3 from list.
3432 ASSERT_TRUE(tree.Unserialize(update));
3433
3434 AXNode* new_item1 = tree.GetFromId(2);
3435 EXPECT_OPTIONAL_EQ(1, new_item1->GetPosInSet());
3436 EXPECT_OPTIONAL_EQ(2, new_item1->GetSetSize());
3437 AXNode* new_item2 = tree.GetFromId(4);
3438 EXPECT_OPTIONAL_EQ(2, new_item2->GetPosInSet());
3439 EXPECT_OPTIONAL_EQ(2, new_item2->GetSetSize());
3440}
3441
3442// Tests GetSetSize and GetPosInSet are correct, even when list items change.
3443// This test adds an item to the front of a list, which invalidates previously
3444// calculated PosInSet and SetSize values. Tests that old values are not
3445// used after tree is updated.
3446TEST(AXTreeTest, SetSizePosInSetAddItem) {
3447 AXTreeUpdate initial_state;
3448 initial_state.root_id = 1;
3449 initial_state.nodes.resize(4);
3450 initial_state.nodes[0].id = 1;
3451 initial_state.nodes[0].role = ax::mojom::Role::kList;
3452 initial_state.nodes[0].child_ids = {2, 3, 4};
3453 initial_state.nodes[1].id = 2;
3454 initial_state.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
3455 initial_state.nodes[2].id = 3;
3456 initial_state.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
3457 initial_state.nodes[3].id = 4;
3458 initial_state.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 3
3459 AXTree tree(initial_state);
3460
3461 AXNode* item1 = tree.GetFromId(2);
3462 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3463 EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
3464 AXNode* item2 = tree.GetFromId(3);
3465 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3466 EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
3467 AXNode* item3 = tree.GetFromId(4);
3468 EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
3469 EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
3470
3471 // Insert an item at the beginning of the list.
3472 AXTreeUpdate update = initial_state;
3473 update.nodes.resize(2);
3474 update.nodes[0].id = 1;
3475 update.nodes[0].child_ids = {5, 2, 3, 4};
3476 update.nodes[1].id = 5;
3477 update.nodes[1].role = ax::mojom::Role::kListItem;
3478 ASSERT_TRUE(tree.Unserialize(update));
3479
3480 AXNode* new_item1 = tree.GetFromId(5);
3481 EXPECT_OPTIONAL_EQ(1, new_item1->GetPosInSet());
3482 EXPECT_OPTIONAL_EQ(4, new_item1->GetSetSize());
3483 AXNode* new_item2 = tree.GetFromId(2);
3484 EXPECT_OPTIONAL_EQ(2, new_item2->GetPosInSet());
3485 EXPECT_OPTIONAL_EQ(4, new_item2->GetSetSize());
3486 AXNode* new_item3 = tree.GetFromId(3);
3487 EXPECT_OPTIONAL_EQ(3, new_item3->GetPosInSet());
3488 EXPECT_OPTIONAL_EQ(4, new_item3->GetSetSize());
3489 AXNode* new_item4 = tree.GetFromId(4);
3490 EXPECT_OPTIONAL_EQ(4, new_item4->GetPosInSet());
3491 EXPECT_OPTIONAL_EQ(4, new_item4->GetSetSize());
3492}
3493
3494// Tests that the outerlying ordered set reports a SetSize. Ordered sets
3495// should not report a PosInSet value other than 0, since they are not
3496// considered to be items within a set (even when nested).
3497TEST(AXTreeTest, OrderedSetReportsSetSize) {
3498 AXTreeUpdate tree_update;
3499 tree_update.root_id = 1;
3500 tree_update.nodes.resize(12);
3501 tree_update.nodes[0].id = 1;
3502 tree_update.nodes[0].role = ax::mojom::Role::kList; // SetSize = 3
3503 tree_update.nodes[0].child_ids = {2, 3, 4, 7, 8, 9, 12};
3504 tree_update.nodes[1].id = 2;
3505 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
3506 tree_update.nodes[2].id = 3;
3507 tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 3
3508 tree_update.nodes[3].id = 4;
3509 tree_update.nodes[3].role = ax::mojom::Role::kList; // SetSize = 2
3510 tree_update.nodes[3].child_ids = {5, 6};
3511 tree_update.nodes[4].id = 5;
3512 tree_update.nodes[4].role = ax::mojom::Role::kListItem; // 1 of 2
3513 tree_update.nodes[5].id = 6;
3514 tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 2 of 2
3515 tree_update.nodes[6].id = 7;
3516 tree_update.nodes[6].role = ax::mojom::Role::kListItem; // 3 of 3
3517 tree_update.nodes[7].id = 8;
3518 tree_update.nodes[7].role = ax::mojom::Role::kList; // SetSize = 0
3519 tree_update.nodes[8].id = 9;
3520 tree_update.nodes[8].role =
3521 ax::mojom::Role::kList; // SetSize = 1 because only 1
3522 // item whose role matches
3523 tree_update.nodes[8].child_ids = {10, 11};
3524 tree_update.nodes[9].id = 10;
3525 tree_update.nodes[9].role = ax::mojom::Role::kArticle;
3526 tree_update.nodes[10].id = 11;
3527 tree_update.nodes[10].role = ax::mojom::Role::kListItem;
3528 tree_update.nodes[11].id = 12;
3529 tree_update.nodes[11].role = ax::mojom::Role::kList;
3530 tree_update.nodes[11].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
3531 AXTree tree(tree_update);
3532
3533 AXNode* outer_list = tree.GetFromId(1);
3534 EXPECT_FALSE(outer_list->GetPosInSet());
3535 EXPECT_OPTIONAL_EQ(3, outer_list->GetSetSize());
3536 AXNode* outer_list_item1 = tree.GetFromId(2);
3537 EXPECT_OPTIONAL_EQ(1, outer_list_item1->GetPosInSet());
3538 EXPECT_OPTIONAL_EQ(3, outer_list_item1->GetSetSize());
3539 AXNode* outer_list_item2 = tree.GetFromId(3);
3540 EXPECT_OPTIONAL_EQ(2, outer_list_item2->GetPosInSet());
3541 EXPECT_OPTIONAL_EQ(3, outer_list_item2->GetSetSize());
3542 AXNode* outer_list_item3 = tree.GetFromId(7);
3543 EXPECT_OPTIONAL_EQ(3, outer_list_item3->GetPosInSet());
3544 EXPECT_OPTIONAL_EQ(3, outer_list_item3->GetSetSize());
3545
3546 AXNode* inner_list1 = tree.GetFromId(4);
3547 EXPECT_FALSE(inner_list1->GetPosInSet());
3548 EXPECT_OPTIONAL_EQ(2, inner_list1->GetSetSize());
3549 AXNode* inner_list1_item1 = tree.GetFromId(5);
3550 EXPECT_OPTIONAL_EQ(1, inner_list1_item1->GetPosInSet());
3551 EXPECT_OPTIONAL_EQ(2, inner_list1_item1->GetSetSize());
3552 AXNode* inner_list1_item2 = tree.GetFromId(6);
3553 EXPECT_OPTIONAL_EQ(2, inner_list1_item2->GetPosInSet());
3554 EXPECT_OPTIONAL_EQ(2, inner_list1_item2->GetSetSize());
3555
3556 AXNode* inner_list2 = tree.GetFromId(8); // Empty list
3557 EXPECT_FALSE(inner_list2->GetPosInSet());
3558 EXPECT_OPTIONAL_EQ(0, inner_list2->GetSetSize());
3559
3560 AXNode* inner_list3 = tree.GetFromId(9);
3561 EXPECT_FALSE(inner_list3->GetPosInSet());
3562 // Only 1 item whose role matches.
3563 EXPECT_OPTIONAL_EQ(1, inner_list3->GetSetSize());
3564 AXNode* inner_list3_article1 = tree.GetFromId(10);
3565 EXPECT_FALSE(inner_list3_article1->GetPosInSet());
3566 EXPECT_FALSE(inner_list3_article1->GetSetSize());
3567 AXNode* inner_list3_item1 = tree.GetFromId(11);
3568 EXPECT_OPTIONAL_EQ(1, inner_list3_item1->GetPosInSet());
3569 EXPECT_OPTIONAL_EQ(1, inner_list3_item1->GetSetSize());
3570
3571 AXNode* inner_list4 = tree.GetFromId(12);
3572 EXPECT_FALSE(inner_list4->GetPosInSet());
3573 // Even though list is empty, kSetSize attribute was set, so it takes
3574 // precedence
3575 EXPECT_OPTIONAL_EQ(5, inner_list4->GetSetSize());
3576}
3577
3578// Tests GetPosInSet and GetSetSize code on invalid input.
3579TEST(AXTreeTest, SetSizePosInSetInvalid) {
3580 AXTreeUpdate tree_update;
3581 tree_update.root_id = 1;
3582 tree_update.nodes.resize(3);
3583 tree_update.nodes[0].id = 1;
3584 tree_update.nodes[0].role = ax::mojom::Role::kListItem; // 0 of 0
3585 tree_update.nodes[0].child_ids = {2, 3};
3586 tree_update.nodes[1].id = 2;
3587 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
3588 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
3589 4); // 0 of 0
3590 tree_update.nodes[2].id = 3;
3591 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
3592 AXTree tree(tree_update);
3593
3594 AXNode* item1 = tree.GetFromId(1);
3595 EXPECT_FALSE(item1->GetPosInSet());
3596 EXPECT_FALSE(item1->GetSetSize());
3597 AXNode* item2 = tree.GetFromId(2);
3598 EXPECT_FALSE(item2->GetPosInSet());
3599 EXPECT_FALSE(item2->GetSetSize());
3600 AXNode* item3 = tree.GetFromId(3);
3601 EXPECT_FALSE(item3->GetPosInSet());
3602 EXPECT_FALSE(item3->GetSetSize());
3603}
3604
3605// Tests GetPosInSet and GetSetSize code on kRadioButtons. Radio buttons
3606// behave differently than other item-like elements; most notably, they do not
3607// need to be contained within an ordered set to report a PosInSet or SetSize.
3608TEST(AXTreeTest, SetSizePosInSetRadioButtons) {
3609 AXTreeUpdate tree_update;
3610 tree_update.root_id = 1;
3611 tree_update.nodes.resize(13);
3612 tree_update.nodes[0].id = 1;
3613 tree_update.nodes[0].child_ids = {2, 3, 4, 10, 13};
3614 // This test passes because the root node is a kRadioGroup.
3615 tree_update.nodes[0].role = ax::mojom::Role::kRadioGroup; // Setsize = 5;
3616
3617 // Radio buttons are not required to be contained within an ordered set.
3618 tree_update.nodes[1].id = 2;
3619 tree_update.nodes[1].role = ax::mojom::Role::kRadioButton; // 1 of 5
3620 tree_update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
3621 "sports");
3622 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 1);
3623 tree_update.nodes[2].id = 3;
3624 tree_update.nodes[2].role = ax::mojom::Role::kRadioButton; // 2 of 5
3625 tree_update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
3626 "books");
3627 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 2);
3628 tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
3629
3630 // Radio group with nested generic container.
3631 tree_update.nodes[3].id = 4;
3632 tree_update.nodes[3].role = ax::mojom::Role::kRadioGroup; // setsize = 4
3633 tree_update.nodes[3].child_ids = {5, 6, 7};
3634 tree_update.nodes[4].id = 5;
3635 tree_update.nodes[4].role = ax::mojom::Role::kRadioButton;
3636 tree_update.nodes[4].AddStringAttribute(ax::mojom::StringAttribute::kName,
3637 "recipes"); // 1 of 4
3638 tree_update.nodes[5].id = 6;
3639 tree_update.nodes[5].role = ax::mojom::Role::kRadioButton;
3640 tree_update.nodes[5].AddStringAttribute(ax::mojom::StringAttribute::kName,
3641 "recipes"); // 2 of 4
3642 tree_update.nodes[6].id = 7;
3643 tree_update.nodes[6].role = ax::mojom::Role::kGenericContainer;
3644 tree_update.nodes[6].child_ids = {8, 9};
3645 tree_update.nodes[7].id = 8;
3646 tree_update.nodes[7].role = ax::mojom::Role::kRadioButton;
3647 tree_update.nodes[7].AddStringAttribute(ax::mojom::StringAttribute::kName,
3648 "recipes"); // 3 of 4
3649 tree_update.nodes[8].id = 9;
3650 tree_update.nodes[8].role = ax::mojom::Role::kRadioButton;
3651 tree_update.nodes[8].AddStringAttribute(ax::mojom::StringAttribute::kName,
3652 "recipes"); // 4 of 4
3653
3654 // Radio buttons are allowed to be contained within forms.
3655 tree_update.nodes[9].id = 10;
3656 tree_update.nodes[9].role = ax::mojom::Role::kForm;
3657 tree_update.nodes[9].child_ids = {11, 12};
3658 tree_update.nodes[10].id = 11;
3659 tree_update.nodes[10].role = ax::mojom::Role::kRadioButton;
3660 tree_update.nodes[10].AddStringAttribute(ax::mojom::StringAttribute::kName,
3661 "cities"); // 1 of 2
3662 tree_update.nodes[11].id = 12;
3663 tree_update.nodes[11].role = ax::mojom::Role::kRadioButton;
3664 tree_update.nodes[11].AddStringAttribute(ax::mojom::StringAttribute::kName,
3665 "cities"); // 2 of 2
3666 tree_update.nodes[12].id = 13;
3667 tree_update.nodes[12].role = ax::mojom::Role::kRadioButton; // 4 of 5
3668 tree_update.nodes[12].AddStringAttribute(ax::mojom::StringAttribute::kName,
3669 "sports");
3670 tree_update.nodes[12].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 4);
3671
3672 AXTree tree(tree_update);
3673
3674 AXNode* sports_button1 = tree.GetFromId(2);
3675 EXPECT_OPTIONAL_EQ(1, sports_button1->GetPosInSet());
3676 EXPECT_OPTIONAL_EQ(5, sports_button1->GetSetSize());
3677 AXNode* books_button = tree.GetFromId(3);
3678 EXPECT_OPTIONAL_EQ(2, books_button->GetPosInSet());
3679 EXPECT_OPTIONAL_EQ(5, books_button->GetSetSize());
3680
3681 AXNode* radiogroup1 = tree.GetFromId(4);
3682 EXPECT_FALSE(radiogroup1->GetPosInSet());
3683 EXPECT_OPTIONAL_EQ(4, radiogroup1->GetSetSize());
3684 AXNode* recipes_button1 = tree.GetFromId(5);
3685 EXPECT_OPTIONAL_EQ(1, recipes_button1->GetPosInSet());
3686 EXPECT_OPTIONAL_EQ(4, recipes_button1->GetSetSize());
3687 AXNode* recipes_button2 = tree.GetFromId(6);
3688 EXPECT_OPTIONAL_EQ(2, recipes_button2->GetPosInSet());
3689 EXPECT_OPTIONAL_EQ(4, recipes_button2->GetSetSize());
3690
3691 AXNode* generic_container = tree.GetFromId(7);
3692 EXPECT_FALSE(generic_container->GetPosInSet());
3693 EXPECT_FALSE(generic_container->GetSetSize());
3694 AXNode* recipes_button3 = tree.GetFromId(8);
3695 EXPECT_OPTIONAL_EQ(3, recipes_button3->GetPosInSet());
3696 EXPECT_OPTIONAL_EQ(4, recipes_button3->GetSetSize());
3697 AXNode* recipes_button4 = tree.GetFromId(9);
3698 EXPECT_OPTIONAL_EQ(4, recipes_button4->GetPosInSet());
3699 EXPECT_OPTIONAL_EQ(4, recipes_button4->GetSetSize());
3700
3701 // Elements with role kForm shouldn't report posinset or setsize
3702 AXNode* form = tree.GetFromId(10);
3703 EXPECT_FALSE(form->GetPosInSet());
3704 EXPECT_FALSE(form->GetSetSize());
3705 AXNode* cities_button1 = tree.GetFromId(11);
3706 EXPECT_OPTIONAL_EQ(1, cities_button1->GetPosInSet());
3707 EXPECT_OPTIONAL_EQ(2, cities_button1->GetSetSize());
3708 AXNode* cities_button2 = tree.GetFromId(12);
3709 EXPECT_OPTIONAL_EQ(2, cities_button2->GetPosInSet());
3710 EXPECT_OPTIONAL_EQ(2, cities_button2->GetSetSize());
3711
3712 AXNode* sports_button2 = tree.GetFromId(13);
3713 EXPECT_OPTIONAL_EQ(4, sports_button2->GetPosInSet());
3714 EXPECT_OPTIONAL_EQ(5, sports_button2->GetSetSize());
3715}
3716
3717// Tests GetPosInSet and GetSetSize on a list that includes radio buttons.
3718// Note that radio buttons do not contribute to the SetSize of the outerlying
3719// list.
3720TEST(AXTreeTest, SetSizePosInSetRadioButtonsInList) {
3721 AXTreeUpdate tree_update;
3722 tree_update.root_id = 1;
3723 tree_update.nodes.resize(6);
3724 tree_update.nodes[0].id = 1;
3725 tree_update.nodes[0].role =
3726 ax::mojom::Role::kList; // SetSize = 2, since only base::contains 2
3727 // ListItems
3728 tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
3729
3730 tree_update.nodes[1].id = 2;
3731 tree_update.nodes[1].role = ax::mojom::Role::kRadioButton; // 1 of 3
3732 tree_update.nodes[2].id = 3;
3733 tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 1 of 2
3734 tree_update.nodes[3].id = 4;
3735 tree_update.nodes[3].role = ax::mojom::Role::kRadioButton; // 2 of 3
3736 tree_update.nodes[4].id = 5;
3737 tree_update.nodes[4].role = ax::mojom::Role::kListItem; // 2 of 2
3738 tree_update.nodes[5].id = 6;
3739 tree_update.nodes[5].role = ax::mojom::Role::kRadioButton; // 3 of 3
3740 AXTree tree(tree_update);
3741
3742 AXNode* list = tree.GetFromId(1);
3743 EXPECT_FALSE(list->GetPosInSet());
3744 EXPECT_OPTIONAL_EQ(2, list->GetSetSize());
3745
3746 AXNode* radiobutton1 = tree.GetFromId(2);
3747 EXPECT_OPTIONAL_EQ(1, radiobutton1->GetPosInSet());
3748 EXPECT_OPTIONAL_EQ(3, radiobutton1->GetSetSize());
3749 AXNode* item1 = tree.GetFromId(3);
3750 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3751 EXPECT_OPTIONAL_EQ(2, item1->GetSetSize());
3752 AXNode* radiobutton2 = tree.GetFromId(4);
3753 EXPECT_OPTIONAL_EQ(2, radiobutton2->GetPosInSet());
3754 EXPECT_OPTIONAL_EQ(3, radiobutton2->GetSetSize());
3755 AXNode* item2 = tree.GetFromId(5);
3756 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3757 EXPECT_OPTIONAL_EQ(2, item2->GetSetSize());
3758 AXNode* radiobutton3 = tree.GetFromId(6);
3759 EXPECT_OPTIONAL_EQ(3, radiobutton3->GetPosInSet());
3760 EXPECT_OPTIONAL_EQ(3, radiobutton3->GetSetSize());
3761
3762 // Ensure that the setsize of list was not modified after calling GetPosInSet
3763 // and GetSetSize on kRadioButtons.
3764 EXPECT_FALSE(list->GetPosInSet());
3765 EXPECT_OPTIONAL_EQ(2, list->GetSetSize());
3766}
3767
3768// Tests GetPosInSet and GetSetSize on a flat tree representation. According
3769// to the tree representation, the three elements are siblings. However,
3770// due to the presence of the kHierarchicalLevel attribute, they all belong
3771// to different sets.
3772TEST(AXTreeTest, SetSizePosInSetFlatTree) {
3773 AXTreeUpdate tree_update;
3774 tree_update.root_id = 1;
3775 tree_update.nodes.resize(4);
3776 tree_update.nodes[0].id = 1;
3777 tree_update.nodes[0].role = ax::mojom::Role::kTree;
3778 tree_update.nodes[0].child_ids = {2, 3, 4};
3779 tree_update.nodes[1].id = 2;
3780 tree_update.nodes[1].role = ax::mojom::Role::kTreeItem; // 1 of 1
3781 tree_update.nodes[1].AddIntAttribute(
3783 tree_update.nodes[2].id = 3;
3784 tree_update.nodes[2].role = ax::mojom::Role::kTreeItem; // 1 of 1
3785 tree_update.nodes[2].AddIntAttribute(
3787 tree_update.nodes[3].id = 4;
3788 tree_update.nodes[3].role = ax::mojom::Role::kTreeItem; // 1 of 1
3789 tree_update.nodes[3].AddIntAttribute(
3791 AXTree tree(tree_update);
3792
3793 AXNode* item1_level1 = tree.GetFromId(2);
3794 EXPECT_OPTIONAL_EQ(1, item1_level1->GetPosInSet());
3795 EXPECT_OPTIONAL_EQ(1, item1_level1->GetSetSize());
3796 AXNode* item1_level2 = tree.GetFromId(3);
3797 EXPECT_OPTIONAL_EQ(1, item1_level2->GetPosInSet());
3798 EXPECT_OPTIONAL_EQ(1, item1_level2->GetSetSize());
3799 AXNode* item1_level3 = tree.GetFromId(4);
3800 EXPECT_OPTIONAL_EQ(1, item1_level3->GetPosInSet());
3801 EXPECT_OPTIONAL_EQ(1, item1_level3->GetSetSize());
3802}
3803
3804// Tests GetPosInSet and GetSetSize on a flat tree representation, where only
3805// the level is specified.
3806TEST(AXTreeTest, SetSizePosInSetFlatTreeLevelsOnly) {
3807 AXTreeUpdate tree_update;
3808 tree_update.root_id = 1;
3809 tree_update.nodes.resize(9);
3810 tree_update.nodes[0].id = 1;
3811 tree_update.nodes[0].role = ax::mojom::Role::kTree;
3812 tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6, 7, 8, 9};
3813 tree_update.nodes[1].id = 2;
3814 tree_update.nodes[1].role = ax::mojom::Role::kTreeItem; // 1 of 3
3815 tree_update.nodes[1].AddIntAttribute(
3817 tree_update.nodes[2].id = 3;
3818 tree_update.nodes[2].role = ax::mojom::Role::kTreeItem; // 1 of 2
3819 tree_update.nodes[2].AddIntAttribute(
3821 tree_update.nodes[3].id = 4;
3822 tree_update.nodes[3].role = ax::mojom::Role::kTreeItem; // 2 of 2
3823 tree_update.nodes[3].AddIntAttribute(
3825 tree_update.nodes[4].id = 5;
3826 tree_update.nodes[4].role = ax::mojom::Role::kTreeItem; // 2 of 3
3827 tree_update.nodes[4].AddIntAttribute(
3829 tree_update.nodes[5].id = 6;
3830 tree_update.nodes[5].role = ax::mojom::Role::kTreeItem; // 1 of 3
3831 tree_update.nodes[5].AddIntAttribute(
3833 tree_update.nodes[6].id = 7;
3834 tree_update.nodes[6].role = ax::mojom::Role::kTreeItem; // 2 of 3
3835 tree_update.nodes[6].AddIntAttribute(
3837 tree_update.nodes[7].id = 8;
3838 tree_update.nodes[7].role = ax::mojom::Role::kTreeItem; // 3 of 3
3839 tree_update.nodes[7].AddIntAttribute(
3841 tree_update.nodes[8].id = 9;
3842 tree_update.nodes[8].role = ax::mojom::Role::kTreeItem; // 3 of 3
3843 tree_update.nodes[8].AddIntAttribute(
3845 AXTree tree(tree_update);
3846
3847 // The order in which we query the nodes should not matter.
3848 AXNode* item3_level1 = tree.GetFromId(9);
3849 EXPECT_OPTIONAL_EQ(3, item3_level1->GetPosInSet());
3850 EXPECT_OPTIONAL_EQ(3, item3_level1->GetSetSize());
3851 AXNode* item3_level2a = tree.GetFromId(8);
3852 EXPECT_OPTIONAL_EQ(3, item3_level2a->GetPosInSet());
3853 EXPECT_OPTIONAL_EQ(3, item3_level2a->GetSetSize());
3854 AXNode* item2_level2a = tree.GetFromId(7);
3855 EXPECT_OPTIONAL_EQ(2, item2_level2a->GetPosInSet());
3856 EXPECT_OPTIONAL_EQ(3, item2_level2a->GetSetSize());
3857 AXNode* item1_level2a = tree.GetFromId(6);
3858 EXPECT_OPTIONAL_EQ(1, item1_level2a->GetPosInSet());
3859 EXPECT_OPTIONAL_EQ(3, item1_level2a->GetSetSize());
3860 AXNode* item2_level1 = tree.GetFromId(5);
3861 EXPECT_OPTIONAL_EQ(2, item2_level1->GetPosInSet());
3862 EXPECT_OPTIONAL_EQ(3, item2_level1->GetSetSize());
3863 AXNode* item2_level2 = tree.GetFromId(4);
3864 EXPECT_OPTIONAL_EQ(2, item2_level2->GetPosInSet());
3865 EXPECT_OPTIONAL_EQ(2, item2_level2->GetSetSize());
3866 AXNode* item1_level2 = tree.GetFromId(3);
3867 EXPECT_OPTIONAL_EQ(1, item1_level2->GetPosInSet());
3868 EXPECT_OPTIONAL_EQ(2, item1_level2->GetSetSize());
3869 AXNode* item1_level1 = tree.GetFromId(2);
3870 EXPECT_OPTIONAL_EQ(1, item1_level1->GetPosInSet());
3871 EXPECT_OPTIONAL_EQ(3, item1_level1->GetSetSize());
3872 AXNode* ordered_set = tree.GetFromId(1);
3873 EXPECT_OPTIONAL_EQ(3, ordered_set->GetSetSize());
3874}
3875
3876// Tests that GetPosInSet and GetSetSize work while a tree is being
3877// unserialized.
3878TEST(AXTreeTest, SetSizePosInSetSubtreeDeleted) {
3879 AXTreeUpdate initial_state;
3880 initial_state.root_id = 1;
3881 initial_state.nodes.resize(3);
3882 initial_state.nodes[0].id = 1;
3883 initial_state.nodes[0].role = ax::mojom::Role::kTree;
3884 initial_state.nodes[0].child_ids = {2, 3};
3885 initial_state.nodes[1].id = 2;
3886 initial_state.nodes[1].role = ax::mojom::Role::kTreeItem;
3887 initial_state.nodes[2].id = 3;
3888 initial_state.nodes[2].role = ax::mojom::Role::kTreeItem;
3889 AXTree tree(initial_state);
3890
3891 AXNode* tree_node = tree.GetFromId(1);
3892 AXNode* item = tree.GetFromId(3);
3893
3894 // This should work normally.
3895 EXPECT_OPTIONAL_EQ(2, item->GetPosInSet());
3896 EXPECT_OPTIONAL_EQ(2, item->GetSetSize());
3897
3898 // Remove item from tree.
3899 AXTreeUpdate tree_update = initial_state;
3900 tree_update.nodes.resize(1);
3901 tree_update.nodes[0].child_ids = {2};
3902
3903 ASSERT_TRUE(tree.Unserialize(tree_update));
3904
3905 // These values are lazily created, so to test that they fail when
3906 // called in the middle of a tree update, fake the update state.
3908 ASSERT_FALSE(tree_node->GetPosInSet());
3909 ASSERT_FALSE(tree_node->GetSetSize());
3910
3911 // Then reset the state to make sure we have the expected values
3912 // after |Unserialize|.
3913 tree.SetTreeUpdateInProgressState(false);
3914 ASSERT_FALSE(tree_node->GetPosInSet());
3915 EXPECT_OPTIONAL_EQ(1, tree_node->GetSetSize());
3916}
3917
3918// Tests that GetPosInSet and GetSetSize work when there are ignored nodes.
3919TEST(AXTreeTest, SetSizePosInSetIgnoredItem) {
3920 AXTreeUpdate initial_state;
3921 initial_state.root_id = 1;
3922 initial_state.nodes.resize(3);
3923 initial_state.nodes[0].id = 1;
3924 initial_state.nodes[0].role = ax::mojom::Role::kTree;
3925 initial_state.nodes[0].child_ids = {2, 3};
3926 initial_state.nodes[1].id = 2;
3927 initial_state.nodes[1].role = ax::mojom::Role::kTreeItem;
3928 initial_state.nodes[2].id = 3;
3929 initial_state.nodes[2].role = ax::mojom::Role::kTreeItem;
3930 AXTree tree(initial_state);
3931
3932 AXNode* tree_node = tree.GetFromId(1);
3933 AXNode* item1 = tree.GetFromId(2);
3934 AXNode* item2 = tree.GetFromId(3);
3935
3936 // This should work normally.
3937 ASSERT_FALSE(tree_node->GetPosInSet());
3938 EXPECT_OPTIONAL_EQ(2, tree_node->GetSetSize());
3939
3940 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
3941 EXPECT_OPTIONAL_EQ(2, item1->GetSetSize());
3942
3943 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
3944 EXPECT_OPTIONAL_EQ(2, item2->GetSetSize());
3945
3946 // Remove item from tree.
3947 AXTreeUpdate tree_update;
3948 tree_update.nodes.resize(1);
3949 tree_update.nodes[0] = initial_state.nodes[1];
3950 tree_update.nodes[0].AddState(ax::mojom::State::kIgnored);
3951
3952 ASSERT_TRUE(tree.Unserialize(tree_update));
3953
3954 ASSERT_FALSE(tree_node->GetPosInSet());
3955 EXPECT_OPTIONAL_EQ(1, tree_node->GetSetSize());
3956
3957 // Ignored nodes are not part of ordered sets.
3958 EXPECT_FALSE(item1->GetPosInSet());
3959 EXPECT_FALSE(item1->GetSetSize());
3960
3961 EXPECT_OPTIONAL_EQ(1, item2->GetPosInSet());
3962 EXPECT_OPTIONAL_EQ(1, item2->GetSetSize());
3963}
3964
3965// Tests that kPopUpButtons are assigned the SetSize of the wrapped
3966// kMenuListPopup, if one is present.
3967TEST(AXTreeTest, SetSizePosInSetPopUpButton) {
3968 AXTreeUpdate initial_state;
3969 initial_state.root_id = 1;
3970 initial_state.nodes.resize(6);
3971 initial_state.nodes[0].id = 1;
3972 initial_state.nodes[0].child_ids = {2, 3};
3973 initial_state.nodes[1].id = 2;
3974 initial_state.nodes[1].role = ax::mojom::Role::kPopUpButton;
3975 initial_state.nodes[2].id = 3;
3976 initial_state.nodes[2].role = ax::mojom::Role::kPopUpButton;
3977 initial_state.nodes[2].child_ids = {4};
3978 initial_state.nodes[3].id = 4;
3979 initial_state.nodes[3].role = ax::mojom::Role::kMenuListPopup;
3980 initial_state.nodes[3].child_ids = {5, 6};
3981 initial_state.nodes[4].id = 5;
3982 initial_state.nodes[4].role = ax::mojom::Role::kMenuListOption;
3983 initial_state.nodes[5].id = 6;
3984 initial_state.nodes[5].role = ax::mojom::Role::kMenuListOption;
3985 AXTree tree(initial_state);
3986
3987 // The first popupbutton should have SetSize of 0.
3988 AXNode* popup_button_1 = tree.GetFromId(2);
3989 EXPECT_OPTIONAL_EQ(0, popup_button_1->GetSetSize());
3990 // The second popupbutton should have SetSize of 2, since the menulistpopup
3991 // that it wraps has a SetSize of 2.
3992 AXNode* popup_button_2 = tree.GetFromId(3);
3993 EXPECT_OPTIONAL_EQ(2, popup_button_2->GetSetSize());
3994}
3995
3996// Tests that PosInSet and SetSize are still correctly calculated when there
3997// are nodes with role of kUnknown layered between items and ordered set.
3998TEST(AXTreeTest, SetSizePosInSetUnkown) {
3999 AXTreeUpdate initial_state;
4000 initial_state.root_id = 1;
4001 initial_state.nodes.resize(5);
4002 initial_state.nodes[0].id = 1;
4003 initial_state.nodes[0].child_ids = {2};
4004 initial_state.nodes[0].role = ax::mojom::Role::kMenu;
4005 initial_state.nodes[1].id = 2;
4006 initial_state.nodes[1].role = ax::mojom::Role::kUnknown;
4007 initial_state.nodes[1].child_ids = {3};
4008 initial_state.nodes[2].id = 3;
4009 initial_state.nodes[2].role = ax::mojom::Role::kUnknown;
4010 initial_state.nodes[2].child_ids = {4, 5};
4011 initial_state.nodes[3].id = 4;
4012 initial_state.nodes[3].role = ax::mojom::Role::kMenuItem;
4013 initial_state.nodes[4].id = 5;
4014 initial_state.nodes[4].role = ax::mojom::Role::kMenuItem;
4015 AXTree tree(initial_state);
4016
4017 AXNode* menu = tree.GetFromId(1);
4018 EXPECT_OPTIONAL_EQ(2, menu->GetSetSize());
4019 AXNode* item1 = tree.GetFromId(4);
4020 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
4021 EXPECT_OPTIONAL_EQ(2, item1->GetSetSize());
4022 AXNode* item2 = tree.GetFromId(5);
4023 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
4024 EXPECT_OPTIONAL_EQ(2, item2->GetSetSize());
4025}
4026
4027TEST(AXTreeTest, SetSizePosInSetMenuItemValidChildOfMenuListPopup) {
4028 AXTreeUpdate initial_state;
4029 initial_state.root_id = 1;
4030 initial_state.nodes.resize(3);
4031 initial_state.nodes[0].id = 1;
4032 initial_state.nodes[0].child_ids = {2, 3};
4033 initial_state.nodes[0].role = ax::mojom::Role::kMenuListPopup;
4034 initial_state.nodes[1].id = 2;
4035 initial_state.nodes[1].role = ax::mojom::Role::kMenuItem;
4036 initial_state.nodes[2].id = 3;
4037 initial_state.nodes[2].role = ax::mojom::Role::kMenuListOption;
4038 AXTree tree(initial_state);
4039
4040 AXNode* menu = tree.GetFromId(1);
4041 EXPECT_OPTIONAL_EQ(2, menu->GetSetSize());
4042 AXNode* item1 = tree.GetFromId(2);
4043 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
4044 EXPECT_OPTIONAL_EQ(2, item1->GetSetSize());
4045 AXNode* item2 = tree.GetFromId(3);
4046 EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
4047 EXPECT_OPTIONAL_EQ(2, item2->GetSetSize());
4048}
4049
4050TEST(AXTreeTest, SetSizePostInSetListBoxOptionWithGroup) {
4051 AXTreeUpdate initial_state;
4052 initial_state.root_id = 1;
4053 initial_state.nodes.resize(7);
4054 initial_state.nodes[0].id = 1;
4055 initial_state.nodes[0].child_ids = {2, 3};
4056 initial_state.nodes[0].role = ax::mojom::Role::kListBox;
4057 initial_state.nodes[1].id = 2;
4058 initial_state.nodes[1].child_ids = {4, 5};
4059 initial_state.nodes[1].role = ax::mojom::Role::kGroup;
4060 initial_state.nodes[2].id = 3;
4061 initial_state.nodes[2].child_ids = {6, 7};
4062 initial_state.nodes[2].role = ax::mojom::Role::kGroup;
4063 initial_state.nodes[3].id = 4;
4064 initial_state.nodes[3].role = ax::mojom::Role::kListBoxOption;
4065 initial_state.nodes[4].id = 5;
4066 initial_state.nodes[4].role = ax::mojom::Role::kListBoxOption;
4067 initial_state.nodes[5].id = 6;
4068 initial_state.nodes[5].role = ax::mojom::Role::kListBoxOption;
4069 initial_state.nodes[6].id = 7;
4070 initial_state.nodes[6].role = ax::mojom::Role::kListBoxOption;
4071 AXTree tree(initial_state);
4072
4073 AXNode* listbox_option1 = tree.GetFromId(4);
4074 EXPECT_OPTIONAL_EQ(1, listbox_option1->GetPosInSet());
4075 EXPECT_OPTIONAL_EQ(4, listbox_option1->GetSetSize());
4076 AXNode* listbox_option2 = tree.GetFromId(5);
4077 EXPECT_OPTIONAL_EQ(2, listbox_option2->GetPosInSet());
4078 EXPECT_OPTIONAL_EQ(4, listbox_option2->GetSetSize());
4079 AXNode* listbox_option3 = tree.GetFromId(6);
4080 EXPECT_OPTIONAL_EQ(3, listbox_option3->GetPosInSet());
4081 EXPECT_OPTIONAL_EQ(4, listbox_option3->GetSetSize());
4082 AXNode* listbox_option4 = tree.GetFromId(7);
4083 EXPECT_OPTIONAL_EQ(4, listbox_option4->GetPosInSet());
4084 EXPECT_OPTIONAL_EQ(4, listbox_option4->GetSetSize());
4085}
4086
4087TEST(AXTreeTest, SetSizePosInSetGroup) {
4088 // The behavior of a group changes depending on the context it appears in
4089 // i.e. if it appears alone vs. if it is contained within another set-like
4090 // element. The below example shows a group standing alone:
4091 //
4092 // <ul role="group"> <!-- SetSize = 3 -->
4093 // <li role="menuitemradio" aria-checked="true">Small</li>
4094 // <li role="menuitemradio" aria-checked="false">Medium</li>
4095 // <li role="menuitemradio" aria-checked="false">Large</li>
4096 // </ul>
4097 //
4098 // However, when it is contained within another set-like element, like a
4099 // listbox, it should simply act like a generic container:
4100 //
4101 // <div role="listbox"> <!-- SetSize = 3 -->
4102 // <div role="option">Red</div> <!-- 1 of 3 -->
4103 // <div role="option">Yellow</div> <!-- 2 of 3 -->
4104 // <div role="group"> <!-- SetSize = 0 -->
4105 // <div role="option">Blue</div> <!-- 3 of 3 -->
4106 // </div>
4107 // </div>
4108 //
4109 // Please note: the GetPosInSet and GetSetSize functions take slightly
4110 // different code paths when initially run on items vs. the container.
4111 // Exercise both code paths in this test.
4112
4113 AXTreeUpdate tree_update;
4114 tree_update.root_id = 1;
4115 tree_update.nodes.resize(6);
4116 tree_update.nodes[0].id = 1;
4117 tree_update.nodes[0].role = ax::mojom::Role::kMenu; // SetSize = 4
4118 tree_update.nodes[0].child_ids = {2, 6};
4119 tree_update.nodes[1].id = 2;
4120 tree_update.nodes[1].role = ax::mojom::Role::kGroup; // SetSize = 0
4121 tree_update.nodes[1].child_ids = {3, 4, 5};
4122 tree_update.nodes[2].id = 3;
4123 tree_update.nodes[2].role = ax::mojom::Role::kMenuItemRadio; // 1 of 4
4124 tree_update.nodes[3].id = 4;
4125 tree_update.nodes[3].role = ax::mojom::Role::kMenuItemRadio; // 2 of 4
4126 tree_update.nodes[4].id = 5;
4127 tree_update.nodes[4].role = ax::mojom::Role::kMenuItemRadio; // 3 of 4
4128 tree_update.nodes[5].id = 6;
4129 tree_update.nodes[5].role = ax::mojom::Role::kMenuItemRadio; // 4 of 4
4130 AXTree tree(tree_update);
4131
4132 // Get data on kMenu first.
4133 AXNode* menu = tree.GetFromId(1);
4134 EXPECT_OPTIONAL_EQ(4, menu->GetSetSize());
4135 AXNode* group = tree.GetFromId(2);
4136 EXPECT_FALSE(group->GetSetSize());
4137 // The below values should have already been computed and cached.
4138 AXNode* item1 = tree.GetFromId(3);
4139 EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
4140 EXPECT_OPTIONAL_EQ(4, item1->GetSetSize());
4141 AXNode* item4 = tree.GetFromId(6);
4142 EXPECT_OPTIONAL_EQ(4, item4->GetPosInSet());
4143 EXPECT_OPTIONAL_EQ(4, item4->GetSetSize());
4144
4145 AXTreeUpdate next_tree_update;
4146 next_tree_update.root_id = 1;
4147 next_tree_update.nodes.resize(6);
4148 next_tree_update.nodes[0].id = 1;
4149 next_tree_update.nodes[0].role = ax::mojom::Role::kListBox; // SetSize = 4
4150 next_tree_update.nodes[0].child_ids = {2, 6};
4151 next_tree_update.nodes[1].id = 2;
4152 next_tree_update.nodes[1].role = ax::mojom::Role::kGroup; // SetSize = 0
4153 next_tree_update.nodes[1].child_ids = {3, 4, 5};
4154 next_tree_update.nodes[2].id = 3;
4155 next_tree_update.nodes[2].role = ax::mojom::Role::kListBoxOption; // 1 of 4
4156 next_tree_update.nodes[3].id = 4;
4157 next_tree_update.nodes[3].role = ax::mojom::Role::kListBoxOption; // 2 of 4
4158 next_tree_update.nodes[4].id = 5;
4159 next_tree_update.nodes[4].role = ax::mojom::Role::kListBoxOption; // 3 of 4
4160 next_tree_update.nodes[5].id = 6;
4161 next_tree_update.nodes[5].role = ax::mojom::Role::kListBoxOption; // 4 of 4
4162 AXTree next_tree(next_tree_update);
4163
4164 // Get data on kListBoxOption first.
4165 AXNode* option1 = next_tree.GetFromId(3);
4166 EXPECT_OPTIONAL_EQ(1, option1->GetPosInSet());
4167 EXPECT_OPTIONAL_EQ(4, option1->GetSetSize());
4168 AXNode* option2 = next_tree.GetFromId(4);
4169 EXPECT_OPTIONAL_EQ(2, option2->GetPosInSet());
4170 EXPECT_OPTIONAL_EQ(4, option2->GetSetSize());
4171 AXNode* option3 = next_tree.GetFromId(5);
4172 EXPECT_OPTIONAL_EQ(3, option3->GetPosInSet());
4173 EXPECT_OPTIONAL_EQ(4, option3->GetSetSize());
4174 AXNode* option4 = next_tree.GetFromId(6);
4175 EXPECT_OPTIONAL_EQ(4, option4->GetPosInSet());
4176 EXPECT_OPTIONAL_EQ(4, option4->GetSetSize());
4177 AXNode* next_group = next_tree.GetFromId(2);
4178 EXPECT_FALSE(next_group->GetSetSize());
4179 // The below value should have already been computed and cached.
4180 AXNode* listbox = next_tree.GetFromId(1);
4181 EXPECT_OPTIONAL_EQ(4, listbox->GetSetSize());
4182
4183 // Standalone groups are allowed.
4184 AXTreeUpdate third_tree_update;
4185 third_tree_update.root_id = 1;
4186 third_tree_update.nodes.resize(3);
4187 third_tree_update.nodes[0].id = 1;
4188 third_tree_update.nodes[0].role = ax::mojom::Role::kGroup;
4189 third_tree_update.nodes[0].child_ids = {2, 3};
4190 third_tree_update.nodes[1].id = 2;
4191 third_tree_update.nodes[1].role = ax::mojom::Role::kListItem;
4192 third_tree_update.nodes[2].id = 3;
4193 third_tree_update.nodes[2].role = ax::mojom::Role::kListItem;
4194 AXTree third_tree(third_tree_update);
4195
4196 // Ensure that groups can't also stand alone.
4197 AXNode* last_group = third_tree.GetFromId(1);
4198 EXPECT_OPTIONAL_EQ(2, last_group->GetSetSize());
4199 AXNode* list_item1 = third_tree.GetFromId(2);
4200 EXPECT_OPTIONAL_EQ(1, list_item1->GetPosInSet());
4201 EXPECT_OPTIONAL_EQ(2, list_item1->GetSetSize());
4202 AXNode* list_item2 = third_tree.GetFromId(3);
4203 EXPECT_OPTIONAL_EQ(2, list_item2->GetPosInSet());
4204 EXPECT_OPTIONAL_EQ(2, list_item2->GetSetSize());
4205
4206 // Test nested groups.
4207 AXTreeUpdate last_tree_update;
4208 last_tree_update.root_id = 1;
4209 last_tree_update.nodes.resize(6);
4210 last_tree_update.nodes[0].id = 1;
4211 last_tree_update.nodes[0].role = ax::mojom::Role::kMenuBar;
4212 last_tree_update.nodes[0].child_ids = {2};
4213 last_tree_update.nodes[1].id = 2;
4214 last_tree_update.nodes[1].role = ax::mojom::Role::kGroup;
4215 last_tree_update.nodes[1].child_ids = {3, 4};
4216 last_tree_update.nodes[2].id = 3;
4217 last_tree_update.nodes[2].role = ax::mojom::Role::kMenuItemCheckBox;
4218 last_tree_update.nodes[3].id = 4;
4219 last_tree_update.nodes[3].role = ax::mojom::Role::kGroup;
4220 last_tree_update.nodes[3].child_ids = {5, 6};
4221 last_tree_update.nodes[4].id = 5;
4222 last_tree_update.nodes[4].role = ax::mojom::Role::kMenuItemCheckBox;
4223 last_tree_update.nodes[5].id = 6;
4224 last_tree_update.nodes[5].role = ax::mojom::Role::kMenuItemCheckBox;
4225 AXTree last_tree(last_tree_update);
4226
4227 AXNode* checkbox1 = last_tree.GetFromId(3);
4228 EXPECT_OPTIONAL_EQ(1, checkbox1->GetPosInSet());
4229 EXPECT_OPTIONAL_EQ(3, checkbox1->GetSetSize());
4230 AXNode* checkbox2 = last_tree.GetFromId(5);
4231 EXPECT_OPTIONAL_EQ(2, checkbox2->GetPosInSet());
4232 EXPECT_OPTIONAL_EQ(3, checkbox2->GetSetSize());
4233 AXNode* checkbox3 = last_tree.GetFromId(6);
4234 EXPECT_OPTIONAL_EQ(3, checkbox3->GetPosInSet());
4235 EXPECT_OPTIONAL_EQ(3, checkbox3->GetSetSize());
4236 AXNode* menu_bar = last_tree.GetFromId(1);
4237 EXPECT_OPTIONAL_EQ(3, menu_bar->GetSetSize());
4238 AXNode* outer_group = last_tree.GetFromId(2);
4239 EXPECT_FALSE(outer_group->GetSetSize());
4240 AXNode* inner_group = last_tree.GetFromId(4);
4241 EXPECT_FALSE(inner_group->GetSetSize());
4242}
4243
4244TEST(AXTreeTest, SetSizePosInSetHidden) {
4245 AXTreeUpdate tree_update;
4246 tree_update.root_id = 1;
4247 tree_update.nodes.resize(6);
4248 tree_update.nodes[0].id = 1;
4249 tree_update.nodes[0].role = ax::mojom::Role::kListBox; // SetSize = 4
4250 tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
4251 tree_update.nodes[1].id = 2;
4252 tree_update.nodes[1].role = ax::mojom::Role::kListBoxOption; // 1 of 4
4253 tree_update.nodes[2].id = 3;
4254 tree_update.nodes[2].role = ax::mojom::Role::kListBoxOption; // 2 of 4
4255 tree_update.nodes[3].id = 4;
4256 tree_update.nodes[3].role = ax::mojom::Role::kListBoxOption; // Hidden
4257 tree_update.nodes[3].AddState(ax::mojom::State::kInvisible);
4258 tree_update.nodes[4].id = 5;
4259 tree_update.nodes[4].role = ax::mojom::Role::kListBoxOption; // 3 of 4
4260 tree_update.nodes[5].id = 6;
4261 tree_update.nodes[5].role = ax::mojom::Role::kListBoxOption; // 4 of 4
4262 AXTree tree(tree_update);
4263
4264 AXNode* list_box = tree.GetFromId(1);
4265 EXPECT_OPTIONAL_EQ(4, list_box->GetSetSize());
4266 AXNode* option1 = tree.GetFromId(2);
4267 EXPECT_OPTIONAL_EQ(1, option1->GetPosInSet());
4268 EXPECT_OPTIONAL_EQ(4, option1->GetSetSize());
4269 AXNode* option2 = tree.GetFromId(3);
4270 EXPECT_OPTIONAL_EQ(2, option2->GetPosInSet());
4271 EXPECT_OPTIONAL_EQ(4, option2->GetSetSize());
4272 AXNode* option_hidden = tree.GetFromId(4);
4273 EXPECT_FALSE(option_hidden->GetPosInSet());
4274 EXPECT_FALSE(option_hidden->GetSetSize());
4275 AXNode* option3 = tree.GetFromId(5);
4276 EXPECT_OPTIONAL_EQ(3, option3->GetPosInSet());
4277 EXPECT_OPTIONAL_EQ(4, option3->GetSetSize());
4278 AXNode* option4 = tree.GetFromId(6);
4279 EXPECT_OPTIONAL_EQ(4, option4->GetPosInSet());
4280 EXPECT_OPTIONAL_EQ(4, option4->GetSetSize());
4281}
4282
4283// Tests that we get the correct PosInSet and SetSize values when using an
4284// aria-controls relationship.
4285TEST(AXTreeTest, SetSizePosInSetControls) {
4286 std::vector<int32_t> three;
4287 three.push_back(3);
4288 std::vector<int32_t> hundred;
4289 hundred.push_back(100);
4290 std::vector<int32_t> eight;
4291 eight.push_back(8);
4292 AXTreeUpdate tree_update;
4293 tree_update.root_id = 1;
4294 tree_update.nodes.resize(8);
4295 tree_update.nodes[0].id = 1;
4296 tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
4297 tree_update.nodes[0].child_ids = {2, 3, 7, 8};
4298 tree_update.nodes[1].id = 2;
4299 tree_update.nodes[1].role = ax::mojom::Role::kPopUpButton; // SetSize = 3
4300 tree_update.nodes[1].AddIntListAttribute(
4302 tree_update.nodes[1].SetHasPopup(ax::mojom::HasPopup::kMenu);
4303 tree_update.nodes[2].id = 3;
4304 tree_update.nodes[2].role = ax::mojom::Role::kMenu; // SetSize = 3
4305 tree_update.nodes[2].child_ids = {4, 5, 6};
4306 tree_update.nodes[3].id = 4;
4307 tree_update.nodes[3].role = ax::mojom::Role::kMenuItem; // 1 of 3
4308 tree_update.nodes[4].id = 5;
4309 tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 2 of 3
4310 tree_update.nodes[5].id = 6;
4311 tree_update.nodes[5].role = ax::mojom::Role::kMenuItem; // 3 of 3
4312 tree_update.nodes[6].id = 7;
4313 tree_update.nodes[6].role =
4314 ax::mojom::Role::kPopUpButton; // Test an invalid controls id.
4315 tree_update.nodes[6].AddIntListAttribute(
4317 // GetSetSize should handle self-references e.g. if a popup button controls
4318 // itself.
4319 tree_update.nodes[7].id = 8;
4320 tree_update.nodes[7].role = ax::mojom::Role::kPopUpButton;
4321 tree_update.nodes[7].AddIntListAttribute(
4323 AXTree tree(tree_update);
4324
4325 AXNode* button = tree.GetFromId(2);
4326 EXPECT_OPTIONAL_EQ(3, button->GetSetSize());
4327 EXPECT_FALSE(button->GetPosInSet());
4328 AXNode* menu = tree.GetFromId(3);
4329 EXPECT_OPTIONAL_EQ(3, menu->GetSetSize());
4330 AXNode* item = tree.GetFromId(4);
4331 EXPECT_OPTIONAL_EQ(1, item->GetPosInSet());
4332 EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
4333 item = tree.GetFromId(5);
4334 EXPECT_OPTIONAL_EQ(2, item->GetPosInSet());
4335 EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
4336 item = tree.GetFromId(6);
4337 EXPECT_OPTIONAL_EQ(3, item->GetPosInSet());
4338 EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
4339 button = tree.GetFromId(7);
4340 EXPECT_OPTIONAL_EQ(0, button->GetSetSize());
4341 button = tree.GetFromId(8);
4342 EXPECT_OPTIONAL_EQ(0, button->GetSetSize());
4343}
4344
4345// Tests GetPosInSet and GetSetSize return the assigned int attribute values
4346// when a pop-up button is a leaf node.
4347TEST(AXTreeTest, SetSizePosInSetLeafPopUpButton) {
4348 AXTreeUpdate tree_update;
4349 tree_update.root_id = 1;
4350 tree_update.nodes.resize(2);
4351 tree_update.nodes[0].id = 1;
4352 tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
4353 tree_update.nodes[0].child_ids = {2};
4354 tree_update.nodes[1].id = 2;
4355 tree_update.nodes[1].role = ax::mojom::Role::kPopUpButton;
4356 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 3);
4357 tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 77);
4358 AXTree tree(tree_update);
4359
4360 AXNode* pop_up_button = tree.GetFromId(2);
4361 EXPECT_OPTIONAL_EQ(3, pop_up_button->GetPosInSet());
4362 EXPECT_OPTIONAL_EQ(77, pop_up_button->GetSetSize());
4363}
4364
4365TEST(AXTreeTest, OnNodeWillBeDeletedHasValidUnignoredParent) {
4366 AXTreeUpdate initial_state;
4367 initial_state.root_id = 1;
4368 initial_state.nodes.resize(3);
4369 initial_state.nodes[0].id = 1;
4370 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4371 initial_state.nodes[0].child_ids = {2};
4372 initial_state.nodes[1].id = 2;
4373 initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer;
4374 initial_state.nodes[1].child_ids = {3};
4375 initial_state.nodes[2].id = 3;
4376 initial_state.nodes[2].role = ax::mojom::Role::kGenericContainer;
4377 AXTree tree(initial_state);
4378
4379 AXTreeUpdate tree_update;
4380 tree_update.nodes.resize(1);
4381 // Remove child from node:2, and add State::kIgnored
4382 tree_update.nodes[0] = initial_state.nodes[1];
4383 tree_update.nodes[0].AddState(ax::mojom::State::kIgnored);
4384 tree_update.nodes[0].child_ids.clear();
4385
4386 // Before node:3 is deleted, the unignored parent is node:2.
4387 // Assert that this is the case in |OnNodeWillBeDeleted|.
4388 TestAXTreeObserver test_observer(&tree);
4389 test_observer.unignored_parent_id_before_node_deleted = 2;
4390 ASSERT_TRUE(tree.Unserialize(tree_update));
4391}
4392
4393TEST(AXTreeTest, OnNodeHasBeenDeleted) {
4394 AXTreeUpdate initial_state;
4395
4396 initial_state.root_id = 1;
4397 initial_state.nodes.resize(6);
4398 initial_state.nodes[0].id = 1;
4399 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4400 initial_state.nodes[0].child_ids = {2};
4401 initial_state.nodes[1].id = 2;
4402 initial_state.nodes[1].role = ax::mojom::Role::kButton;
4403 initial_state.nodes[1].child_ids = {3, 4};
4404 initial_state.nodes[2].id = 3;
4405 initial_state.nodes[2].role = ax::mojom::Role::kCheckBox;
4406 initial_state.nodes[3].id = 4;
4407 initial_state.nodes[3].role = ax::mojom::Role::kStaticText;
4408 initial_state.nodes[3].child_ids = {5, 6};
4409 initial_state.nodes[4].id = 5;
4410 initial_state.nodes[4].role = ax::mojom::Role::kInlineTextBox;
4411 initial_state.nodes[5].id = 6;
4412 initial_state.nodes[5].role = ax::mojom::Role::kInlineTextBox;
4413
4414 AXTree tree(initial_state);
4415
4417 update.nodes.resize(2);
4418 update.nodes[0] = initial_state.nodes[1];
4419 update.nodes[0].child_ids = {4};
4420 update.nodes[1] = initial_state.nodes[3];
4421 update.nodes[1].child_ids = {};
4422
4423 TestAXTreeObserver test_observer(&tree);
4424 ASSERT_TRUE(tree.Unserialize(update));
4425
4426 EXPECT_EQ(3U, test_observer.deleted_ids().size());
4427 EXPECT_EQ(3, test_observer.deleted_ids()[0]);
4428 EXPECT_EQ(5, test_observer.deleted_ids()[1]);
4429 EXPECT_EQ(6, test_observer.deleted_ids()[2]);
4430
4431 // Verify that the nodes we intend to delete in the update are actually
4432 // absent from the tree.
4433 for (auto id : test_observer.deleted_ids()) {
4434 SCOPED_TRACE(testing::Message()
4435 << "Node with id=" << id << ", should not exist in the tree");
4436 EXPECT_EQ(nullptr, tree.GetFromId(id));
4437 }
4438}
4439
4440// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
4441// Make sure that we correctly Unserialize if a newly created node is deleted,
4442// and possibly recreated later.
4443TEST(AXTreeTest, SingleUpdateDeletesNewlyCreatedChildNode) {
4444 AXTreeUpdate initial_state;
4445 initial_state.root_id = 1;
4446 initial_state.nodes.resize(1);
4447 initial_state.nodes[0].id = 1;
4448 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4449 AXTree tree(initial_state);
4450
4451 AXTreeUpdate tree_update;
4452 tree_update.nodes.resize(6);
4453 // Add child node:2
4454 tree_update.nodes[0] = initial_state.nodes[0];
4455 tree_update.nodes[0].child_ids = {2};
4456 tree_update.nodes[1].id = 2;
4457 tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer;
4458 // Remove child node:2
4459 tree_update.nodes[2] = initial_state.nodes[0];
4460 // Add child node:2
4461 tree_update.nodes[3] = initial_state.nodes[0];
4462 tree_update.nodes[3].child_ids = {2};
4463 tree_update.nodes[4].id = 2;
4464 tree_update.nodes[4].role = ax::mojom::Role::kGenericContainer;
4465 // Remove child node:2
4466 tree_update.nodes[5] = initial_state.nodes[0];
4467
4468 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4469
4470 ASSERT_EQ(
4471 "AXTree\n"
4472 "id=1 rootWebArea (0, 0)-(0, 0)\n",
4473 tree.ToString());
4474
4475 // Unserialize again, but with another add child.
4476 tree_update.nodes.resize(8);
4477 tree_update.nodes[6] = initial_state.nodes[0];
4478 tree_update.nodes[6].child_ids = {2};
4479 tree_update.nodes[7].id = 2;
4480 tree_update.nodes[7].role = ax::mojom::Role::kGenericContainer;
4481 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4482
4483 ASSERT_EQ(
4484 "AXTree\n"
4485 "id=1 rootWebArea (0, 0)-(0, 0) child_ids=2\n"
4486 " id=2 genericContainer (0, 0)-(0, 0)\n",
4487 tree.ToString());
4488}
4489
4490// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
4491// Make sure that we correctly Unserialize if a node is reparented multiple
4492// times.
4493TEST(AXTreeTest, SingleUpdateReparentsNodeMultipleTimes) {
4494 // ++{kRootWebArea, 1}
4495 // ++++{kList, 2}
4496 // ++++++{kListItem, 4}
4497 // ++++{kList, 3}
4498 AXTreeUpdate initial_state;
4499 initial_state.root_id = 1;
4500 initial_state.nodes.resize(4);
4501 initial_state.nodes[0].id = 1;
4502 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4503 initial_state.nodes[0].child_ids = {2, 3};
4504 initial_state.nodes[1].id = 2;
4505 initial_state.nodes[1].role = ax::mojom::Role::kList;
4506 initial_state.nodes[1].child_ids = {4};
4507 initial_state.nodes[2].id = 3;
4508 initial_state.nodes[2].role = ax::mojom::Role::kList;
4509 initial_state.nodes[3].id = 4;
4510 initial_state.nodes[3].role = ax::mojom::Role::kListItem;
4511 AXTree tree(initial_state);
4512
4513 AXTreeUpdate tree_update;
4514 tree_update.nodes.resize(6);
4515 // Remove child node:4
4516 tree_update.nodes[0].id = 2;
4517 tree_update.nodes[0].role = ax::mojom::Role::kList;
4518 // Reparent child node:4 onto node:3
4519 tree_update.nodes[1].id = 3;
4520 tree_update.nodes[1].role = ax::mojom::Role::kList;
4521 tree_update.nodes[1].child_ids = {4};
4522 tree_update.nodes[2].id = 4;
4523 tree_update.nodes[2].role = ax::mojom::Role::kListItem;
4524 // Remove child ndoe:4
4525 tree_update.nodes[3].id = 3;
4526 tree_update.nodes[3].role = ax::mojom::Role::kList;
4527 // Reparent child node:4 onto node:2
4528 tree_update.nodes[4].id = 2;
4529 tree_update.nodes[4].role = ax::mojom::Role::kList;
4530 tree_update.nodes[4].child_ids = {4};
4531 tree_update.nodes[5].id = 4;
4532 tree_update.nodes[5].role = ax::mojom::Role::kListItem;
4533
4534 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4535 EXPECT_EQ(
4536 "AXTree\nid=1 rootWebArea (0, 0)-(0, 0) child_ids=2,3\n"
4537 " id=2 list (0, 0)-(0, 0) child_ids=4\n"
4538 " id=4 listItem (0, 0)-(0, 0)\n"
4539 " id=3 list (0, 0)-(0, 0)\n",
4540 tree.ToString());
4541
4542 // Unserialize again, but with another reparent.
4543 tree_update.nodes.resize(9);
4544 tree_update.nodes[6] = tree_update.nodes[0];
4545 tree_update.nodes[7] = tree_update.nodes[1];
4546 tree_update.nodes[8] = tree_update.nodes[2];
4547
4548 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4549 EXPECT_EQ(
4550 "AXTree\nid=1 rootWebArea (0, 0)-(0, 0) child_ids=2,3\n"
4551 " id=2 list (0, 0)-(0, 0)\n"
4552 " id=3 list (0, 0)-(0, 0) child_ids=4\n"
4553 " id=4 listItem (0, 0)-(0, 0)\n",
4554 tree.ToString());
4555}
4556
4557// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
4558// Make sure that we correctly Unserialize if a newly created node toggles its
4559// ignored state.
4560TEST(AXTreeTest, SingleUpdateIgnoresNewlyCreatedUnignoredChildNode) {
4561 AXTreeUpdate initial_state;
4562 initial_state.root_id = 1;
4563 initial_state.nodes.resize(1);
4564 initial_state.nodes[0].id = 1;
4565 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4566 AXTree tree(initial_state);
4567
4568 AXTreeUpdate tree_update;
4569 tree_update.nodes.resize(3);
4570 // Add child node:2
4571 tree_update.nodes[0] = initial_state.nodes[0];
4572 tree_update.nodes[0].child_ids = {2};
4573 tree_update.nodes[1].id = 2;
4574 tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer;
4575 // Add State::kIgnored to node:2
4576 tree_update.nodes[2] = tree_update.nodes[1];
4577 tree_update.nodes[2].AddState(ax::mojom::State::kIgnored);
4578
4579 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4580
4581 ASSERT_EQ(
4582 "AXTree\n"
4583 "id=1 rootWebArea (0, 0)-(0, 0) child_ids=2\n"
4584 " id=2 genericContainer IGNORED (0, 0)-(0, 0)\n",
4585 tree.ToString());
4586}
4587
4588// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
4589// Make sure that we correctly Unserialize if a newly created node toggles its
4590// ignored state.
4591TEST(AXTreeTest, SingleUpdateTogglesIgnoredStateAfterCreatingNode) {
4592 AXTreeUpdate initial_state;
4593 initial_state.root_id = 1;
4594 initial_state.nodes.resize(1);
4595 initial_state.nodes[0].id = 1;
4596 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4597 AXTree tree(initial_state);
4598
4599 ASSERT_EQ(
4600 "AXTree\n"
4601 "id=1 rootWebArea (0, 0)-(0, 0)\n",
4602 tree.ToString());
4603
4604 AXTreeUpdate tree_update;
4605 tree_update.nodes.resize(5);
4606 // Add child node:2, node:3
4607 tree_update.nodes[0] = initial_state.nodes[0];
4608 tree_update.nodes[0].child_ids = {2, 3};
4609 tree_update.nodes[1].id = 2;
4610 tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer;
4611 tree_update.nodes[2].id = 3;
4612 tree_update.nodes[2].role = ax::mojom::Role::kGenericContainer;
4613 tree_update.nodes[2].AddState(ax::mojom::State::kIgnored);
4614 // Add State::kIgnored to node:2
4615 tree_update.nodes[3] = tree_update.nodes[1];
4616 tree_update.nodes[3].AddState(ax::mojom::State::kIgnored);
4617 // Remove State::kIgnored from node:3
4618 tree_update.nodes[4] = tree_update.nodes[2];
4619 tree_update.nodes[4].RemoveState(ax::mojom::State::kIgnored);
4620
4621 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4622
4623 ASSERT_EQ(
4624 "AXTree\n"
4625 "id=1 rootWebArea (0, 0)-(0, 0) child_ids=2,3\n"
4626 " id=2 genericContainer IGNORED (0, 0)-(0, 0)\n"
4627 " id=3 genericContainer (0, 0)-(0, 0)\n",
4628 tree.ToString());
4629}
4630
4631// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged.
4632// Make sure that we correctly Unserialize if a node toggles its ignored state
4633// and is then removed from the tree.
4634TEST(AXTreeTest, SingleUpdateTogglesIgnoredStateBeforeDestroyingNode) {
4635 AXTreeUpdate initial_state;
4636 initial_state.root_id = 1;
4637 initial_state.nodes.resize(3);
4638 initial_state.nodes[0].id = 1;
4639 initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
4640 initial_state.nodes[0].child_ids = {2, 3};
4641 initial_state.nodes[1].id = 2;
4642 initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer;
4643 initial_state.nodes[2].id = 3;
4644 initial_state.nodes[2].role = ax::mojom::Role::kGenericContainer;
4645 initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
4646 AXTree tree(initial_state);
4647
4648 ASSERT_EQ(
4649 "AXTree\n"
4650 "id=1 rootWebArea (0, 0)-(0, 0) child_ids=2,3\n"
4651 " id=2 genericContainer (0, 0)-(0, 0)\n"
4652 " id=3 genericContainer IGNORED (0, 0)-(0, 0)\n",
4653 tree.ToString());
4654
4655 AXTreeUpdate tree_update;
4656 tree_update.nodes.resize(3);
4657 // Add State::kIgnored to node:2
4658 tree_update.nodes[0] = initial_state.nodes[1];
4659 tree_update.nodes[0].AddState(ax::mojom::State::kIgnored);
4660 // Remove State::kIgnored from node:3
4661 tree_update.nodes[1] = initial_state.nodes[2];
4662 tree_update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
4663 // Remove child node:2, node:3
4664 tree_update.nodes[2] = initial_state.nodes[0];
4665 tree_update.nodes[2].child_ids.clear();
4666
4667 ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error();
4668
4669 ASSERT_EQ(
4670 "AXTree\n"
4671 "id=1 rootWebArea (0, 0)-(0, 0)\n",
4672 tree.ToString());
4673}
4674
4675// Tests that the IsInListMarker() method returns true if the current node is a
4676// list marker or if it's a descendant node of a list marker.
4677TEST(AXTreeTest, TestIsInListMarker) {
4678 // This test uses the template of a list of one element: "1. List item"
4679 AXTreeUpdate tree_update;
4680 tree_update.root_id = 1;
4681 tree_update.nodes.resize(8);
4682 tree_update.nodes[0].id = 1;
4683 tree_update.nodes[0].role = ax::mojom::Role::kList;
4684 tree_update.nodes[0].child_ids = {2, 3};
4685 tree_update.nodes[1].id = 2;
4686 tree_update.nodes[1].role = ax::mojom::Role::kListItem;
4687 tree_update.nodes[2].id = 3;
4688 tree_update.nodes[2].child_ids = {4, 7};
4689 tree_update.nodes[3].id = 4;
4690 tree_update.nodes[3].role = ax::mojom::Role::kListMarker;
4691 tree_update.nodes[3].child_ids = {5};
4692 tree_update.nodes[4].id = 5;
4693 tree_update.nodes[4].role = ax::mojom::Role::kStaticText; // "1. "
4694 tree_update.nodes[4].child_ids = {6};
4695 tree_update.nodes[5].id = 6;
4696 tree_update.nodes[5].role = ax::mojom::Role::kInlineTextBox; // "1. "
4697 tree_update.nodes[6].id = 7;
4698 tree_update.nodes[6].role = ax::mojom::Role::kStaticText; // "List item"
4699 tree_update.nodes[6].child_ids = {8};
4700 tree_update.nodes[7].id = 8;
4701 tree_update.nodes[7].role = ax::mojom::Role::kInlineTextBox; // "List item"
4702 AXTree tree(tree_update);
4703
4704 AXNode* list_node = tree.GetFromId(1);
4705 ASSERT_EQ(false, list_node->IsInListMarker());
4706
4707 AXNode* list_item_node = tree.GetFromId(2);
4708 ASSERT_EQ(false, list_item_node->IsInListMarker());
4709
4710 AXNode* list_marker1 = tree.GetFromId(4);
4711 ASSERT_EQ(true, list_marker1->IsInListMarker());
4712
4713 AXNode* static_node1 = tree.GetFromId(5);
4714 ASSERT_EQ(true, static_node1->IsInListMarker());
4715
4716 AXNode* inline_node1 = tree.GetFromId(6);
4717 ASSERT_EQ(true, inline_node1->IsInListMarker());
4718
4719 AXNode* static_node2 = tree.GetFromId(7);
4720 ASSERT_EQ(false, static_node2->IsInListMarker());
4721
4722 AXNode* inline_node2 = tree.GetFromId(8);
4723 ASSERT_EQ(false, inline_node2->IsInListMarker());
4724}
4725
4726} // namespace ui
#define TEST(S, s, D, expected)
#define EXPECT_OPTIONAL_EQ(expected, actual)
#define TEST_SELECTION(tree_update, tree, input, expected)
std::optional< int > GetSetSize()
Definition ax_node.cc:954
size_t GetUnignoredChildCount() const
Definition ax_node.cc:37
bool HasStringAttribute(ax::mojom::StringAttribute attribute) const
Definition ax_node.h:239
UnignoredChildIterator UnignoredChildrenEnd() const
Definition ax_node.cc:316
AXID id() const
Definition ax_node.h:110
size_t GetUnignoredIndexInParent() const
Definition ax_node.cc:66
AXNode * GetLastUnignoredChild() const
Definition ax_node.cc:81
static constexpr AXID kInvalidAXID
Definition ax_node.h:41
const std::string & GetStringAttribute(ax::mojom::StringAttribute attribute) const
Definition ax_node.h:242
bool IsInListMarker() const
Definition ax_node.cc:1139
AXNode * GetNextSibling() const
Definition ax_node.cc:347
AXNode * GetLastChild() const
Definition ax_node.cc:329
AXNode * GetPreviousUnignoredSibling() const
Definition ax_node.cc:224
UnignoredChildIterator UnignoredChildrenBegin() const
Definition ax_node.cc:311
AXNode * GetFirstChild() const
Definition ax_node.cc:322
AXNode * GetPreviousSibling() const
Definition ax_node.cc:337
AXNode * GetNextUnignoredSibling() const
Definition ax_node.cc:155
std::optional< int > GetPosInSet()
Definition ax_node.cc:949
const AXNodeData & data() const
Definition ax_node.h:112
static AXTreeID CreateNewAXTreeID()
Definition ax_tree_id.cc:47
void SetTreeUpdateInProgressState(bool set_tree_update_value)
Definition ax_tree.cc:2467
std::string ToString() const
Definition ax_tree.cc:1240
const std::string & error() const
Definition ax_tree.h:134
const AXTreeData & data() const
Definition ax_tree.h:59
virtual bool Unserialize(const AXTreeUpdate &update)
Definition ax_tree.cc:969
std::set< int32_t > GetReverseRelations(ax::mojom::IntAttribute attr, int32_t dst_id) const
Definition ax_tree.cc:922
const IntReverseRelationMap & int_reverse_relations()
Definition ax_tree.h:122
AXNode * GetFromId(int32_t id) const override
Definition ax_tree.cc:728
AXNode * root() const
Definition ax_tree.h:57
Selection GetUnignoredSelection() const override
Definition ax_tree.cc:2345
const IntListReverseRelationMap & intlist_reverse_relations()
Definition ax_tree.h:125
AtkStateType state
glong glong end
GAsyncResult * result
std::string StringPrintf(const std::string &format, Args... args)
bool Contains(const Container &container, const Value &value)
std::string NumberToString(int32_t number)
const char * ToString(ax::mojom::Event event)
bool HasState(ax::mojom::State state) const
std::vector< int32_t > child_ids
ax::mojom::Role role
std::string title
AXTreeID tree_id
std::vector< AXNodeData > nodes
#define EXPECT_TRUE(handle)
Definition unit_test.h:685