Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_accessible_node_test.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// Included first as it collides with the X11 headers.
7#include "gtest/gtest.h"
8
11
13 protected:
14 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
15};
16
17// Checks can build a tree of nodes.
19 int64_t view_id = 123;
20 g_autoptr(FlAccessibleNode) root = fl_accessible_node_new(engine, view_id, 0);
21 g_autoptr(FlAccessibleNode) child1 =
23 fl_accessible_node_set_parent(child1, ATK_OBJECT(root), 0);
24 g_autoptr(FlAccessibleNode) child2 =
26 fl_accessible_node_set_parent(child2, ATK_OBJECT(root), 1);
27 g_autoptr(GPtrArray) children =
28 g_ptr_array_new_with_free_func(g_object_unref);
29 g_ptr_array_add(children, g_object_ref(child1));
30 g_ptr_array_add(children, g_object_ref(child2));
31 fl_accessible_node_set_children(root, children);
32
33 EXPECT_EQ(atk_object_get_n_accessible_children(ATK_OBJECT(root)), 2);
34 EXPECT_EQ(atk_object_get_index_in_parent(ATK_OBJECT(root)), 0);
35 g_autoptr(AtkObject) c1 =
36 atk_object_ref_accessible_child(ATK_OBJECT(root), 0);
37 EXPECT_EQ(ATK_OBJECT(child1), c1);
38 g_autoptr(AtkObject) c2 =
39 atk_object_ref_accessible_child(ATK_OBJECT(root), 1);
40 EXPECT_EQ(ATK_OBJECT(child2), c2);
41 EXPECT_EQ(atk_object_get_parent(ATK_OBJECT(root)), nullptr);
42
43 EXPECT_EQ(atk_object_get_parent(ATK_OBJECT(child1)), ATK_OBJECT(root));
44 EXPECT_EQ(atk_object_get_index_in_parent(ATK_OBJECT(child1)), 0);
45 EXPECT_EQ(atk_object_get_n_accessible_children(ATK_OBJECT(child1)), 0);
46
47 EXPECT_EQ(atk_object_get_parent(ATK_OBJECT(child2)), ATK_OBJECT(root));
48 EXPECT_EQ(atk_object_get_index_in_parent(ATK_OBJECT(child2)), 1);
49 EXPECT_EQ(atk_object_get_n_accessible_children(ATK_OBJECT(child2)), 0);
50}
51
52// Checks node name is exposed to ATK.
54 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
55 fl_accessible_node_set_name(node, "test");
56 EXPECT_STREQ(atk_object_get_name(ATK_OBJECT(node)), "test");
57}
58
59// Checks node extents are exposed to ATK.
61 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
62 fl_accessible_node_set_extents(node, 1, 2, 3, 4);
63 gint x, y, width, height;
64 atk_component_get_extents(ATK_COMPONENT(node), &x, &y, &width, &height,
65 ATK_XY_PARENT);
66 EXPECT_EQ(x, 1);
67 EXPECT_EQ(y, 2);
68 EXPECT_EQ(width, 3);
69 EXPECT_EQ(height, 4);
70}
71
72// Checks the Flutter focused flag are mapped to ATK flags.
74 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
75
76 FlutterSemanticsFlags flags1 = {};
78 fl_accessible_node_set_flags(node, &flags1);
79 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
80 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_FOCUSABLE));
81 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_FOCUSED));
82 g_object_unref(state1);
83
84 FlutterSemanticsFlags flags2 = {};
86 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
87 ::testing::_, ATK_STATE_FOCUSABLE, TRUE));
88 fl_accessible_node_set_flags(node, &flags2);
89 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
90 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_FOCUSABLE));
91 EXPECT_FALSE(atk_state_set_contains_state(state2, ATK_STATE_FOCUSED));
92 g_object_unref(state2);
93
94 FlutterSemanticsFlags flags3 = {};
96 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
97 ::testing::_, ATK_STATE_FOCUSED, TRUE));
98 fl_accessible_node_set_flags(node, &flags3);
99 AtkStateSet* state3 = atk_object_ref_state_set(ATK_OBJECT(node));
100 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_FOCUSABLE));
101 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_FOCUSED));
102 g_object_unref(state3);
103}
104
105// Checks the Flutter checked and toggled flags are mapped to ATK flags.
106TEST_F(FlAccessibleNodeTest, CheckedAndToggledFlags) {
107 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
108
109 FlutterSemanticsFlags flags1 = {};
112 fl_accessible_node_set_flags(node, &flags1);
113 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
114 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_CHECKABLE));
115 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_CHECKED));
116 g_object_unref(state1);
117
118 FlutterSemanticsFlags flags2 = {};
121 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
122 ::testing::_, ATK_STATE_CHECKABLE, TRUE));
123 fl_accessible_node_set_flags(node, &flags2);
124 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
125 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_CHECKABLE));
126 EXPECT_FALSE(atk_state_set_contains_state(state2, ATK_STATE_CHECKED));
127 g_object_unref(state2);
128
129 FlutterSemanticsFlags flags3 = {};
132 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
133 ::testing::_, ATK_STATE_CHECKED, TRUE));
134 fl_accessible_node_set_flags(node, &flags3);
135 AtkStateSet* state3 = atk_object_ref_state_set(ATK_OBJECT(node));
136 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_CHECKABLE));
137 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_CHECKED));
138 g_object_unref(state3);
139
140 FlutterSemanticsFlags flags4 = {};
143 fl_accessible_node_set_flags(node, &flags4);
144 AtkStateSet* state4 = atk_object_ref_state_set(ATK_OBJECT(node));
145 EXPECT_TRUE(atk_state_set_contains_state(state4, ATK_STATE_CHECKABLE));
146 EXPECT_TRUE(atk_state_set_contains_state(state4, ATK_STATE_CHECKED));
147 g_object_unref(state4);
148
149 FlutterSemanticsFlags flags5 = {};
152 fl_accessible_node_set_flags(node, &flags5);
153 AtkStateSet* state5 = atk_object_ref_state_set(ATK_OBJECT(node));
154 EXPECT_TRUE(atk_state_set_contains_state(state5, ATK_STATE_CHECKABLE));
155 EXPECT_TRUE(atk_state_set_contains_state(state5, ATK_STATE_CHECKED));
156 g_object_unref(state5);
157}
158
159// Checks the Flutter selected flag is mapped to ATK flags.
161 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
162
163 FlutterSemanticsFlags flags1 = {};
165 fl_accessible_node_set_flags(node, &flags1);
166 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
167 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_SELECTED));
168 g_object_unref(state1);
169
170 FlutterSemanticsFlags flags2 = {};
172 fl_accessible_node_set_flags(node, &flags2);
173 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
174 EXPECT_FALSE(atk_state_set_contains_state(state2, ATK_STATE_SELECTED));
175 g_object_unref(state2);
176
177 FlutterSemanticsFlags flags3 = {};
179 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
180 ::testing::_, ATK_STATE_SELECTED, TRUE));
181 fl_accessible_node_set_flags(node, &flags3);
182 AtkStateSet* state3 = atk_object_ref_state_set(ATK_OBJECT(node));
183 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_SELECTED));
184 g_object_unref(state3);
185}
186
187// Checks the Flutter enabled flag is mapped to ATK flags.
189 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
190
191 FlutterSemanticsFlags flags1 = {};
193 fl_accessible_node_set_flags(node, &flags1);
194 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
195 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_SENSITIVE));
196 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_ENABLED));
197 g_object_unref(state1);
198
199 FlutterSemanticsFlags flags2 = {};
201 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
202 ::testing::_, ATK_STATE_SENSITIVE, TRUE));
203 fl_accessible_node_set_flags(node, &flags2);
204 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
205 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_SENSITIVE));
206 EXPECT_FALSE(atk_state_set_contains_state(state2, ATK_STATE_ENABLED));
207 g_object_unref(state2);
208
209 FlutterSemanticsFlags flags3 = {};
211 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
212 ::testing::_, ATK_STATE_ENABLED, TRUE));
213 fl_accessible_node_set_flags(node, &flags3);
214 AtkStateSet* state3 = atk_object_ref_state_set(ATK_OBJECT(node));
215 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_SENSITIVE));
216 EXPECT_TRUE(atk_state_set_contains_state(state3, ATK_STATE_ENABLED));
217 g_object_unref(state3);
218}
219
220// Checks the Flutter obscured flag is mapped to ATK flags.
222 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
223
224 FlutterSemanticsFlags flags1 = {};
225 flags1.is_obscured = true;
226 fl_accessible_node_set_flags(node, &flags1);
227 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
228 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_SHOWING));
229 g_object_unref(state1);
230
231 FlutterSemanticsFlags flags2 = {};
232 flags2.is_obscured = false;
233 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
234 ::testing::_, ATK_STATE_SHOWING, TRUE));
235 fl_accessible_node_set_flags(node, &flags2);
236 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
237 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_SHOWING));
238 g_object_unref(state2);
239}
240
241// Checks the Flutter hidden flag is mapped to ATK flags.
243 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
244
245 FlutterSemanticsFlags flags1 = {};
246 flags1.is_hidden = true;
247 fl_accessible_node_set_flags(node, &flags1);
248 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
249 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_VISIBLE));
250 g_object_unref(state1);
251
252 FlutterSemanticsFlags flags2 = {};
253 flags2.is_hidden = false;
254 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
255 ::testing::_, ATK_STATE_VISIBLE, TRUE));
256 fl_accessible_node_set_flags(node, &flags2);
257 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
258 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_VISIBLE));
259 g_object_unref(state2);
260}
261
262// Checks the Flutter read only flag is mapped to ATK flags.
264 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
265
266 FlutterSemanticsFlags flags1 = {};
267 flags1.is_read_only = false;
268 fl_accessible_node_set_flags(node, &flags1);
269 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
270 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_READ_ONLY));
271 g_object_unref(state1);
272
273 FlutterSemanticsFlags flags2 = {};
274 flags2.is_read_only = true;
275 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
276 ::testing::_, ATK_STATE_READ_ONLY, TRUE));
277 fl_accessible_node_set_flags(node, &flags2);
278 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
279 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_READ_ONLY));
280 g_object_unref(state2);
281}
282
283// Checks the Flutter text field flag is mapped to ATK flags.
284TEST_F(FlAccessibleNodeTest, TextFieldFlags) {
285 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
286
287 FlutterSemanticsFlags flags1 = {};
288 flags1.is_text_field = false;
289 fl_accessible_node_set_flags(node, &flags1);
290 AtkStateSet* state1 = atk_object_ref_state_set(ATK_OBJECT(node));
291 EXPECT_FALSE(atk_state_set_contains_state(state1, ATK_STATE_EDITABLE));
292 g_object_unref(state1);
293
294 FlutterSemanticsFlags flags2 = {};
295 flags2.is_text_field = true;
296 EXPECT_CALL(mock_gtk, atk_object_notify_state_change(
297 ::testing::_, ATK_STATE_EDITABLE, TRUE));
298 fl_accessible_node_set_flags(node, &flags2);
299 AtkStateSet* state2 = atk_object_ref_state_set(ATK_OBJECT(node));
300 EXPECT_TRUE(atk_state_set_contains_state(state2, ATK_STATE_EDITABLE));
301 g_object_unref(state2);
302}
303
304// Checks Flutter flags are mapped to appropriate ATK roles.
306 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
307
308 FlutterSemanticsFlags flags1 = {};
309 flags1.is_button = true;
310 fl_accessible_node_set_flags(node, &flags1);
311 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_PUSH_BUTTON);
312
313 FlutterSemanticsFlags flags2 = {};
315 fl_accessible_node_set_flags(node, &flags2);
316 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_CHECK_BOX);
317
318 FlutterSemanticsFlags flags3 = {};
320 flags3.is_in_mutually_exclusive_group = true;
321 fl_accessible_node_set_flags(node, &flags3);
322 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_RADIO_BUTTON);
323
324 FlutterSemanticsFlags flags4 = {};
326 fl_accessible_node_set_flags(node, &flags4);
327 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_TOGGLE_BUTTON);
328
329 FlutterSemanticsFlags flags5 = {};
330 flags5.is_text_field = true;
331 fl_accessible_node_set_flags(node, &flags5);
332 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_TEXT);
333
334 FlutterSemanticsFlags flags6 = {};
335 flags6.is_text_field = true;
336 flags6.is_obscured = true;
337 fl_accessible_node_set_flags(node, &flags6);
338 EXPECT_EQ(atk_object_get_role(ATK_OBJECT(node)), ATK_ROLE_PASSWORD_TEXT);
339}
340
341// Checks Flutter actions are mapped to the appropriate ATK actions.
343 g_autoptr(FlAccessibleNode) node = fl_accessible_node_new(engine, 123, 0);
345 node, static_cast<FlutterSemanticsAction>(
347
348 EXPECT_EQ(atk_action_get_n_actions(ATK_ACTION(node)), 2);
349 EXPECT_STREQ(atk_action_get_name(ATK_ACTION(node), 0), "Tap");
350 EXPECT_STREQ(atk_action_get_name(ATK_ACTION(node), 1), "LongPress");
351}
352
353// The AT-SPI bridge can keep nodes alive past engine teardown. Accessing a
354// node after its engine is destroyed must not dereference freed memory.
355// https://github.com/flutter/flutter/issues/188660
356TEST_F(FlAccessibleNodeTest, SurvivesEngineDestruction) {
357 g_autoptr(FlDartProject) project = fl_dart_project_new();
358 FlEngine* local_engine = fl_engine_new(project);
359 g_autoptr(FlAccessibleNode) node =
360 fl_accessible_node_new(local_engine, 123, 0);
362
363 // Destroy the engine while the node is still alive.
364 g_object_unref(local_engine);
365
366 // Performing an action no longer reaches the engine, but does not crash.
367 EXPECT_FALSE(atk_action_do_action(ATK_ACTION(node), 0));
368}
369
370// Accessing a node after its parent is destroyed must not dereference freed
371// memory.
372TEST_F(FlAccessibleNodeTest, SurvivesParentDestruction) {
373 FlAccessibleNode* root = fl_accessible_node_new(engine, 123, 0);
374 g_autoptr(FlAccessibleNode) child = fl_accessible_node_new(engine, 123, 1);
375 fl_accessible_node_set_parent(child, ATK_OBJECT(root), 0);
376
377 // Destroy the parent while the child is still alive.
378 g_object_unref(root);
379
380 // The child no longer reports a parent, but does not crash.
381 EXPECT_EQ(atk_object_get_parent(ATK_OBJECT(child)), nullptr);
382 gint x = 0, y = 0, width = 0, height = 0;
383 atk_component_get_extents(ATK_COMPONENT(child), &x, &y, &width, &height,
384 ATK_XY_SCREEN);
385}
::testing::NiceMock< flutter::testing::MockGtk > mock_gtk
int32_t x
@ kFlutterCheckStateNone
The semantics node does not have check state.
Definition embedder.h:289
@ kFlutterCheckStateTrue
The semantics node is checked.
Definition embedder.h:291
@ kFlutterCheckStateFalse
The semantics node is not checked.
Definition embedder.h:293
FlutterSemanticsAction
Definition embedder.h:122
@ kFlutterSemanticsActionLongPress
Definition embedder.h:128
@ kFlutterSemanticsActionTap
Definition embedder.h:125
@ kFlutterTristateTrue
The property is applicable and its state is "true" or "on".
Definition embedder.h:282
@ kFlutterTristateFalse
The property is applicable and its state is "false" or "off".
Definition embedder.h:284
@ kFlutterTristateNone
The property is not applicable to this semantics node.
Definition embedder.h:280
FlutterEngine engine
Definition main.cc:84
g_autoptr(FlEngine) engine
void fl_accessible_node_set_name(FlAccessibleNode *self, const gchar *name)
void fl_accessible_node_set_actions(FlAccessibleNode *self, FlutterSemanticsAction actions)
void fl_accessible_node_set_children(FlAccessibleNode *self, GPtrArray *children)
void fl_accessible_node_set_extents(FlAccessibleNode *self, gint x, gint y, gint width, gint height)
void fl_accessible_node_set_parent(FlAccessibleNode *self, AtkObject *parent, gint index)
void fl_accessible_node_set_flags(FlAccessibleNode *self, FlutterSemanticsFlags *flags)
FlAccessibleNode * fl_accessible_node_new(FlEngine *engine, FlutterViewId view_id, int32_t node_id)
TEST_F(FlAccessibleNodeTest, BuildTree)
return TRUE
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition fl_engine.cc:730
G_BEGIN_DECLS FlutterViewId view_id
void atk_object_notify_state_change(AtkObject *accessible, AtkState state, gboolean value)
Definition mock_gtk.cc:391
double y
int32_t height
int32_t width
FlutterTristate is_enabled
Whether a semantic node is currently enabled.
Definition embedder.h:306
bool is_obscured
Whether the value of the semantics node is obscured.
Definition embedder.h:326
FlutterTristate is_selected
Whether a semantics node is selected.
Definition embedder.h:304
bool is_in_mutually_exclusive_group
Whether a semantic node is in a mutually exclusive group.
Definition embedder.h:322
FlutterTristate is_toggled
Definition embedder.h:309
FlutterTristate is_focused
Whether the semantic node currently holds the user's focus.
Definition embedder.h:316
bool is_hidden
Whether the semantics node is considered hidden.
Definition embedder.h:333
bool is_text_field
Whether the semantic node represents a text field.
Definition embedder.h:320
FlutterCheckState is_checked
Whether a semantics node is checked.
Definition embedder.h:302
bool is_button
Whether the semantic node represents a button.
Definition embedder.h:318