Flutter iOS Embedder
accessibility_bridge_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 "accessibility_bridge.h"
6 
7 #include "gmock/gmock.h"
8 #include "gtest/gtest.h"
9 
10 #include "flutter/third_party/accessibility/ax/ax_tree_manager_map.h"
12 
13 namespace flutter {
14 namespace testing {
15 
16 using ::testing::Contains;
17 
18 FlutterSemanticsFlags kEmptyFlags = FlutterSemanticsFlags{};
19 
20 FlutterSemanticsNode2 CreateSemanticsNode(
21  int32_t id,
22  const char* label,
23  const std::vector<int32_t>* children = nullptr) {
24  return {
25  .id = id,
26  .flags__deprecated__ = static_cast<FlutterSemanticsFlag>(0),
27  .actions = static_cast<FlutterSemanticsAction>(0),
28  .text_selection_base = -1,
29  .text_selection_extent = -1,
30  .label = label,
31  .hint = "",
32  .value = "",
33  .increased_value = "",
34  .decreased_value = "",
35  .child_count = children ? children->size() : 0,
36  .children_in_traversal_order = children ? children->data() : nullptr,
37  .custom_accessibility_actions_count = 0,
38  .tooltip = "",
39  .flags2 = &kEmptyFlags,
40  };
41 }
42 
44  std::shared_ptr<TestAccessibilityBridge> bridge =
45  std::make_shared<TestAccessibilityBridge>();
46 
47  std::vector<int32_t> children{1, 2};
48  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &children);
49  FlutterSemanticsNode2 child1 = CreateSemanticsNode(1, "child 1");
50  FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
51 
52  bridge->AddFlutterSemanticsNodeUpdate(root);
53  bridge->AddFlutterSemanticsNodeUpdate(child1);
54  bridge->AddFlutterSemanticsNodeUpdate(child2);
55  bridge->CommitUpdates();
56 
57  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
58  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
59  auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
60  EXPECT_EQ(root_node->GetChildCount(), 2);
61  EXPECT_EQ(root_node->GetData().child_ids[0], 1);
62  EXPECT_EQ(root_node->GetData().child_ids[1], 2);
63  EXPECT_EQ(root_node->GetName(), "root");
64 
65  EXPECT_EQ(child1_node->GetChildCount(), 0);
66  EXPECT_EQ(child1_node->GetName(), "child 1");
67 
68  EXPECT_EQ(child2_node->GetChildCount(), 0);
69  EXPECT_EQ(child2_node->GetName(), "child 2");
70 }
71 
72 // Flutter used to assume that the accessibility root had ID 0.
73 // In a multi-view world, each view has its own accessibility root
74 // with a globally unique node ID.
75 TEST(AccessibilityBridgeTest, AccessibilityRootId) {
76  std::shared_ptr<TestAccessibilityBridge> bridge =
77  std::make_shared<TestAccessibilityBridge>();
78 
79  std::vector<int32_t> children{456, 789};
80  FlutterSemanticsNode2 root = CreateSemanticsNode(123, "root", &children);
81  FlutterSemanticsNode2 child1 = CreateSemanticsNode(456, "child 1");
82  FlutterSemanticsNode2 child2 = CreateSemanticsNode(789, "child 2");
83 
84  bridge->AddFlutterSemanticsNodeUpdate(root);
85  bridge->AddFlutterSemanticsNodeUpdate(child1);
86  bridge->AddFlutterSemanticsNodeUpdate(child2);
87  bridge->CommitUpdates();
88 
89  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(123).lock();
90  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(456).lock();
91  auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(789).lock();
92  auto fake_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
93 
94  EXPECT_EQ(bridge->GetRootAsAXNode()->id(), 123);
95  EXPECT_EQ(bridge->RootDelegate()->GetName(), "root");
96 
97  EXPECT_EQ(root_node->GetChildCount(), 2);
98  EXPECT_EQ(root_node->GetData().child_ids[0], 456);
99  EXPECT_EQ(root_node->GetData().child_ids[1], 789);
100  EXPECT_EQ(root_node->GetName(), "root");
101 
102  EXPECT_EQ(child1_node->GetChildCount(), 0);
103  EXPECT_EQ(child1_node->GetName(), "child 1");
104 
105  EXPECT_EQ(child2_node->GetChildCount(), 0);
106  EXPECT_EQ(child2_node->GetName(), "child 2");
107 
108  ASSERT_FALSE(fake_delegate);
109 }
110 
111 // Semantic nodes can be added in any order.
113  std::shared_ptr<TestAccessibilityBridge> bridge =
114  std::make_shared<TestAccessibilityBridge>();
115 
116  std::vector<int32_t> root_children{34, 56};
117  std::vector<int32_t> child2_children{78};
118  std::vector<int32_t> child3_children{90};
119  FlutterSemanticsNode2 root = CreateSemanticsNode(12, "root", &root_children);
120  FlutterSemanticsNode2 child1 = CreateSemanticsNode(34, "child 1");
121  FlutterSemanticsNode2 child2 =
122  CreateSemanticsNode(56, "child 2", &child2_children);
123  FlutterSemanticsNode2 child3 =
124  CreateSemanticsNode(78, "child 3", &child3_children);
125  FlutterSemanticsNode2 child4 = CreateSemanticsNode(90, "child 4");
126 
127  bridge->AddFlutterSemanticsNodeUpdate(child3);
128  bridge->AddFlutterSemanticsNodeUpdate(child2);
129  bridge->AddFlutterSemanticsNodeUpdate(root);
130  bridge->AddFlutterSemanticsNodeUpdate(child1);
131  bridge->AddFlutterSemanticsNodeUpdate(child4);
132  bridge->CommitUpdates();
133 
134  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(12).lock();
135  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(34).lock();
136  auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(56).lock();
137  auto child3_node = bridge->GetFlutterPlatformNodeDelegateFromID(78).lock();
138  auto child4_node = bridge->GetFlutterPlatformNodeDelegateFromID(90).lock();
139 
140  EXPECT_EQ(bridge->GetRootAsAXNode()->id(), 12);
141  EXPECT_EQ(bridge->RootDelegate()->GetName(), "root");
142 
143  EXPECT_EQ(root_node->GetChildCount(), 2);
144  EXPECT_EQ(root_node->GetData().child_ids[0], 34);
145  EXPECT_EQ(root_node->GetData().child_ids[1], 56);
146  EXPECT_EQ(root_node->GetName(), "root");
147 
148  EXPECT_EQ(child1_node->GetChildCount(), 0);
149  EXPECT_EQ(child1_node->GetName(), "child 1");
150 
151  EXPECT_EQ(child2_node->GetChildCount(), 1);
152  EXPECT_EQ(child2_node->GetData().child_ids[0], 78);
153  EXPECT_EQ(child2_node->GetName(), "child 2");
154 
155  EXPECT_EQ(child3_node->GetChildCount(), 1);
156  EXPECT_EQ(child3_node->GetData().child_ids[0], 90);
157  EXPECT_EQ(child3_node->GetName(), "child 3");
158 
159  EXPECT_EQ(child4_node->GetChildCount(), 0);
160  EXPECT_EQ(child4_node->GetName(), "child 4");
161 }
162 
163 TEST(AccessibilityBridgeTest, CanFireChildrenChangedCorrectly) {
164  std::shared_ptr<TestAccessibilityBridge> bridge =
165  std::make_shared<TestAccessibilityBridge>();
166 
167  std::vector<int32_t> children{1};
168  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &children);
169  FlutterSemanticsNode2 child1 = CreateSemanticsNode(1, "child 1");
170 
171  bridge->AddFlutterSemanticsNodeUpdate(root);
172  bridge->AddFlutterSemanticsNodeUpdate(child1);
173 
174  bridge->CommitUpdates();
175 
176  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
177  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
178  EXPECT_EQ(root_node->GetChildCount(), 1);
179  EXPECT_EQ(root_node->GetData().child_ids[0], 1);
180  EXPECT_EQ(root_node->GetName(), "root");
181 
182  EXPECT_EQ(child1_node->GetChildCount(), 0);
183  EXPECT_EQ(child1_node->GetName(), "child 1");
184  bridge->accessibility_events.clear();
185 
186  // Add a child to root.
187  root.child_count = 2;
188  int32_t new_children[] = {1, 2};
189  root.children_in_traversal_order = new_children;
190 
191  FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
192 
193  bridge->AddFlutterSemanticsNodeUpdate(root);
194  bridge->AddFlutterSemanticsNodeUpdate(child2);
195  bridge->CommitUpdates();
196 
197  root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
198 
199  EXPECT_EQ(root_node->GetChildCount(), 2);
200  EXPECT_EQ(root_node->GetData().child_ids[0], 1);
201  EXPECT_EQ(root_node->GetData().child_ids[1], 2);
202  EXPECT_EQ(bridge->accessibility_events.size(), size_t{2});
203  std::set<ui::AXEventGenerator::Event> actual_event{
204  bridge->accessibility_events.begin(), bridge->accessibility_events.end()};
205  EXPECT_THAT(actual_event,
206  Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED));
207  EXPECT_THAT(actual_event,
208  Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED));
209 }
210 
211 TEST(AccessibilityBridgeTest, CanHandleSelectionChangeCorrectly) {
212  std::shared_ptr<TestAccessibilityBridge> bridge =
213  std::make_shared<TestAccessibilityBridge>();
214  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
215  auto flags = FlutterSemanticsFlags{
216  .is_focused = FlutterTristate::kFlutterTristateTrue,
217  .is_text_field = true,
218  };
219  root.flags2 = &flags;
220 
221  bridge->AddFlutterSemanticsNodeUpdate(root);
222  bridge->CommitUpdates();
223 
224  const ui::AXTreeData& tree = bridge->GetAXTreeData();
225  EXPECT_EQ(tree.sel_anchor_object_id, ui::AXNode::kInvalidAXID);
226  bridge->accessibility_events.clear();
227 
228  // Update the selection.
229  root.text_selection_base = 0;
230  root.text_selection_extent = 5;
231  bridge->AddFlutterSemanticsNodeUpdate(root);
232 
233  bridge->CommitUpdates();
234 
235  EXPECT_EQ(tree.sel_anchor_object_id, 0);
236  EXPECT_EQ(tree.sel_anchor_offset, 0);
237  EXPECT_EQ(tree.sel_focus_object_id, 0);
238  EXPECT_EQ(tree.sel_focus_offset, 5);
239  ASSERT_EQ(bridge->accessibility_events.size(), size_t{2});
240  EXPECT_EQ(bridge->accessibility_events[0],
241  ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED);
242  EXPECT_EQ(bridge->accessibility_events[1],
243  ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED);
244 }
245 
246 TEST(AccessibilityBridgeTest, DoesNotAssignEditableRootToSelectableText) {
247  std::shared_ptr<TestAccessibilityBridge> bridge =
248  std::make_shared<TestAccessibilityBridge>();
249  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
250  auto flags = FlutterSemanticsFlags{
251  .is_text_field = true,
252  .is_read_only = true,
253  };
254  root.flags2 = &flags;
255 
256  bridge->AddFlutterSemanticsNodeUpdate(root);
257  bridge->CommitUpdates();
258 
259  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
260 
261  EXPECT_FALSE(root_node->GetData().GetBoolAttribute(
262  ax::mojom::BoolAttribute::kEditableRoot));
263 }
264 
265 TEST(AccessibilityBridgeTest, SwitchHasSwitchRole) {
266  std::shared_ptr<TestAccessibilityBridge> bridge =
267  std::make_shared<TestAccessibilityBridge>();
268  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
269  auto flags = FlutterSemanticsFlags{
270  .is_enabled = FlutterTristate::kFlutterTristateTrue,
271  .is_toggled = FlutterTristate::kFlutterTristateFalse,
272  };
273 
274  root.flags2 = &flags;
275  bridge->AddFlutterSemanticsNodeUpdate(root);
276  bridge->CommitUpdates();
277 
278  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
279  EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSwitch);
280 }
281 
282 TEST(AccessibilityBridgeTest, SliderHasSliderRole) {
283  std::shared_ptr<TestAccessibilityBridge> bridge =
284  std::make_shared<TestAccessibilityBridge>();
285  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
286  auto flags = FlutterSemanticsFlags{
287  .is_enabled = FlutterTristate::kFlutterTristateTrue,
288  .is_focused = FlutterTristate::kFlutterTristateFalse,
289  .is_slider = true,
290  };
291 
292  root.flags2 = &flags;
293 
294  bridge->AddFlutterSemanticsNodeUpdate(root);
295  bridge->CommitUpdates();
296 
297  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
298  EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSlider);
299 }
300 
301 // Ensure that checkboxes have their checked status set apropriately
302 // Previously, only Radios could have this flag updated
303 // Resulted in the issue seen at
304 // https://github.com/flutter/flutter/issues/96218
305 // As this fix involved code run on all platforms, it is included here.
306 TEST(AccessibilityBridgeTest, CanSetCheckboxChecked) {
307  std::shared_ptr<TestAccessibilityBridge> bridge =
308  std::make_shared<TestAccessibilityBridge>();
309  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
310  auto flags = FlutterSemanticsFlags{
311  .is_checked = FlutterCheckState::kFlutterCheckStateTrue,
312  };
313 
314  root.flags2 = &flags;
315  bridge->AddFlutterSemanticsNodeUpdate(root);
316  bridge->CommitUpdates();
317 
318  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
319  EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
320  EXPECT_EQ(root_node->GetData().GetCheckedState(),
321  ax::mojom::CheckedState::kTrue);
322 }
323 
324 // Verify that a node can be moved from one parent to another.
325 TEST(AccessibilityBridgeTest, CanReparentNode) {
326  std::shared_ptr<TestAccessibilityBridge> bridge =
327  std::make_shared<TestAccessibilityBridge>();
328 
329  std::vector<int32_t> root_children{1};
330  std::vector<int32_t> child1_children{2};
331  FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &root_children);
332  FlutterSemanticsNode2 child1 =
333  CreateSemanticsNode(1, "child 1", &child1_children);
334  FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
335 
336  bridge->AddFlutterSemanticsNodeUpdate(root);
337  bridge->AddFlutterSemanticsNodeUpdate(child1);
338  bridge->AddFlutterSemanticsNodeUpdate(child2);
339  bridge->CommitUpdates();
340  bridge->accessibility_events.clear();
341 
342  // Reparent child2 from child1 to the root.
343  child1.child_count = 0;
344  child1.children_in_traversal_order = nullptr;
345 
346  int32_t new_root_children[] = {1, 2};
347  root.child_count = 2;
348  root.children_in_traversal_order = new_root_children;
349 
350  bridge->AddFlutterSemanticsNodeUpdate(root);
351  bridge->AddFlutterSemanticsNodeUpdate(child1);
352  bridge->AddFlutterSemanticsNodeUpdate(child2);
353  bridge->CommitUpdates();
354 
355  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
356  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
357  auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
358 
359  EXPECT_EQ(root_node->GetChildCount(), 2);
360  EXPECT_EQ(root_node->GetData().child_ids[0], 1);
361  EXPECT_EQ(root_node->GetData().child_ids[1], 2);
362  EXPECT_EQ(root_node->GetName(), "root");
363 
364  EXPECT_EQ(child1_node->GetChildCount(), 0);
365  EXPECT_EQ(child1_node->GetName(), "child 1");
366 
367  EXPECT_EQ(child2_node->GetChildCount(), 0);
368  EXPECT_EQ(child2_node->GetName(), "child 2");
369 
370  ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
371 
372  // Child2 is moved from child1 to root.
373  EXPECT_THAT(bridge->accessibility_events,
374  Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
375  EXPECT_THAT(bridge->accessibility_events,
376  Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1));
377 
378  // Child1 is no longer a parent. It loses its group role and disables its
379  // 'clip children' attribute.
380  EXPECT_THAT(
381  bridge->accessibility_events,
382  Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1));
383  EXPECT_THAT(bridge->accessibility_events,
384  Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1));
385 }
386 
387 // Verify that multiple nodes can be moved to new parents.
388 TEST(AccessibilityBridgeTest, CanReparentMultipleNodes) {
389  std::shared_ptr<TestAccessibilityBridge> bridge =
390  std::make_shared<TestAccessibilityBridge>();
391 
392  int32_t root_id = 0;
393  int32_t intermediary1_id = 1;
394  int32_t intermediary2_id = 2;
395  int32_t leaf1_id = 3;
396  int32_t leaf2_id = 4;
397  int32_t leaf3_id = 5;
398 
399  std::vector<int32_t> root_children{intermediary1_id, intermediary2_id};
400  std::vector<int32_t> intermediary1_children{leaf1_id};
401  std::vector<int32_t> intermediary2_children{leaf2_id, leaf3_id};
402  FlutterSemanticsNode2 root =
403  CreateSemanticsNode(root_id, "root", &root_children);
404  FlutterSemanticsNode2 intermediary1 = CreateSemanticsNode(
405  intermediary1_id, "intermediary 1", &intermediary1_children);
406  FlutterSemanticsNode2 intermediary2 = CreateSemanticsNode(
407  intermediary2_id, "intermediary 2", &intermediary2_children);
408  FlutterSemanticsNode2 leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1");
409  FlutterSemanticsNode2 leaf2 = CreateSemanticsNode(leaf2_id, "leaf 2");
410  FlutterSemanticsNode2 leaf3 = CreateSemanticsNode(leaf3_id, "leaf 3");
411 
412  bridge->AddFlutterSemanticsNodeUpdate(root);
413  bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
414  bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
415  bridge->AddFlutterSemanticsNodeUpdate(leaf1);
416  bridge->AddFlutterSemanticsNodeUpdate(leaf2);
417  bridge->AddFlutterSemanticsNodeUpdate(leaf3);
418  bridge->CommitUpdates();
419  bridge->accessibility_events.clear();
420 
421  // Swap intermediary 1's and intermediary2's children.
422  int32_t new_intermediary1_children[] = {leaf2_id, leaf3_id};
423  intermediary1.child_count = 2;
424  intermediary1.children_in_traversal_order = new_intermediary1_children;
425 
426  int32_t new_intermediary2_children[] = {leaf1_id};
427  intermediary2.child_count = 1;
428  intermediary2.children_in_traversal_order = new_intermediary2_children;
429 
430  bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
431  bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
432  bridge->AddFlutterSemanticsNodeUpdate(leaf1);
433  bridge->AddFlutterSemanticsNodeUpdate(leaf2);
434  bridge->AddFlutterSemanticsNodeUpdate(leaf3);
435  bridge->CommitUpdates();
436 
437  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
438  auto intermediary1_node =
439  bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock();
440  auto intermediary2_node =
441  bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock();
442  auto leaf1_node =
443  bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock();
444  auto leaf2_node =
445  bridge->GetFlutterPlatformNodeDelegateFromID(leaf2_id).lock();
446  auto leaf3_node =
447  bridge->GetFlutterPlatformNodeDelegateFromID(leaf3_id).lock();
448 
449  EXPECT_EQ(root_node->GetChildCount(), 2);
450  EXPECT_EQ(root_node->GetData().child_ids[0], intermediary1_id);
451  EXPECT_EQ(root_node->GetData().child_ids[1], intermediary2_id);
452  EXPECT_EQ(root_node->GetName(), "root");
453 
454  EXPECT_EQ(intermediary1_node->GetChildCount(), 2);
455  EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf2_id);
456  EXPECT_EQ(intermediary1_node->GetData().child_ids[1], leaf3_id);
457  EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1");
458 
459  EXPECT_EQ(intermediary2_node->GetChildCount(), 1);
460  EXPECT_EQ(intermediary2_node->GetData().child_ids[0], leaf1_id);
461  EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2");
462 
463  EXPECT_EQ(leaf1_node->GetChildCount(), 0);
464  EXPECT_EQ(leaf1_node->GetName(), "leaf 1");
465 
466  EXPECT_EQ(leaf2_node->GetChildCount(), 0);
467  EXPECT_EQ(leaf2_node->GetName(), "leaf 2");
468 
469  EXPECT_EQ(leaf3_node->GetChildCount(), 0);
470  EXPECT_EQ(leaf3_node->GetName(), "leaf 3");
471 
472  // Intermediary 1 and intermediary 2 have new children.
473  // Leaf 1, 2, and 3 are all moved.
474  ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
475  EXPECT_THAT(bridge->accessibility_events,
476  Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
477  EXPECT_THAT(bridge->accessibility_events,
478  Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(3));
479 }
480 
481 // Verify that a node with a child can be moved from one parent to another.
482 TEST(AccessibilityBridgeTest, CanReparentNodeWithChild) {
483  std::shared_ptr<TestAccessibilityBridge> bridge =
484  std::make_shared<TestAccessibilityBridge>();
485 
486  int32_t root_id = 0;
487  int32_t intermediary1_id = 1;
488  int32_t intermediary2_id = 2;
489  int32_t leaf1_id = 3;
490 
491  std::vector<int32_t> root_children{intermediary1_id, intermediary2_id};
492  std::vector<int32_t> intermediary1_children{leaf1_id};
493  FlutterSemanticsNode2 root =
494  CreateSemanticsNode(root_id, "root", &root_children);
495  FlutterSemanticsNode2 intermediary1 = CreateSemanticsNode(
496  intermediary1_id, "intermediary 1", &intermediary1_children);
497  FlutterSemanticsNode2 intermediary2 =
498  CreateSemanticsNode(intermediary2_id, "intermediary 2");
499  FlutterSemanticsNode2 leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1");
500 
501  bridge->AddFlutterSemanticsNodeUpdate(root);
502  bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
503  bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
504  bridge->AddFlutterSemanticsNodeUpdate(leaf1);
505  bridge->CommitUpdates();
506  bridge->accessibility_events.clear();
507 
508  // Move intermediary1 from root to intermediary 2.
509  int32_t new_root_children[] = {intermediary2_id};
510  root.child_count = 1;
511  root.children_in_traversal_order = new_root_children;
512 
513  int32_t new_intermediary2_children[] = {intermediary1_id};
514  intermediary2.child_count = 1;
515  intermediary2.children_in_traversal_order = new_intermediary2_children;
516 
517  bridge->AddFlutterSemanticsNodeUpdate(root);
518  bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
519  bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
520  bridge->AddFlutterSemanticsNodeUpdate(leaf1);
521  bridge->CommitUpdates();
522 
523  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
524  auto intermediary1_node =
525  bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock();
526  auto intermediary2_node =
527  bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock();
528  auto leaf1_node =
529  bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock();
530 
531  EXPECT_EQ(root_node->GetChildCount(), 1);
532  EXPECT_EQ(root_node->GetData().child_ids[0], intermediary2_id);
533  EXPECT_EQ(root_node->GetName(), "root");
534 
535  EXPECT_EQ(intermediary2_node->GetChildCount(), 1);
536  EXPECT_EQ(intermediary2_node->GetData().child_ids[0], intermediary1_id);
537  EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2");
538 
539  EXPECT_EQ(intermediary1_node->GetChildCount(), 1);
540  EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf1_id);
541  EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1");
542 
543  EXPECT_EQ(leaf1_node->GetChildCount(), 0);
544  EXPECT_EQ(leaf1_node->GetName(), "leaf 1");
545 
546  ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
547 
548  EXPECT_THAT(bridge->accessibility_events,
549  Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
550  EXPECT_THAT(bridge->accessibility_events,
551  Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1));
552 
553  // Intermediary 2 becomes a parent node. It updates to group role and enables
554  // its 'clip children' attribute.
555  EXPECT_THAT(
556  bridge->accessibility_events,
557  Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1));
558  EXPECT_THAT(bridge->accessibility_events,
559  Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1));
560 }
561 
562 TEST(AccessibilityBridgeTest, AXTreeManagerTest) {
563  std::shared_ptr<TestAccessibilityBridge> bridge =
564  std::make_shared<TestAccessibilityBridge>();
565 
566  ui::AXTreeID tree_id = bridge->GetTreeID();
567  ui::AXTreeManager* manager =
568  ui::AXTreeManagerMap::GetInstance().GetManager(tree_id);
569  ASSERT_EQ(manager, static_cast<ui::AXTreeManager*>(bridge.get()));
570 }
571 
572 TEST(AccessibilityBridgeTest, LineBreakingObjectTest) {
573  std::shared_ptr<TestAccessibilityBridge> bridge =
574  std::make_shared<TestAccessibilityBridge>();
575 
576  const int32_t root_id = 0;
577 
578  FlutterSemanticsNode2 root = CreateSemanticsNode(root_id, "root", {});
579 
580  bridge->AddFlutterSemanticsNodeUpdate(root);
581  bridge->CommitUpdates();
582 
583  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
584  auto root_data = root_node->GetData();
585  EXPECT_TRUE(root_data.HasBoolAttribute(
586  ax::mojom::BoolAttribute::kIsLineBreakingObject));
587  EXPECT_TRUE(root_data.GetBoolAttribute(
588  ax::mojom::BoolAttribute::kIsLineBreakingObject));
589 }
590 
591 } // namespace testing
592 } // namespace flutter
flutter::FakeDelegate fake_delegate
TEST(AccessibilityBridgeTest, BasicTest)
FlutterSemanticsNode2 CreateSemanticsNode(int32_t id, const char *label, const std::vector< int32_t > *children=nullptr)
FlutterSemanticsFlags kEmptyFlags