Flutter Engine
The Flutter Engine
ax_platform_node_base_unittest.cc
Go to the documentation of this file.
1// Copyright 2019 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
6
7#include "gtest/gtest.h"
8
9#include "base/string_utils.h"
11
12namespace ui {
13namespace {
14
15void MakeStaticText(AXNodeData* node, int id, const std::string& text) {
16 node->id = id;
18 node->SetName(text);
19}
20
21void MakeGroup(AXNodeData* node, int id, std::vector<int> child_ids) {
22 node->id = id;
23 node->role = ax::mojom::Role::kGroup;
24 node->child_ids = child_ids;
25}
26
27void SetIsInvisible(AXTree* tree, int id, bool invisible) {
29 update.nodes.resize(1);
30 update.nodes[0] = tree->GetFromId(id)->data();
31 if (invisible)
32 update.nodes[0].AddState(ax::mojom::State::kInvisible);
33 else
34 update.nodes[0].RemoveState(ax::mojom::State::kInvisible);
35 tree->Unserialize(update);
36}
37
38void SetRole(AXTree* tree, int id, ax::mojom::Role role) {
40 update.nodes.resize(1);
41 update.nodes[0] = tree->GetFromId(id)->data();
42 update.nodes[0].role = role;
43 tree->Unserialize(update);
44}
45
46} // namespace
47
48TEST(AXPlatformNodeBaseTest, GetHypertext) {
50
51 // RootWebArea #1
52 // ++++StaticText "text1" #2
53 // ++++StaticText "text2" #3
54 // ++++StaticText "text3" #4
55
56 update.root_id = 1;
57 update.nodes.resize(4);
58
59 update.nodes[0].id = 1;
60 update.nodes[0].role = ax::mojom::Role::kWebArea;
61 update.nodes[0].child_ids = {2, 3, 4};
62
63 MakeStaticText(&update.nodes[1], 2, "text1");
64 MakeStaticText(&update.nodes[2], 3, "text2");
65 MakeStaticText(&update.nodes[3], 4, "text3");
66
67 AXTree tree(update);
68
69 // Set an AXMode on the AXPlatformNode as some platforms (auralinux) use it to
70 // determine if it should enable accessibility.
72
75
76 EXPECT_EQ(root->GetHypertext(), base::UTF8ToUTF16("text1text2text3"));
77
78 AXPlatformNodeBase* text1 = static_cast<AXPlatformNodeBase*>(
80 EXPECT_EQ(text1->GetHypertext(), base::UTF8ToUTF16("text1"));
81
82 AXPlatformNodeBase* text2 = static_cast<AXPlatformNodeBase*>(
84 EXPECT_EQ(text2->GetHypertext(), base::UTF8ToUTF16("text2"));
85
86 AXPlatformNodeBase* text3 = static_cast<AXPlatformNodeBase*>(
88 EXPECT_EQ(text3->GetHypertext(), base::UTF8ToUTF16("text3"));
89}
90
91TEST(AXPlatformNodeBaseTest, GetHypertextIgnoredContainerSiblings) {
93
94 // RootWebArea #1
95 // ++genericContainer IGNORED #2
96 // ++++StaticText "text1" #3
97 // ++genericContainer IGNORED #4
98 // ++++StaticText "text2" #5
99 // ++genericContainer IGNORED #6
100 // ++++StaticText "text3" #7
101
102 update.root_id = 1;
103 update.nodes.resize(7);
104
105 update.nodes[0].id = 1;
106 update.nodes[0].role = ax::mojom::Role::kWebArea;
107 update.nodes[0].child_ids = {2, 4, 6};
108
109 update.nodes[1].id = 2;
110 update.nodes[1].child_ids = {3};
112 update.nodes[1].AddState(ax::mojom::State::kIgnored);
113 MakeStaticText(&update.nodes[2], 3, "text1");
114
115 update.nodes[3].id = 4;
116 update.nodes[3].child_ids = {5};
118 update.nodes[3].AddState(ax::mojom::State::kIgnored);
119 MakeStaticText(&update.nodes[4], 5, "text2");
120
121 update.nodes[5].id = 6;
122 update.nodes[5].child_ids = {7};
124 update.nodes[5].AddState(ax::mojom::State::kIgnored);
125 MakeStaticText(&update.nodes[6], 7, "text3");
126
127 AXTree tree(update);
128 // Set an AXMode on the AXPlatformNode as some platforms (auralinux) use it to
129 // determine if it should enable accessibility.
131
134
135 EXPECT_EQ(root->GetHypertext(), base::UTF8ToUTF16("text1text2text3"));
136
137 AXPlatformNodeBase* text1_ignored_container =
138 static_cast<AXPlatformNodeBase*>(
140 EXPECT_EQ(text1_ignored_container->GetHypertext(),
141 base::UTF8ToUTF16("text1"));
142
143 AXPlatformNodeBase* text2_ignored_container =
144 static_cast<AXPlatformNodeBase*>(
146 EXPECT_EQ(text2_ignored_container->GetHypertext(),
147 base::UTF8ToUTF16("text2"));
148
149 AXPlatformNodeBase* text3_ignored_container =
150 static_cast<AXPlatformNodeBase*>(
152 EXPECT_EQ(text3_ignored_container->GetHypertext(),
153 base::UTF8ToUTF16("text3"));
154}
155
156TEST(AXPlatformNodeBaseTest, InnerTextIgnoresInvisibleAndIgnored) {
158
159 update.root_id = 1;
160 update.nodes.resize(6);
161
162 MakeStaticText(&update.nodes[1], 2, "a");
163 MakeStaticText(&update.nodes[2], 3, "b");
164
165 MakeStaticText(&update.nodes[4], 5, "d");
166 MakeStaticText(&update.nodes[5], 6, "e");
167
168 MakeGroup(&update.nodes[3], 4, {5, 6});
169 MakeGroup(&update.nodes[0], 1, {2, 3, 4});
170
171 AXTree tree(update);
172
173 auto* root = static_cast<AXPlatformNodeBase*>(
175
176 // Set an AXMode on the AXPlatformNode as some platforms (auralinux) use it to
177 // determine if it should enable accessibility.
179
180 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("abde"));
181
182 // Setting invisible or ignored on a static text node causes it to be included
183 // or excluded from the root node's inner text:
184 {
185 SetIsInvisible(&tree, 2, true);
186 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("bde"));
187
188 SetIsInvisible(&tree, 2, false);
189 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("abde"));
190
191 SetRole(&tree, 2, ax::mojom::Role::kIgnored);
192 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("bde"));
193
194 SetRole(&tree, 2, ax::mojom::Role::kStaticText);
195 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("abde"));
196 }
197
198 // Setting invisible or ignored on a group node has no effect on the inner
199 // text:
200 {
201 SetIsInvisible(&tree, 4, true);
202 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("abde"));
203
204 SetRole(&tree, 4, ax::mojom::Role::kIgnored);
205 EXPECT_EQ(root->GetInnerText(), base::UTF8ToUTF16("abde"));
206 }
207}
208
209TEST(AXPlatformNodeBaseTest, TestSelectedChildren) {
211
212 AXNodeData root_data;
213 root_data.id = 1;
216 root_data.child_ids = {2, 3};
217
218 AXNodeData item_1_data;
219 item_1_data.id = 2;
222
223 AXNodeData item_2_data;
224 item_2_data.id = 3;
226
228 update.root_id = 1;
229 update.nodes = {root_data, item_1_data, item_2_data};
230 AXTree tree(update);
231
232 auto* root = static_cast<AXPlatformNodeBase*>(
234
235 int num = root->GetSelectionCount();
236 EXPECT_EQ(num, 1);
237
238 gfx::NativeViewAccessible first_child = root->ChildAtIndex(0);
239 AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
240 EXPECT_EQ(first_child, first_selected_node->GetNativeViewAccessible());
241 EXPECT_EQ(nullptr, root->GetSelectedItem(1));
242}
243
244TEST(AXPlatformNodeBaseTest, TestSelectedChildrenWithGroup) {
246
247 AXNodeData root_data;
248 root_data.id = 1;
252 root_data.child_ids = {2, 3};
253
254 AXNodeData group_1_data;
255 group_1_data.id = 2;
256 group_1_data.role = ax::mojom::Role::kGroup;
257 group_1_data.child_ids = {4, 5};
258
259 AXNodeData group_2_data;
260 group_2_data.id = 3;
261 group_2_data.role = ax::mojom::Role::kGroup;
262 group_2_data.child_ids = {6, 7};
263
264 AXNodeData item_1_data;
265 item_1_data.id = 4;
268
269 AXNodeData item_2_data;
270 item_2_data.id = 5;
272
273 AXNodeData item_3_data;
274 item_3_data.id = 6;
276
277 AXNodeData item_4_data;
278 item_4_data.id = 7;
281
283 update.root_id = 1;
284 update.nodes = {root_data, group_1_data, group_2_data, item_1_data,
285 item_2_data, item_3_data, item_4_data};
286 AXTree tree(update);
287
288 auto* root = static_cast<AXPlatformNodeBase*>(
290
291 int num = root->GetSelectionCount();
292 EXPECT_EQ(num, 2);
293
294 gfx::NativeViewAccessible first_group_child =
295 static_cast<AXPlatformNodeBase*>(
297 ->ChildAtIndex(0);
298 AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
299 EXPECT_EQ(first_group_child, first_selected_node->GetNativeViewAccessible());
300
301 gfx::NativeViewAccessible second_group_child =
302 static_cast<AXPlatformNodeBase*>(
304 ->ChildAtIndex(1);
305 AXPlatformNodeBase* second_selected_node = root->GetSelectedItem(1);
306 EXPECT_EQ(second_group_child,
307 second_selected_node->GetNativeViewAccessible());
308}
309
310TEST(AXPlatformNodeBaseTest, TestSelectedChildrenMixed) {
312
313 // Build the below tree which is mixed with listBoxOption and group.
314 // id=1 listBox FOCUSABLE MULTISELECTABLE (0, 0)-(0, 0) child_ids=2,3,4,9
315 // ++id=2 listBoxOption (0, 0)-(0, 0) selected=true
316 // ++id=3 group (0, 0)-(0, 0) child_ids=5,6
317 // ++++id=5 listBoxOption (0, 0)-(0, 0) selected=true
318 // ++++id=6 listBoxOption (0, 0)-(0, 0)
319 // ++id=4 group (0, 0)-(0, 0) child_ids=7,8
320 // ++++id=7 listBoxOption (0, 0)-(0, 0)
321 // ++++id=8 listBoxOption (0, 0)-(0, 0) selected=true
322 // ++id=9 listBoxOption (0, 0)-(0, 0) selected=true
323
324 AXNodeData root_data;
325 root_data.id = 1;
329 root_data.child_ids = {2, 3, 4, 9};
330
331 AXNodeData item_1_data;
332 item_1_data.id = 2;
335
336 AXNodeData group_1_data;
337 group_1_data.id = 3;
338 group_1_data.role = ax::mojom::Role::kGroup;
339 group_1_data.child_ids = {5, 6};
340
341 AXNodeData item_2_data;
342 item_2_data.id = 5;
345
346 AXNodeData item_3_data;
347 item_3_data.id = 6;
349
350 AXNodeData group_2_data;
351 group_2_data.id = 4;
352 group_2_data.role = ax::mojom::Role::kGroup;
353 group_2_data.child_ids = {7, 8};
354
355 AXNodeData item_4_data;
356 item_4_data.id = 7;
358
359 AXNodeData item_5_data;
360 item_5_data.id = 8;
363
364 AXNodeData item_6_data;
365 item_6_data.id = 9;
368
370 update.root_id = 1;
371 update.nodes = {root_data, item_1_data, group_1_data,
372 item_2_data, item_3_data, group_2_data,
373 item_4_data, item_5_data, item_6_data};
374 AXTree tree(update);
375
376 auto* root = static_cast<AXPlatformNodeBase*>(
378
379 int num = root->GetSelectionCount();
380 EXPECT_EQ(num, 4);
381
382 gfx::NativeViewAccessible first_child = root->ChildAtIndex(0);
383 AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
384 EXPECT_EQ(first_child, first_selected_node->GetNativeViewAccessible());
385
386 gfx::NativeViewAccessible first_group_child =
387 static_cast<AXPlatformNodeBase*>(
389 ->ChildAtIndex(0);
390 AXPlatformNodeBase* second_selected_node = root->GetSelectedItem(1);
391 EXPECT_EQ(first_group_child, second_selected_node->GetNativeViewAccessible());
392
393 gfx::NativeViewAccessible second_group_child =
394 static_cast<AXPlatformNodeBase*>(
396 ->ChildAtIndex(1);
397 AXPlatformNodeBase* third_selected_node = root->GetSelectedItem(2);
398 EXPECT_EQ(second_group_child, third_selected_node->GetNativeViewAccessible());
399
400 gfx::NativeViewAccessible fourth_child = root->ChildAtIndex(3);
401 AXPlatformNodeBase* fourth_selected_node = root->GetSelectedItem(3);
402 EXPECT_EQ(fourth_child, fourth_selected_node->GetNativeViewAccessible());
403}
404
405TEST(AXPlatformNodeBaseTest, CompareTo) {
406 // Compare the nodes' logical orders for the following tree. Node name is
407 // denoted according to its id (i.e. "n#" is id#). Nodes that have smaller ids
408 // are always logically less than nodes with bigger ids.
409 //
410 // n1
411 // |
412 // __ n2 ___
413 // / \ \
414 // n3 _ n8 n9
415 // / \ \ \
416 // n4 n5 n6 n10
417 // /
418 // n7
420 AXNodeData node1;
421 node1.id = 1;
423 node1.child_ids = {2};
424
425 AXNodeData node2;
426 node2.id = 2;
428 node2.child_ids = {3, 8, 9};
429
430 AXNodeData node3;
431 node3.id = 3;
433 node3.child_ids = {4, 5, 6};
434
435 AXNodeData node4;
436 node4.id = 4;
438
439 AXNodeData node5;
440 node5.id = 5;
442
443 AXNodeData node6;
444 node6.id = 6;
446 node6.child_ids = {7};
447
448 AXNodeData node7;
449 node7.id = 7;
451
452 AXNodeData node8;
453 node8.id = 8;
455
456 AXNodeData node9;
457 node9.id = 9;
459 node9.child_ids = {10};
460
461 AXNodeData node10;
462 node10.id = 10;
464
466 update.root_id = 1;
467 update.nodes = {node1, node2, node3, node4, node5,
468 node6, node7, node8, node9, node10};
469
470 AXTree tree(update);
471
472 // Retrieve the nodes in a level-order traversal way.
473 auto* n1 = static_cast<AXPlatformNodeBase*>(
475 auto* n2 = static_cast<AXPlatformNodeBase*>(
476 AXPlatformNode::FromNativeViewAccessible(n1->ChildAtIndex(0)));
477 auto* n3 = static_cast<AXPlatformNodeBase*>(
478 AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(0)));
479 auto* n8 = static_cast<AXPlatformNodeBase*>(
480 AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(1)));
481 auto* n9 = static_cast<AXPlatformNodeBase*>(
482 AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(2)));
483 auto* n4 = static_cast<AXPlatformNodeBase*>(
484 AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(0)));
485 auto* n5 = static_cast<AXPlatformNodeBase*>(
486 AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(1)));
487 auto* n6 = static_cast<AXPlatformNodeBase*>(
488 AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(2)));
489 auto* n10 = static_cast<AXPlatformNodeBase*>(
490 AXPlatformNode::FromNativeViewAccessible(n9->ChildAtIndex(0)));
491 auto* n7 = static_cast<AXPlatformNodeBase*>(
492 AXPlatformNode::FromNativeViewAccessible(n6->ChildAtIndex(0)));
493
494 // Test for two nodes that do not share the same root. They should not be
495 // comparable.
496 AXPlatformNodeBase detached_node;
497 EXPECT_EQ(std::nullopt, n1->CompareTo(detached_node));
498
499 // Create a test vector of all the tree nodes arranged in a pre-order
500 // traversal way. The node that has a smaller index in the vector should also
501 // be logically less (comes before) the nodes with bigger index.
502 std::vector<AXPlatformNodeBase*> preorder_tree_nodes = {n1, n2, n3, n4, n5,
503 n6, n7, n8, n9, n10};
504 // Test through all permutations of lhs/rhs comparisons of nodes from
505 // |preorder_tree_nodes|.
506 for (auto* lhs : preorder_tree_nodes) {
507 for (auto* rhs : preorder_tree_nodes) {
508 int expected_result = 0;
509 if (lhs->GetData().id < rhs->GetData().id)
510 expected_result = -1;
511 else if (lhs->GetData().id > rhs->GetData().id)
512 expected_result = 1;
513
514 EXPECT_NE(std::nullopt, lhs->CompareTo(*rhs));
515 int actual_result = 0;
516 if (lhs->CompareTo(*rhs) < 0)
517 actual_result = -1;
518 else if (lhs->CompareTo(*rhs) > 0)
519 actual_result = 1;
520
521 SCOPED_TRACE(testing::Message()
522 << "lhs.id=" << base::NumberToString(lhs->GetData().id)
523 << ", rhs.id=" << base::NumberToString(rhs->GetData().id)
524 << ", lhs->CompareTo(*rhs)={actual:"
525 << base::NumberToString(actual_result) << ", expected:"
526 << base::NumberToString(expected_result) << "}");
527
528 EXPECT_EQ(expected_result, actual_result);
529 }
530 }
531}
532} // namespace ui
gfx::NativeViewAccessible GetNativeViewAccessible() override
std::u16string GetHypertext() const
static AXPlatformNode * FromNativeViewAccessible(gfx::NativeViewAccessible accessible)
static void NotifyAddAXModeFlags(AXMode mode_flags)
AXNode * root() const
Definition: ax_tree.h:57
static TestAXNodeWrapper * GetOrCreate(AXTree *tree, AXNode *node)
AXPlatformNode * ax_platform_node() const
std::u16string text
std::u16string UTF8ToUTF16(std::string src)
Definition: string_utils.cc:67
std::string NumberToString(int32_t number)
Definition: string_utils.cc:91
UnimplementedNativeViewAccessible * NativeViewAccessible
string root
Definition: scale_cpu.py:20
static constexpr AXMode kAXModeComplete(AXMode::kNativeAPIs|AXMode::kWebContents|AXMode::kInlineTextBoxes|AXMode::kScreenReader|AXMode::kHTML)
AXTreeUpdateBase< AXNodeData, AXTreeData > AXTreeUpdate
TEST(AXEnumUtilTest, Event)
Definition: update.py:1
void AddState(ax::mojom::State state)
std::vector< int32_t > child_ids
Definition: ax_node_data.h:291
void AddBoolAttribute(ax::mojom::BoolAttribute attribute, bool value)
ax::mojom::Role role
Definition: ax_node_data.h:277
const uintptr_t id