Flutter Linux Embedder
flutter_platform_node_delegate_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 
6 
7 #include "flutter/third_party/accessibility/ax/ax_action_data.h"
8 #include "gtest/gtest.h"
9 
11 
12 namespace flutter {
13 namespace testing {
14 
15 TEST(FlutterPlatformNodeDelegateTest, NodeDelegateHasUniqueId) {
16  std::shared_ptr<TestAccessibilityBridge> bridge =
17  std::make_shared<TestAccessibilityBridge>();
18 
19  // Add node 0: root.
20  FlutterSemanticsNode2 node0{sizeof(FlutterSemanticsNode2), 0};
21  std::vector<int32_t> node0_children{1};
22  FlutterSemanticsFlags emptyFlags = FlutterSemanticsFlags{};
23  node0.child_count = node0_children.size();
24  node0.children_in_traversal_order = node0_children.data();
25  node0.children_in_hit_test_order = node0_children.data();
26  node0.flags2 = &emptyFlags;
27 
28  // Add node 1: text child of node 0.
29  FlutterSemanticsNode2 node1{sizeof(FlutterSemanticsNode2), 1};
30  node1.label = "prefecture";
31  node1.value = "Kyoto";
32  node1.flags2 = &emptyFlags;
33 
34  bridge->AddFlutterSemanticsNodeUpdate(node0);
35  bridge->AddFlutterSemanticsNodeUpdate(node1);
36  bridge->CommitUpdates();
37 
38  auto node0_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
39  auto node1_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
40  EXPECT_TRUE(node0_delegate->GetUniqueId() != node1_delegate->GetUniqueId());
41 }
42 
43 TEST(FlutterPlatformNodeDelegateTest, canPerfomActions) {
44  std::shared_ptr<TestAccessibilityBridge> bridge =
45  std::make_shared<TestAccessibilityBridge>();
46  FlutterSemanticsNode2 root;
47  root.id = 0;
48  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
49  flags.is_text_field = true;
50  root.flags2 = &flags;
51  // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
52  root.actions = static_cast<FlutterSemanticsAction>(0);
53  root.text_selection_base = -1;
54  root.text_selection_extent = -1;
55  root.label = "root";
56  root.hint = "";
57  root.value = "";
58  root.increased_value = "";
59  root.decreased_value = "";
60  root.tooltip = "";
61  root.child_count = 0;
62  root.custom_accessibility_actions_count = 0;
63  bridge->AddFlutterSemanticsNodeUpdate(root);
64 
65  bridge->CommitUpdates();
66 
67  auto accessibility = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
68  // Performs an AXAction.
69  ui::AXActionData action_data;
70  action_data.action = ax::mojom::Action::kDoDefault;
71  accessibility->AccessibilityPerformAction(action_data);
72  EXPECT_EQ(bridge->performed_actions.size(), size_t{1});
73  EXPECT_EQ(bridge->performed_actions[0],
74  FlutterSemanticsAction::kFlutterSemanticsActionTap);
75 
76  action_data.action = ax::mojom::Action::kFocus;
77  accessibility->AccessibilityPerformAction(action_data);
78  EXPECT_EQ(bridge->performed_actions.size(), size_t{2});
79  EXPECT_EQ(
80  bridge->performed_actions[1],
81  FlutterSemanticsAction::kFlutterSemanticsActionDidGainAccessibilityFocus);
82 
83  action_data.action = ax::mojom::Action::kScrollToMakeVisible;
84  accessibility->AccessibilityPerformAction(action_data);
85  EXPECT_EQ(bridge->performed_actions.size(), size_t{3});
86  EXPECT_EQ(bridge->performed_actions[2],
87  FlutterSemanticsAction::kFlutterSemanticsActionShowOnScreen);
88 }
89 
90 TEST(FlutterPlatformNodeDelegateTest, canGetAXNode) {
91  // Set up a flutter accessibility node.
92  std::shared_ptr<TestAccessibilityBridge> bridge =
93  std::make_shared<TestAccessibilityBridge>();
94  FlutterSemanticsNode2 root;
95  root.id = 0;
96  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
97  flags.is_text_field = true;
98  root.flags2 = &flags;
99  // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
100  root.actions = static_cast<FlutterSemanticsAction>(0);
101  root.text_selection_base = -1;
102  root.text_selection_extent = -1;
103  root.label = "root";
104  root.hint = "";
105  root.value = "";
106  root.increased_value = "";
107  root.decreased_value = "";
108  root.tooltip = "";
109  root.child_count = 0;
110  root.custom_accessibility_actions_count = 0;
111  bridge->AddFlutterSemanticsNodeUpdate(root);
112 
113  bridge->CommitUpdates();
114 
115  auto accessibility = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
116  EXPECT_EQ(accessibility->GetData().id, 0);
117 }
118 
119 TEST(FlutterPlatformNodeDelegateTest, canCalculateBoundsCorrectly) {
120  std::shared_ptr<TestAccessibilityBridge> bridge =
121  std::make_shared<TestAccessibilityBridge>();
122  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
123  FlutterSemanticsNode2 root;
124  root.id = 0;
125  root.label = "root";
126  root.hint = "";
127  root.value = "";
128  root.increased_value = "";
129  root.decreased_value = "";
130  root.tooltip = "";
131  root.child_count = 1;
132  root.flags2 = &flags;
133  int32_t children[] = {1};
134  root.children_in_traversal_order = children;
135  root.custom_accessibility_actions_count = 0;
136  root.rect = {0, 0, 100, 100}; // LTRB
137  root.transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
138  bridge->AddFlutterSemanticsNodeUpdate(root);
139 
140  FlutterSemanticsNode2 child1;
141  child1.id = 1;
142  child1.label = "child 1";
143  child1.hint = "";
144  child1.value = "";
145  child1.increased_value = "";
146  child1.decreased_value = "";
147  child1.tooltip = "";
148  child1.child_count = 0;
149  child1.flags2 = &flags;
150  child1.custom_accessibility_actions_count = 0;
151  child1.rect = {0, 0, 50, 50}; // LTRB
152  child1.transform = {0.5, 0, 0, 0, 0.5, 0, 0, 0, 1};
153  bridge->AddFlutterSemanticsNodeUpdate(child1);
154 
155  bridge->CommitUpdates();
156  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
157  ui::AXOffscreenResult result;
158  gfx::Rect bounds =
159  child1_node->GetBoundsRect(ui::AXCoordinateSystem::kScreenDIPs,
160  ui::AXClippingBehavior::kClipped, &result);
161  EXPECT_EQ(bounds.x(), 0);
162  EXPECT_EQ(bounds.y(), 0);
163  EXPECT_EQ(bounds.width(), 25);
164  EXPECT_EQ(bounds.height(), 25);
165  EXPECT_EQ(result, ui::AXOffscreenResult::kOnscreen);
166 }
167 
168 TEST(FlutterPlatformNodeDelegateTest, canCalculateOffScreenBoundsCorrectly) {
169  std::shared_ptr<TestAccessibilityBridge> bridge =
170  std::make_shared<TestAccessibilityBridge>();
171  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
172  FlutterSemanticsNode2 root;
173  root.id = 0;
174  root.label = "root";
175  root.hint = "";
176  root.value = "";
177  root.increased_value = "";
178  root.decreased_value = "";
179  root.tooltip = "";
180  root.child_count = 1;
181  root.flags2 = &flags;
182  int32_t children[] = {1};
183  root.children_in_traversal_order = children;
184  root.custom_accessibility_actions_count = 0;
185  root.rect = {0, 0, 100, 100}; // LTRB
186  root.transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
187  bridge->AddFlutterSemanticsNodeUpdate(root);
188 
189  FlutterSemanticsNode2 child1;
190  child1.id = 1;
191  child1.label = "child 1";
192  child1.hint = "";
193  child1.value = "";
194  child1.increased_value = "";
195  child1.decreased_value = "";
196  child1.tooltip = "";
197  child1.child_count = 0;
198  child1.flags2 = &flags;
199  child1.custom_accessibility_actions_count = 0;
200  child1.rect = {90, 90, 100, 100}; // LTRB
201  child1.transform = {2, 0, 0, 0, 2, 0, 0, 0, 1};
202  bridge->AddFlutterSemanticsNodeUpdate(child1);
203 
204  bridge->CommitUpdates();
205  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
206  ui::AXOffscreenResult result;
207  gfx::Rect bounds =
208  child1_node->GetBoundsRect(ui::AXCoordinateSystem::kScreenDIPs,
209  ui::AXClippingBehavior::kUnclipped, &result);
210  EXPECT_EQ(bounds.x(), 180);
211  EXPECT_EQ(bounds.y(), 180);
212  EXPECT_EQ(bounds.width(), 20);
213  EXPECT_EQ(bounds.height(), 20);
214  EXPECT_EQ(result, ui::AXOffscreenResult::kOffscreen);
215 }
216 
217 TEST(FlutterPlatformNodeDelegateTest, canUseOwnerBridge) {
218  std::shared_ptr<TestAccessibilityBridge> bridge =
219  std::make_shared<TestAccessibilityBridge>();
220  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
221  FlutterSemanticsNode2 root{sizeof(FlutterSemanticsNode2), 0};
222  root.label = "root";
223  root.hint = "";
224  root.value = "";
225  root.increased_value = "";
226  root.decreased_value = "";
227  root.tooltip = "";
228  root.heading_level = 0;
229  root.child_count = 1;
230  root.flags2 = &flags;
231  int32_t children[] = {1};
232  root.children_in_traversal_order = children;
233  root.custom_accessibility_actions_count = 0;
234  root.rect = {0, 0, 100, 100}; // LTRB
235  root.transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
236  bridge->AddFlutterSemanticsNodeUpdate(root);
237 
238  FlutterSemanticsNode2 child1{sizeof(FlutterSemanticsNode2), 1};
239  child1.label = "child 1";
240  child1.hint = "";
241  child1.value = "";
242  child1.increased_value = "";
243  child1.decreased_value = "";
244  child1.tooltip = "";
245  child1.heading_level = 0;
246  child1.child_count = 0;
247  child1.flags2 = &flags;
248  child1.custom_accessibility_actions_count = 0;
249  child1.rect = {0, 0, 50, 50}; // LTRB
250  child1.transform = {0.5, 0, 0, 0, 0.5, 0, 0, 0, 1};
251  bridge->AddFlutterSemanticsNodeUpdate(child1);
252 
253  bridge->CommitUpdates();
254  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
255  auto owner_bridge = child1_node->GetOwnerBridge().lock();
256 
257  bool result;
258  gfx::RectF bounds = owner_bridge->RelativeToGlobalBounds(
259  child1_node->GetAXNode(), result, true);
260  EXPECT_EQ(bounds.x(), 0);
261  EXPECT_EQ(bounds.y(), 0);
262  EXPECT_EQ(bounds.width(), 25);
263  EXPECT_EQ(bounds.height(), 25);
264  EXPECT_EQ(result, false);
265 }
266 
267 TEST(FlutterPlatformNodeDelegateTest, selfIsLowestPlatformAncestor) {
268  std::shared_ptr<TestAccessibilityBridge> bridge =
269  std::make_shared<TestAccessibilityBridge>();
270  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
271  FlutterSemanticsNode2 root;
272  root.id = 0;
273  root.label = "root";
274  root.hint = "";
275  root.value = "";
276  root.increased_value = "";
277  root.decreased_value = "";
278  root.tooltip = "";
279  root.child_count = 0;
280  root.flags2 = &flags;
281  root.children_in_traversal_order = nullptr;
282  root.custom_accessibility_actions_count = 0;
283  bridge->AddFlutterSemanticsNodeUpdate(root);
284 
285  bridge->CommitUpdates();
286  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
287  auto lowest_platform_ancestor = root_node->GetLowestPlatformAncestor();
288  EXPECT_EQ(root_node->GetNativeViewAccessible(), lowest_platform_ancestor);
289 }
290 
291 TEST(FlutterPlatformNodeDelegateTest, canGetFromNodeID) {
292  std::shared_ptr<TestAccessibilityBridge> bridge =
293  std::make_shared<TestAccessibilityBridge>();
294  FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
295  FlutterSemanticsNode2 root;
296  root.id = 0;
297  root.label = "root";
298  root.hint = "";
299  root.value = "";
300  root.increased_value = "";
301  root.decreased_value = "";
302  root.tooltip = "";
303  root.child_count = 1;
304  root.flags2 = &flags;
305  int32_t children[] = {1};
306  root.children_in_traversal_order = children;
307  root.custom_accessibility_actions_count = 0;
308  bridge->AddFlutterSemanticsNodeUpdate(root);
309 
310  FlutterSemanticsNode2 child1;
311  child1.id = 1;
312  child1.label = "child 1";
313  child1.hint = "";
314  child1.value = "";
315  child1.increased_value = "";
316  child1.decreased_value = "";
317  child1.tooltip = "";
318  child1.child_count = 0;
319  child1.flags2 = &flags;
320  child1.custom_accessibility_actions_count = 0;
321  bridge->AddFlutterSemanticsNodeUpdate(child1);
322 
323  bridge->CommitUpdates();
324  auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
325  auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
326  auto node_by_id = root_node->GetFromNodeID(1);
327  EXPECT_EQ(child1_node->GetPlatformNode(), node_by_id);
328 }
329 
330 } // namespace testing
331 } // namespace flutter
TEST(AccessibilityBridgeTest, BasicTest)