| Flutter Linux Embedder |

fl_accessible_text_field_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.
6 #include "gtest/gtest.h"
7
8 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
9 #include "flutter/shell/platform/linux/fl_accessible_text_field.h"
10 #include "flutter/shell/platform/linux/fl_engine_private.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h"
12 #include "flutter/shell/platform/linux/testing/mock_signal_handler.h"
13
14 // MOCK_ENGINE_PROC is leaky by design
15 // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
16
17 static FlValue* decode_semantic_data(const uint8_t* data, size_t data_length) {
18 g_autoptr(GBytes) bytes = g_bytes_new(data, data_length);
19 g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
20 return fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), bytes,
21 nullptr);
22 }
23
24 // Tests that semantic node value updates from Flutter emit AtkText::text-insert
25 // and AtkText::text-remove signals as expected.
26 TEST(FlAccessibleTextFieldTest, SetValue) {
27 g_autoptr(FlDartProject) project = fl_dart_project_new();
28 g_autoptr(FlEngine) engine = fl_engine_new(project);
29 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
30
31 // "" -> "Flutter"
32 {
33 flutter::testing::MockSignalHandler2<int, int> text_inserted(node,
34 "text-insert");
35 flutter::testing::MockSignalHandler text_removed(node, "text-remove");
36
37 EXPECT_SIGNAL2(text_inserted, ::testing::Eq(0), ::testing::Eq(7));
38 EXPECT_SIGNAL(text_removed).Times(0);
39
40 fl_accessible_node_set_value(node, "Flutter");
41 }
42
43 // "Flutter" -> "Flutter"
44 {
45 flutter::testing::MockSignalHandler text_inserted(node, "text-insert");
46 flutter::testing::MockSignalHandler text_removed(node, "text-remove");
47
48 EXPECT_SIGNAL(text_inserted).Times(0);
49 EXPECT_SIGNAL(text_removed).Times(0);
50
51 fl_accessible_node_set_value(node, "Flutter");
52 }
53
54 // "Flutter" -> "engine"
55 {
56 flutter::testing::MockSignalHandler2<int, int> text_inserted(node,
57 "text-insert");
58 flutter::testing::MockSignalHandler2<int, int> text_removed(node,
59 "text-remove");
60
61 EXPECT_SIGNAL2(text_inserted, ::testing::Eq(0), ::testing::Eq(6));
62 EXPECT_SIGNAL2(text_removed, ::testing::Eq(0), ::testing::Eq(7));
63
64 fl_accessible_node_set_value(node, "engine");
65 }
66
67 // "engine" -> ""
68 {
69 flutter::testing::MockSignalHandler text_inserted(node, "text-insert");
70 flutter::testing::MockSignalHandler2<int, int> text_removed(node,
71 "text-remove");
72
73 EXPECT_SIGNAL(text_inserted).Times(0);
74 EXPECT_SIGNAL2(text_removed, ::testing::Eq(0), ::testing::Eq(6));
75
76 fl_accessible_node_set_value(node, "");
77 }
78 }
79
80 // Tests that semantic node selection updates from Flutter emit
81 // AtkText::text-selection-changed and AtkText::text-caret-moved signals as
82 // expected.
83 TEST(FlAccessibleTextFieldTest, SetTextSelection) {
84 g_autoptr(FlDartProject) project = fl_dart_project_new();
85 g_autoptr(FlEngine) engine = fl_engine_new(project);
86 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
87
88 // [-1,-1] -> [2,3]
89 {
90 flutter::testing::MockSignalHandler text_selection_changed(
91 node, "text-selection-changed");
92 flutter::testing::MockSignalHandler1<int> text_caret_moved(
93 node, "text-caret-moved");
94
95 EXPECT_SIGNAL(text_selection_changed);
96 EXPECT_SIGNAL1(text_caret_moved, ::testing::Eq(3));
97
98 fl_accessible_node_set_text_selection(node, 2, 3);
99 }
100
101 // [2,3] -> [3,3]
102 {
103 flutter::testing::MockSignalHandler text_selection_changed(
104 node, "text-selection-changed");
105 flutter::testing::MockSignalHandler text_caret_moved(node,
106 "text-caret-moved");
107
108 EXPECT_SIGNAL(text_selection_changed);
109 EXPECT_SIGNAL(text_caret_moved).Times(0);
110
111 fl_accessible_node_set_text_selection(node, 3, 3);
112 }
113
114 // [3,3] -> [3,3]
115 {
116 flutter::testing::MockSignalHandler text_selection_changed(
117 node, "text-selection-changed");
118 flutter::testing::MockSignalHandler text_caret_moved(node,
119 "text-caret-moved");
120
121 EXPECT_SIGNAL(text_selection_changed).Times(0);
122 EXPECT_SIGNAL(text_caret_moved).Times(0);
123
124 fl_accessible_node_set_text_selection(node, 3, 3);
125 }
126
127 // [3,3] -> [4,4]
128 {
129 flutter::testing::MockSignalHandler text_selection_changed(
130 node, "text-selection-changed");
131 flutter::testing::MockSignalHandler1<int> text_caret_moved(
132 node, "text-caret-moved");
133
134 EXPECT_SIGNAL(text_selection_changed).Times(0);
135 EXPECT_SIGNAL1(text_caret_moved, ::testing::Eq(4));
136
137 fl_accessible_node_set_text_selection(node, 4, 4);
138 }
139 }
140
141 // Tests that fl_accessible_text_field_perform_action() passes the required
142 // "expandSelection" argument for semantic cursor move actions.
143 TEST(FlAccessibleTextFieldTest, PerformAction) {
144 g_autoptr(GPtrArray) action_datas = g_ptr_array_new_with_free_func(
145 reinterpret_cast<GDestroyNotify>(fl_value_unref));
146
147 g_autoptr(FlDartProject) project = fl_dart_project_new();
148 g_autoptr(FlEngine) engine = fl_engine_new(project);
149
150 g_autoptr(GError) error = nullptr;
151 EXPECT_TRUE(fl_engine_start(engine, &error));
152 EXPECT_EQ(error, nullptr);
153
154 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
155 MOCK_ENGINE_PROC(
156 DispatchSemanticsAction,
157 ([&action_datas](auto engine, uint64_t id,
158 FlutterSemanticsAction action, const uint8_t* data,
159 size_t data_length) {
160 g_ptr_array_add(action_datas,
161 decode_semantic_data(data, data_length));
162 return kSuccess;
163 }));
164
165 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
166 fl_accessible_node_set_actions(
167 node, static_cast<FlutterSemanticsAction>(
168 kFlutterSemanticsActionMoveCursorForwardByCharacter |
169 kFlutterSemanticsActionMoveCursorBackwardByCharacter |
170 kFlutterSemanticsActionMoveCursorForwardByWord |
171 kFlutterSemanticsActionMoveCursorBackwardByWord));
172
173 g_autoptr(FlValue) expand_selection = fl_value_new_bool(false);
174
175 for (int i = 0; i < 4; ++i) {
176 atk_action_do_action(ATK_ACTION(node), i);
177
178 FlValue* data = static_cast<FlValue*>(g_ptr_array_index(action_datas, i));
179 EXPECT_NE(data, nullptr);
180 EXPECT_TRUE(fl_value_equal(data, expand_selection));
181 }
182 }
183
184 // Tests AtkText::get_character_count.
185 TEST(FlAccessibleTextFieldTest, GetCharacterCount) {
186 g_autoptr(FlDartProject) project = fl_dart_project_new();
187 g_autoptr(FlEngine) engine = fl_engine_new(project);
188 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
189
190 EXPECT_EQ(atk_text_get_character_count(ATK_TEXT(node)), 0);
191
192 fl_accessible_node_set_value(node, "Flutter!");
193
194 EXPECT_EQ(atk_text_get_character_count(ATK_TEXT(node)), 8);
195 }
196
197 // Tests AtkText::get_text.
198 TEST(FlAccessibleTextFieldTest, GetText) {
199 g_autoptr(FlDartProject) project = fl_dart_project_new();
200 g_autoptr(FlEngine) engine = fl_engine_new(project);
201 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
202
203 g_autofree gchar* empty = atk_text_get_text(ATK_TEXT(node), 0, -1);
204 EXPECT_STREQ(empty, "");
205
206 flutter::testing::MockSignalHandler text_inserted(node, "text-insert");
207 EXPECT_SIGNAL(text_inserted).Times(1);
208
209 fl_accessible_node_set_value(node, "Flutter!");
210
211 g_autofree gchar* flutter = atk_text_get_text(ATK_TEXT(node), 0, -1);
212 EXPECT_STREQ(flutter, "Flutter!");
213
214 g_autofree gchar* tt = atk_text_get_text(ATK_TEXT(node), 3, 5);
215 EXPECT_STREQ(tt, "tt");
216 }
217
218 // Tests AtkText::get_caret_offset.
219 TEST(FlAccessibleTextFieldTest, GetCaretOffset) {
220 g_autoptr(FlDartProject) project = fl_dart_project_new();
221 g_autoptr(FlEngine) engine = fl_engine_new(project);
222 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
223
224 EXPECT_EQ(atk_text_get_caret_offset(ATK_TEXT(node)), -1);
225
226 fl_accessible_node_set_text_selection(node, 1, 2);
227
228 EXPECT_EQ(atk_text_get_caret_offset(ATK_TEXT(node)), 2);
229 }
230
231 // Tests AtkText::set_caret_offset.
232 TEST(FlAccessibleTextFieldTest, SetCaretOffset) {
233 int base = -1;
234 int extent = -1;
235
236 g_autoptr(FlDartProject) project = fl_dart_project_new();
237 g_autoptr(FlEngine) engine = fl_engine_new(project);
238
239 g_autoptr(GError) error = nullptr;
240 EXPECT_TRUE(fl_engine_start(engine, &error));
241 EXPECT_EQ(error, nullptr);
242
243 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
244 MOCK_ENGINE_PROC(
245 DispatchSemanticsAction,
246 ([&base, &extent](auto engine, uint64_t id,
247 FlutterSemanticsAction action, const uint8_t* data,
248 size_t data_length) {
249 EXPECT_EQ(action, kFlutterSemanticsActionSetSelection);
250 g_autoptr(FlValue) value = decode_semantic_data(data, data_length);
251 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
252 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
253 extent = fl_value_get_int(fl_value_lookup_string(value, "extent"));
254 return kSuccess;
255 }));
256
257 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
258
259 EXPECT_TRUE(atk_text_set_caret_offset(ATK_TEXT(node), 3));
260 EXPECT_EQ(base, 3);
261 EXPECT_EQ(extent, 3);
262 }
263
264 // Tests AtkText::get_n_selections.
265 TEST(FlAccessibleTextFieldTest, GetNSelections) {
266 g_autoptr(FlDartProject) project = fl_dart_project_new();
267 g_autoptr(FlEngine) engine = fl_engine_new(project);
268 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
269
270 EXPECT_EQ(atk_text_get_n_selections(ATK_TEXT(node)), 0);
271
272 fl_accessible_node_set_text_selection(node, 1, 2);
273
274 EXPECT_EQ(atk_text_get_n_selections(ATK_TEXT(node)), 1);
275 }
276
277 // Tests AtkText::get_selection.
278 TEST(FlAccessibleTextFieldTest, GetSelection) {
279 g_autoptr(FlDartProject) project = fl_dart_project_new();
280 g_autoptr(FlEngine) engine = fl_engine_new(project);
281 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
282
283 EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 0, nullptr, nullptr),
284 nullptr);
285
286 fl_accessible_node_set_value(node, "Flutter");
287 fl_accessible_node_set_text_selection(node, 2, 5);
288
289 gint start, end;
290 g_autofree gchar* selection =
291 atk_text_get_selection(ATK_TEXT(node), 0, &start, &end);
292 EXPECT_STREQ(selection, "utt");
293 EXPECT_EQ(start, 2);
294 EXPECT_EQ(end, 5);
295
296 // reverse
297 fl_accessible_node_set_text_selection(node, 5, 2);
298 g_autofree gchar* reverse =
299 atk_text_get_selection(ATK_TEXT(node), 0, &start, &end);
300 EXPECT_STREQ(reverse, "utt");
301 EXPECT_EQ(start, 2);
302 EXPECT_EQ(end, 5);
303
304 // empty
305 fl_accessible_node_set_text_selection(node, 5, 5);
306 EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 0, &start, &end), nullptr);
307
308 // selection num != 0
309 EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 1, &start, &end), nullptr);
310 }
311
312 // Tests AtkText::add_selection.
313 TEST(FlAccessibleTextFieldTest, AddSelection) {
314 int base = -1;
315 int extent = -1;
316
317 g_autoptr(FlDartProject) project = fl_dart_project_new();
318 g_autoptr(FlEngine) engine = fl_engine_new(project);
319
320 g_autoptr(GError) error = nullptr;
321 EXPECT_TRUE(fl_engine_start(engine, &error));
322 EXPECT_EQ(error, nullptr);
323
324 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
325 MOCK_ENGINE_PROC(
326 DispatchSemanticsAction,
327 ([&base, &extent](auto engine, uint64_t id,
328 FlutterSemanticsAction action, const uint8_t* data,
329 size_t data_length) {
330 EXPECT_EQ(action, kFlutterSemanticsActionSetSelection);
331 g_autoptr(FlValue) value = decode_semantic_data(data, data_length);
332 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
333 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
334 extent = fl_value_get_int(fl_value_lookup_string(value, "extent"));
335 return kSuccess;
336 }));
337
338 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
339
340 EXPECT_TRUE(atk_text_add_selection(ATK_TEXT(node), 2, 4));
341 EXPECT_EQ(base, 2);
342 EXPECT_EQ(extent, 4);
343
344 fl_accessible_node_set_text_selection(node, 2, 4);
345
346 // already has selection
347 EXPECT_FALSE(atk_text_add_selection(ATK_TEXT(node), 6, 7));
348 EXPECT_EQ(base, 2);
349 EXPECT_EQ(extent, 4);
350 }
351
352 // Tests AtkText::remove_selection.
353 TEST(FlAccessibleTextFieldTest, RemoveSelection) {
354 int base = -1;
355 int extent = -1;
356
357 g_autoptr(FlDartProject) project = fl_dart_project_new();
358 g_autoptr(FlEngine) engine = fl_engine_new(project);
359
360 g_autoptr(GError) error = nullptr;
361 EXPECT_TRUE(fl_engine_start(engine, &error));
362 EXPECT_EQ(error, nullptr);
363
364 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
365 MOCK_ENGINE_PROC(
366 DispatchSemanticsAction,
367 ([&base, &extent](auto engine, uint64_t id,
368 FlutterSemanticsAction action, const uint8_t* data,
369 size_t data_length) {
370 EXPECT_EQ(action, kFlutterSemanticsActionSetSelection);
371 g_autoptr(FlValue) value = decode_semantic_data(data, data_length);
372 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
373 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
374 extent = fl_value_get_int(fl_value_lookup_string(value, "extent"));
375 return kSuccess;
376 }));
377
378 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
379
380 // no selection
381 EXPECT_FALSE(atk_text_remove_selection(ATK_TEXT(node), 0));
382 EXPECT_EQ(base, -1);
383 EXPECT_EQ(extent, -1);
384
385 fl_accessible_node_set_text_selection(node, 2, 4);
386
387 // selection num != 0
388 EXPECT_FALSE(atk_text_remove_selection(ATK_TEXT(node), 1));
389 EXPECT_EQ(base, -1);
390 EXPECT_EQ(extent, -1);
391
392 // ok, collapses selection
393 EXPECT_TRUE(atk_text_remove_selection(ATK_TEXT(node), 0));
394 EXPECT_EQ(base, 4);
395 EXPECT_EQ(extent, 4);
396 }
397
398 // Tests AtkText::set_selection.
399 TEST(FlAccessibleTextFieldTest, SetSelection) {
400 int base = -1;
401 int extent = -1;
402
403 g_autoptr(FlDartProject) project = fl_dart_project_new();
404 g_autoptr(FlEngine) engine = fl_engine_new(project);
405
406 g_autoptr(GError) error = nullptr;
407 EXPECT_TRUE(fl_engine_start(engine, &error));
408 EXPECT_EQ(error, nullptr);
409
410 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
411 MOCK_ENGINE_PROC(
412 DispatchSemanticsAction,
413 ([&base, &extent](auto engine, uint64_t id,
414 FlutterSemanticsAction action, const uint8_t* data,
415 size_t data_length) {
416 EXPECT_EQ(action, kFlutterSemanticsActionSetSelection);
417 g_autoptr(FlValue) value = decode_semantic_data(data, data_length);
418 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
419 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
420 extent = fl_value_get_int(fl_value_lookup_string(value, "extent"));
421 return kSuccess;
422 }));
423
424 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
425
426 // selection num != 0
427 EXPECT_FALSE(atk_text_set_selection(ATK_TEXT(node), 1, 2, 4));
428 EXPECT_EQ(base, -1);
429 EXPECT_EQ(extent, -1);
430
431 EXPECT_TRUE(atk_text_set_selection(ATK_TEXT(node), 0, 2, 4));
432 EXPECT_EQ(base, 2);
433 EXPECT_EQ(extent, 4);
434
435 EXPECT_TRUE(atk_text_set_selection(ATK_TEXT(node), 0, 5, 1));
436 EXPECT_EQ(base, 5);
437 EXPECT_EQ(extent, 1);
438 }
439
440 // Tests AtkEditableText::set_text_contents.
441 TEST(FlAccessibleTextFieldTest, SetTextContents) {
442 g_autofree gchar* text = nullptr;
443
444 g_autoptr(FlDartProject) project = fl_dart_project_new();
445 g_autoptr(FlEngine) engine = fl_engine_new(project);
446
447 g_autoptr(GError) error = nullptr;
448 EXPECT_TRUE(fl_engine_start(engine, &error));
449 EXPECT_EQ(error, nullptr);
450
451 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
452 MOCK_ENGINE_PROC(
453 DispatchSemanticsAction,
454 ([&text](auto engine, uint64_t id, FlutterSemanticsAction action,
455 const uint8_t* data, size_t data_length) {
456 EXPECT_EQ(action, kFlutterSemanticsActionSetText);
457 g_autoptr(FlValue) value = decode_semantic_data(data, data_length);
458 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING);
459 text = g_strdup(fl_value_get_string(value));
460 return kSuccess;
461 }));
462
463 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
464
465 atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(node), "Flutter");
466 EXPECT_STREQ(text, "Flutter");
467 }
468
469 // Tests AtkEditableText::insert/delete_text.
470 TEST(FlAccessibleTextFieldTest, InsertDeleteText) {
471 g_autofree gchar* text = nullptr;
472 int base = -1;
473 int extent = -1;
474
475 g_autoptr(FlDartProject) project = fl_dart_project_new();
476 g_autoptr(FlEngine) engine = fl_engine_new(project);
477
478 g_autoptr(GError) error = nullptr;
479 EXPECT_TRUE(fl_engine_start(engine, &error));
480 EXPECT_EQ(error, nullptr);
481
482 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
483 MOCK_ENGINE_PROC(
484 DispatchSemanticsAction,
485 ([&text, &base, &extent](auto engine, uint64_t id,
486 FlutterSemanticsAction action,
487 const uint8_t* data, size_t data_length) {
488 EXPECT_THAT(action,
489 ::testing::AnyOf(kFlutterSemanticsActionSetText,
490 kFlutterSemanticsActionSetSelection));
491 if (action == kFlutterSemanticsActionSetText) {
492 g_autoptr(FlValue) value =
493 decode_semantic_data(data, data_length);
494 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING);
495 g_free(text);
496 text = g_strdup(fl_value_get_string(value));
497 } else {
498 g_autoptr(FlValue) value =
499 decode_semantic_data(data, data_length);
500 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
501 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
502 extent =
503 fl_value_get_int(fl_value_lookup_string(value, "extent"));
504 }
505 return kSuccess;
506 }));
507
508 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
509 fl_accessible_node_set_value(node, "Fler");
510
511 gint pos = 2;
512 atk_editable_text_insert_text(ATK_EDITABLE_TEXT(node), "utt", 3, &pos);
513 EXPECT_EQ(pos, 5);
514 EXPECT_STREQ(text, "Flutter");
515 EXPECT_EQ(base, pos);
516 EXPECT_EQ(extent, pos);
517
518 atk_editable_text_delete_text(ATK_EDITABLE_TEXT(node), 2, 5);
519 EXPECT_STREQ(text, "Fler");
520 EXPECT_EQ(base, 2);
521 EXPECT_EQ(extent, 2);
522 }
523
524 // Tests AtkEditableText::copy/cut/paste_text.
525 TEST(FlAccessibleTextFieldTest, CopyCutPasteText) {
526 int base = -1;
527 int extent = -1;
528 FlutterSemanticsAction act = kFlutterSemanticsActionCustomAction;
529
530 g_autoptr(FlDartProject) project = fl_dart_project_new();
531 g_autoptr(FlEngine) engine = fl_engine_new(project);
532
533 g_autoptr(GError) error = nullptr;
534 EXPECT_TRUE(fl_engine_start(engine, &error));
535 EXPECT_EQ(error, nullptr);
536
537 fl_engine_get_embedder_api(engine)->DispatchSemanticsAction =
538 MOCK_ENGINE_PROC(
539 DispatchSemanticsAction,
540 ([&act, &base, &extent](auto engine, uint64_t id,
541 FlutterSemanticsAction action,
542 const uint8_t* data, size_t data_length) {
543 EXPECT_THAT(action,
544 ::testing::AnyOf(kFlutterSemanticsActionCut,
545 kFlutterSemanticsActionCopy,
546 kFlutterSemanticsActionPaste,
547 kFlutterSemanticsActionSetSelection));
548 act = action;
549 if (action == kFlutterSemanticsActionSetSelection) {
550 g_autoptr(FlValue) value =
551 decode_semantic_data(data, data_length);
552 EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP);
553 base = fl_value_get_int(fl_value_lookup_string(value, "base"));
554 extent =
555 fl_value_get_int(fl_value_lookup_string(value, "extent"));
556 }
557 return kSuccess;
558 }));
559
560 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
561
562 atk_editable_text_copy_text(ATK_EDITABLE_TEXT(node), 2, 5);
563 EXPECT_EQ(base, 2);
564 EXPECT_EQ(extent, 5);
565 EXPECT_EQ(act, kFlutterSemanticsActionCopy);
566
567 atk_editable_text_cut_text(ATK_EDITABLE_TEXT(node), 1, 4);
568 EXPECT_EQ(base, 1);
569 EXPECT_EQ(extent, 4);
570 EXPECT_EQ(act, kFlutterSemanticsActionCut);
571
572 atk_editable_text_paste_text(ATK_EDITABLE_TEXT(node), 3);
573 EXPECT_EQ(base, 3);
574 EXPECT_EQ(extent, 3);
575 EXPECT_EQ(act, kFlutterSemanticsActionPaste);
576 }
577
578 TEST(FlAccessibleTextFieldTest, TextBoundary) {
579 g_autoptr(FlDartProject) project = fl_dart_project_new();
580 g_autoptr(FlEngine) engine = fl_engine_new(project);
581 g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1);
582
583 fl_accessible_node_set_value(node,
584 "Lorem ipsum.\nDolor sit amet. Praesent commodo?"
585 "\n\nPraesent et felis dui.");
586
587 // |Lorem
588 gint start_offset = -1, end_offset = -1;
589 g_autofree gchar* lorem_char = atk_text_get_string_at_offset(
590 ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_CHAR, &start_offset, &end_offset);
591 EXPECT_STREQ(lorem_char, "L");
592 EXPECT_EQ(start_offset, 0);
593 EXPECT_EQ(end_offset, 1);
594
595 g_autofree gchar* lorem_word = atk_text_get_string_at_offset(
596 ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_WORD, &start_offset, &end_offset);
597 EXPECT_STREQ(lorem_word, "Lorem");
598 EXPECT_EQ(start_offset, 0);
599 EXPECT_EQ(end_offset, 5);
600
601 g_autofree gchar* lorem_sentence = atk_text_get_string_at_offset(
602 ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset,
603 &end_offset);
604 EXPECT_STREQ(lorem_sentence, "Lorem ipsum.");
605 EXPECT_EQ(start_offset, 0);
606 EXPECT_EQ(end_offset, 12);
607
608 g_autofree gchar* lorem_line = atk_text_get_string_at_offset(
609 ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_LINE, &start_offset, &end_offset);
610 EXPECT_STREQ(lorem_line, "Lorem ipsum.");
611 EXPECT_EQ(start_offset, 0);
612 EXPECT_EQ(end_offset, 12);
613
614 g_autofree gchar* lorem_paragraph = atk_text_get_string_at_offset(
615 ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset,
616 &end_offset);
617 EXPECT_STREQ(lorem_paragraph,
618 "Lorem ipsum.\nDolor sit amet. Praesent commodo?");
619 EXPECT_EQ(start_offset, 0);
620 EXPECT_EQ(end_offset, 46);
621
622 // Pra|esent
623 g_autofree gchar* praesent_char = atk_text_get_string_at_offset(
624 ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_CHAR, &start_offset,
625 &end_offset);
626 EXPECT_STREQ(praesent_char, "e");
627 EXPECT_EQ(start_offset, 32);
628 EXPECT_EQ(end_offset, 33);
629
630 g_autofree gchar* praesent_word = atk_text_get_string_at_offset(
631 ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_WORD, &start_offset,
632 &end_offset);
633 EXPECT_STREQ(praesent_word, "Praesent");
634 EXPECT_EQ(start_offset, 29);
635 EXPECT_EQ(end_offset, 37);
636
637 g_autofree gchar* praesent_sentence = atk_text_get_string_at_offset(
638 ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset,
639 &end_offset);
640 EXPECT_STREQ(praesent_sentence, "Praesent commodo?");
641 EXPECT_EQ(start_offset, 29);
642 EXPECT_EQ(end_offset, 46);
643
644 g_autofree gchar* praesent_line = atk_text_get_string_at_offset(
645 ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_LINE, &start_offset,
646 &end_offset);
647 EXPECT_STREQ(praesent_line, "Dolor sit amet. Praesent commodo?");
648 EXPECT_EQ(start_offset, 13);
649 EXPECT_EQ(end_offset, 46);
650
651 g_autofree gchar* praesent_paragraph = atk_text_get_string_at_offset(
652 ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset,
653 &end_offset);
654 EXPECT_STREQ(praesent_paragraph,
655 "Lorem ipsum.\nDolor sit amet. Praesent commodo?");
656 EXPECT_EQ(start_offset, 0);
657 EXPECT_EQ(end_offset, 46);
658
659 // feli|s
660 g_autofree gchar* felis_char = atk_text_get_string_at_offset(
661 ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_CHAR, &start_offset,
662 &end_offset);
663 EXPECT_STREQ(felis_char, "s");
664 EXPECT_EQ(start_offset, 64);
665 EXPECT_EQ(end_offset, 65);
666
667 g_autofree gchar* felis_word = atk_text_get_string_at_offset(
668 ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_WORD, &start_offset,
669 &end_offset);
670 EXPECT_STREQ(felis_word, "felis");
671 EXPECT_EQ(start_offset, 60);
672 EXPECT_EQ(end_offset, 65);
673
674 g_autofree gchar* felis_sentence = atk_text_get_string_at_offset(
675 ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset,
676 &end_offset);
677 EXPECT_STREQ(felis_sentence, "Praesent et felis dui.");
678 EXPECT_EQ(start_offset, 48);
679 EXPECT_EQ(end_offset, 70);
680
681 g_autofree gchar* felis_line = atk_text_get_string_at_offset(
682 ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_LINE, &start_offset,
683 &end_offset);
684 EXPECT_STREQ(felis_line, "Praesent et felis dui.");
685 EXPECT_EQ(start_offset, 48);
686 EXPECT_EQ(end_offset, 70);
687
688 g_autofree gchar* felis_paragraph = atk_text_get_string_at_offset(
689 ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset,
690 &end_offset);
691 EXPECT_STREQ(felis_paragraph, "\nPraesent et felis dui.");
692 EXPECT_EQ(start_offset, 47);
693 EXPECT_EQ(end_offset, 70);
694 }
695
696 // NOLINTEND(clang-analyzer-core.StackAddressEscape)
FL_VALUE_TYPE_MAP
@ FL_VALUE_TYPE_MAP
Definition: fl_value.h:74
fl_engine_get_embedder_api
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:661
fl_standard_message_codec_new
G_MODULE_EXPORT FlStandardMessageCodec * fl_standard_message_codec_new()
Definition: fl_standard_message_codec.cc:637
fl_accessible_node_set_text_selection
void fl_accessible_node_set_text_selection(FlAccessibleNode *self, gint base, gint extent)
Definition: fl_accessible_node.cc:563
fl_accessible_text_field_new
FlAccessibleNode * fl_accessible_text_field_new(FlEngine *engine, int32_t id)
Definition: fl_accessible_text_field.cc:618
i
int i
Definition: fl_socket_accessible.cc:18
fl_value_new_bool
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:255
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
fl_dart_project_new
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
Definition: fl_dart_project.cc:50
fl_value_lookup_string
G_MODULE_EXPORT FlValue * fl_value_lookup_string(FlValue *self, const gchar *key)
Definition: fl_value.cc:811
fl_value_get_string
const G_MODULE_EXPORT gchar * fl_value_get_string(FlValue *self)
Definition: fl_value.cc:682
fl_value_get_int
G_MODULE_EXPORT int64_t fl_value_get_int(FlValue *self)
Definition: fl_value.cc:668
fl_value_unref
G_MODULE_EXPORT void fl_value_unref(FlValue *self)
Definition: fl_value.cc:400
fl_message_codec_decode_message
G_MODULE_EXPORT FlValue * fl_message_codec_decode_message(FlMessageCodec *self, GBytes *message, GError **error)
Definition: fl_message_codec.cc:33
decode_semantic_data
static FlValue * decode_semantic_data(const uint8_t *data, size_t data_length)
Definition: fl_accessible_text_field_test.cc:17
fl_value_get_type
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition: fl_value.cc:466
FL_VALUE_TYPE_STRING
@ FL_VALUE_TYPE_STRING
Definition: fl_value.h:68
fl_engine_private.h
flutter
Definition: accessibility_bridge.cc:14
start
glong start
Definition: fl_accessible_text_field.cc:39
fl_accessible_node_set_value
void fl_accessible_node_set_value(FlAccessibleNode *self, const gchar *value)
Definition: fl_accessible_node.cc:557
end
glong glong end
Definition: fl_accessible_text_field.cc:40
fl_value_equal
G_MODULE_EXPORT bool fl_value_equal(FlValue *a, FlValue *b)
Definition: fl_value.cc:471
fl_engine_new
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition: fl_engine.cc:524
fl_accessible_text_field.h
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
fl_engine_start
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:544
fl_standard_message_codec.h
fl_accessible_node_set_actions
void fl_accessible_node_set_actions(FlAccessibleNode *self, FlutterSemanticsAction actions)
Definition: fl_accessible_node.cc:550
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
TEST
TEST(FlAccessibleTextFieldTest, SetValue)
Definition: fl_accessible_text_field_test.cc:26