Flutter Engine
The Flutter Engine
embedder_a11y_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// Allow access to fml::MessageLoop::GetCurrent() in order to flush platform
6// thread tasks.
7#define FML_USED_ON_EMBEDDER
8
9#include <functional>
10
11#include "flutter/fml/macros.h"
12#include "flutter/fml/message_loop.h"
13#include "flutter/fml/synchronization/waitable_event.h"
14#include "flutter/lib/ui/semantics/semantics_node.h"
15#include "flutter/shell/platform/embedder/embedder.h"
16#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
17#include "flutter/testing/testing.h"
19
20#include "gmock/gmock.h" // For EXPECT_THAT and matchers
21#include "gtest/gtest.h"
22
23// CREATE_NATIVE_ENTRY is leaky by design
24// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
25
26namespace flutter {
27namespace testing {
28
30using ::testing::ElementsAre;
31
32constexpr static char kTooltip[] = "tooltip";
33
34TEST_F(EmbedderTest, CannotProvideMultipleSemanticsCallbacks) {
35 {
38 builder.SetSoftwareRendererConfig();
39 builder.GetProjectArgs().update_semantics_callback =
40 [](const FlutterSemanticsUpdate* update, void* user_data) {};
41 builder.GetProjectArgs().update_semantics_callback2 =
42 [](const FlutterSemanticsUpdate2* update, void* user_data) {};
43 auto engine = builder.InitializeEngine();
44 ASSERT_FALSE(engine.is_valid());
45 engine.reset();
46 }
47
48 {
51 builder.SetSoftwareRendererConfig();
52 builder.GetProjectArgs().update_semantics_callback2 =
53 [](const FlutterSemanticsUpdate2* update, void* user_data) {};
54 builder.GetProjectArgs().update_semantics_node_callback =
55 [](const FlutterSemanticsNode* update, void* user_data) {};
56 builder.GetProjectArgs().update_semantics_custom_action_callback =
58 auto engine = builder.InitializeEngine();
59 ASSERT_FALSE(engine.is_valid());
60 engine.reset();
61 }
62
63 {
66 builder.SetSoftwareRendererConfig();
67 builder.GetProjectArgs().update_semantics_callback =
68 [](const FlutterSemanticsUpdate* update, void* user_data) {};
69 builder.GetProjectArgs().update_semantics_node_callback =
70 [](const FlutterSemanticsNode* update, void* user_data) {};
71 builder.GetProjectArgs().update_semantics_custom_action_callback =
73 auto engine = builder.InitializeEngine();
74 ASSERT_FALSE(engine.is_valid());
75 engine.reset();
76 }
77
78 {
81 builder.SetSoftwareRendererConfig();
82 builder.GetProjectArgs().update_semantics_callback2 =
83 [](const FlutterSemanticsUpdate2* update, void* user_data) {};
84 builder.GetProjectArgs().update_semantics_callback =
85 [](const FlutterSemanticsUpdate* update, void* user_data) {};
86 builder.GetProjectArgs().update_semantics_node_callback =
87 [](const FlutterSemanticsNode* update, void* user_data) {};
88 builder.GetProjectArgs().update_semantics_custom_action_callback =
90 auto engine = builder.InitializeEngine();
91 ASSERT_FALSE(engine.is_valid());
92 engine.reset();
93 }
94}
95
96TEST_F(EmbedderA11yTest, A11yTreeIsConsistentUsingV3Callbacks) {
97#if defined(OS_FUCHSIA)
98 GTEST_SKIP() << "This test crashes on Fuchsia. https://fxbug.dev/87493 ";
99#else
100
101 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
102
103 fml::AutoResetWaitableEvent signal_native_latch;
104
105 // Called by the Dart text fixture on the UI thread to signal that the C++
106 // unittest should resume.
107 context.AddNativeCallback(
108 "SignalNativeTest",
109 CREATE_NATIVE_ENTRY(([&signal_native_latch](Dart_NativeArguments) {
110 signal_native_latch.Signal();
111 })));
112
113 // Called by test fixture on UI thread to pass data back to this test.
114 NativeEntry notify_semantics_enabled_callback;
115 context.AddNativeCallback(
116 "NotifySemanticsEnabled",
118 ([&notify_semantics_enabled_callback](Dart_NativeArguments args) {
119 ASSERT_NE(notify_semantics_enabled_callback, nullptr);
120 notify_semantics_enabled_callback(args);
121 })));
122
123 NativeEntry notify_accessibility_features_callback;
124 context.AddNativeCallback(
125 "NotifyAccessibilityFeatures",
127 [&notify_accessibility_features_callback](Dart_NativeArguments args) {
128 ASSERT_NE(notify_accessibility_features_callback, nullptr);
129 notify_accessibility_features_callback(args);
130 })));
131
132 NativeEntry notify_semantics_action_callback;
133 context.AddNativeCallback(
134 "NotifySemanticsAction",
136 ([&notify_semantics_action_callback](Dart_NativeArguments args) {
137 ASSERT_NE(notify_semantics_action_callback, nullptr);
138 notify_semantics_action_callback(args);
139 })));
140
141 fml::AutoResetWaitableEvent semantics_update_latch;
142 context.SetSemanticsUpdateCallback2(
143 [&](const FlutterSemanticsUpdate2* update) {
144 ASSERT_EQ(size_t(4), update->node_count);
145 ASSERT_EQ(size_t(1), update->custom_action_count);
146
147 for (size_t i = 0; i < update->node_count; i++) {
148 const FlutterSemanticsNode2* node = update->nodes[i];
149
150 ASSERT_EQ(1.0, node->transform.scaleX);
151 ASSERT_EQ(2.0, node->transform.skewX);
152 ASSERT_EQ(3.0, node->transform.transX);
153 ASSERT_EQ(4.0, node->transform.skewY);
154 ASSERT_EQ(5.0, node->transform.scaleY);
155 ASSERT_EQ(6.0, node->transform.transY);
156 ASSERT_EQ(7.0, node->transform.pers0);
157 ASSERT_EQ(8.0, node->transform.pers1);
158 ASSERT_EQ(9.0, node->transform.pers2);
159 ASSERT_EQ(std::strncmp(kTooltip, node->tooltip, sizeof(kTooltip) - 1),
160 0);
161
162 if (node->id == 128) {
163 ASSERT_EQ(0x3f3, node->platform_view_id);
164 } else {
165 ASSERT_NE(kFlutterSemanticsNodeIdBatchEnd, node->id);
166 ASSERT_EQ(0, node->platform_view_id);
167 }
168 }
169
170 semantics_update_latch.Signal();
171 });
172
174 builder.SetSoftwareRendererConfig();
175 builder.SetDartEntrypoint("a11y_main");
176
177 auto engine = builder.LaunchEngine();
178 ASSERT_TRUE(engine.is_valid());
179
180 // 1: Wait for initial notifySemanticsEnabled(false).
181 fml::AutoResetWaitableEvent notify_semantics_enabled_latch;
182 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
183 Dart_Handle exception = nullptr;
184 bool enabled =
186 ASSERT_FALSE(enabled);
187 notify_semantics_enabled_latch.Signal();
188 };
189 notify_semantics_enabled_latch.Wait();
190
191 // Prepare notifyAccessibilityFeatures callback.
192 fml::AutoResetWaitableEvent notify_features_latch;
193 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
194 Dart_Handle exception = nullptr;
195 bool enabled =
197 ASSERT_FALSE(enabled);
198 notify_features_latch.Signal();
199 };
200
201 // 2: Enable semantics. Wait for notifySemanticsEnabled(true).
202 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_2;
203 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
204 Dart_Handle exception = nullptr;
205 bool enabled =
207 ASSERT_TRUE(enabled);
208 notify_semantics_enabled_latch_2.Signal();
209 };
212 notify_semantics_enabled_latch_2.Wait();
213
214 // 3: Wait for notifyAccessibilityFeatures (reduce_motion == false)
215 notify_features_latch.Wait();
216
217 // 4: Wait for notifyAccessibilityFeatures (reduce_motion == true)
218 fml::AutoResetWaitableEvent notify_features_latch_2;
219 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
220 Dart_Handle exception = nullptr;
221 bool enabled =
223 ASSERT_TRUE(enabled);
224 notify_features_latch_2.Signal();
225 };
229 notify_features_latch_2.Wait();
230
231 // 5: Wait for UpdateSemantics callback on platform (current) thread.
232 signal_native_latch.Wait();
234 semantics_update_latch.Wait();
235
236 // 6: Dispatch a tap to semantics node 42. Wait for NotifySemanticsAction.
237 fml::AutoResetWaitableEvent notify_semantics_action_latch;
238 notify_semantics_action_callback = [&](Dart_NativeArguments args) {
239 Dart_Handle exception = nullptr;
240 int64_t node_id =
242 ASSERT_EQ(42, node_id);
243
244 int64_t action_id =
246 ASSERT_EQ(static_cast<int32_t>(flutter::SemanticsAction::kTap), action_id);
247
248 std::vector<int64_t> semantic_args =
250 exception);
251 ASSERT_THAT(semantic_args, ElementsAre(2, 1));
252 notify_semantics_action_latch.Signal();
253 };
254 std::vector<uint8_t> bytes({2, 1});
256 engine.get(), 42, kFlutterSemanticsActionTap, &bytes[0], bytes.size());
258 notify_semantics_action_latch.Wait();
259
260 // 7: Disable semantics. Wait for NotifySemanticsEnabled(false).
261 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_3;
262 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
263 Dart_Handle exception = nullptr;
264 bool enabled =
266 ASSERT_FALSE(enabled);
267 notify_semantics_enabled_latch_3.Signal();
268 };
271 notify_semantics_enabled_latch_3.Wait();
272#endif // OS_FUCHSIA
273}
274
275TEST_F(EmbedderA11yTest, A11yStringAttributes) {
276#if defined(OS_FUCHSIA)
277 GTEST_SKIP() << "This test crashes on Fuchsia. https://fxbug.dev/87493 ";
278#else
279
280 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
281
282 fml::AutoResetWaitableEvent signal_native_latch;
283
284 // Called by the Dart text fixture on the UI thread to signal that the C++
285 // unittest should resume.
286 context.AddNativeCallback(
287 "SignalNativeTest",
288 CREATE_NATIVE_ENTRY(([&signal_native_latch](Dart_NativeArguments) {
289 signal_native_latch.Signal();
290 })));
291
292 fml::AutoResetWaitableEvent semantics_update_latch;
293 context.SetSemanticsUpdateCallback2(
294 [&](const FlutterSemanticsUpdate2* update) {
295 ASSERT_EQ(update->node_count, size_t(1));
296 ASSERT_EQ(update->custom_action_count, size_t(0));
297
298 auto node = update->nodes[0];
299
300 // Verify label
301 {
302 ASSERT_EQ(std::string(node->label), "What is the meaning of life?");
303 ASSERT_EQ(node->label_attribute_count, size_t(2));
304
305 ASSERT_EQ(node->label_attributes[0]->start, size_t(0));
306 ASSERT_EQ(node->label_attributes[0]->end, size_t(28));
307 ASSERT_EQ(node->label_attributes[0]->type,
309 ASSERT_EQ(std::string(node->label_attributes[0]->locale->locale),
310 "en");
311
312 ASSERT_EQ(node->label_attributes[1]->start, size_t(0));
313 ASSERT_EQ(node->label_attributes[1]->end, size_t(1));
314 ASSERT_EQ(node->label_attributes[1]->type,
316 }
317
318 // Verify hint
319 {
320 ASSERT_EQ(std::string(node->hint), "It's a number");
321 ASSERT_EQ(node->hint_attribute_count, size_t(2));
322
323 ASSERT_EQ(node->hint_attributes[0]->start, size_t(0));
324 ASSERT_EQ(node->hint_attributes[0]->end, size_t(1));
325 ASSERT_EQ(node->hint_attributes[0]->type,
327 ASSERT_EQ(std::string(node->hint_attributes[0]->locale->locale),
328 "en");
329
330 ASSERT_EQ(node->hint_attributes[1]->start, size_t(2));
331 ASSERT_EQ(node->hint_attributes[1]->end, size_t(3));
332 ASSERT_EQ(node->hint_attributes[1]->type,
334 ASSERT_EQ(std::string(node->hint_attributes[1]->locale->locale),
335 "fr");
336 }
337
338 // Verify value
339 {
340 ASSERT_EQ(std::string(node->value), "42");
341 ASSERT_EQ(node->value_attribute_count, size_t(1));
342
343 ASSERT_EQ(node->value_attributes[0]->start, size_t(0));
344 ASSERT_EQ(node->value_attributes[0]->end, size_t(2));
345 ASSERT_EQ(node->value_attributes[0]->type,
347 ASSERT_EQ(std::string(node->value_attributes[0]->locale->locale),
348 "en-US");
349 }
350
351 // Verify increased value
352 {
353 ASSERT_EQ(std::string(node->increased_value), "43");
354 ASSERT_EQ(node->increased_value_attribute_count, size_t(2));
355
356 ASSERT_EQ(node->increased_value_attributes[0]->start, size_t(0));
357 ASSERT_EQ(node->increased_value_attributes[0]->end, size_t(1));
358 ASSERT_EQ(node->increased_value_attributes[0]->type,
360
361 ASSERT_EQ(node->increased_value_attributes[1]->start, size_t(1));
362 ASSERT_EQ(node->increased_value_attributes[1]->end, size_t(2));
363 ASSERT_EQ(node->increased_value_attributes[1]->type,
365 }
366
367 // Verify decreased value
368 {
369 ASSERT_EQ(std::string(node->decreased_value), "41");
370 ASSERT_EQ(node->decreased_value_attribute_count, size_t(0));
371 ASSERT_EQ(node->decreased_value_attributes, nullptr);
372 }
373
374 semantics_update_latch.Signal();
375 });
376
378 builder.SetSoftwareRendererConfig();
379 builder.SetDartEntrypoint("a11y_string_attributes");
380
381 auto engine = builder.LaunchEngine();
382 ASSERT_TRUE(engine.is_valid());
383
384 // 1: Enable semantics.
387
388 // 2: Wait for semantics update callback on platform (current) thread.
389 signal_native_latch.Wait();
391 semantics_update_latch.Wait();
392#endif // OS_FUCHSIA
393}
394
395TEST_F(EmbedderA11yTest, A11yTreeIsConsistentUsingV2Callbacks) {
396#if defined(OS_FUCHSIA)
397 GTEST_SKIP() << "This test crashes on Fuchsia. https://fxbug.dev/87493 ";
398#else
399
400 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
401
402 fml::AutoResetWaitableEvent signal_native_latch;
403
404 // Called by the Dart text fixture on the UI thread to signal that the C++
405 // unittest should resume.
406 context.AddNativeCallback(
407 "SignalNativeTest",
408 CREATE_NATIVE_ENTRY(([&signal_native_latch](Dart_NativeArguments) {
409 signal_native_latch.Signal();
410 })));
411
412 // Called by test fixture on UI thread to pass data back to this test.
413 NativeEntry notify_semantics_enabled_callback;
414 context.AddNativeCallback(
415 "NotifySemanticsEnabled",
417 ([&notify_semantics_enabled_callback](Dart_NativeArguments args) {
418 ASSERT_NE(notify_semantics_enabled_callback, nullptr);
419 notify_semantics_enabled_callback(args);
420 })));
421
422 NativeEntry notify_accessibility_features_callback;
423 context.AddNativeCallback(
424 "NotifyAccessibilityFeatures",
426 [&notify_accessibility_features_callback](Dart_NativeArguments args) {
427 ASSERT_NE(notify_accessibility_features_callback, nullptr);
428 notify_accessibility_features_callback(args);
429 })));
430
431 NativeEntry notify_semantics_action_callback;
432 context.AddNativeCallback(
433 "NotifySemanticsAction",
435 ([&notify_semantics_action_callback](Dart_NativeArguments args) {
436 ASSERT_NE(notify_semantics_action_callback, nullptr);
437 notify_semantics_action_callback(args);
438 })));
439
440 fml::AutoResetWaitableEvent semantics_update_latch;
441 context.SetSemanticsUpdateCallback([&](const FlutterSemanticsUpdate* update) {
442 ASSERT_EQ(size_t(4), update->nodes_count);
443 ASSERT_EQ(size_t(1), update->custom_actions_count);
444
445 for (size_t i = 0; i < update->nodes_count; i++) {
446 const FlutterSemanticsNode* node = update->nodes + i;
447
448 ASSERT_EQ(1.0, node->transform.scaleX);
449 ASSERT_EQ(2.0, node->transform.skewX);
450 ASSERT_EQ(3.0, node->transform.transX);
451 ASSERT_EQ(4.0, node->transform.skewY);
452 ASSERT_EQ(5.0, node->transform.scaleY);
453 ASSERT_EQ(6.0, node->transform.transY);
454 ASSERT_EQ(7.0, node->transform.pers0);
455 ASSERT_EQ(8.0, node->transform.pers1);
456 ASSERT_EQ(9.0, node->transform.pers2);
457 ASSERT_EQ(std::strncmp(kTooltip, node->tooltip, sizeof(kTooltip) - 1), 0);
458
459 if (node->id == 128) {
460 ASSERT_EQ(0x3f3, node->platform_view_id);
461 } else {
462 ASSERT_NE(kFlutterSemanticsNodeIdBatchEnd, node->id);
463 ASSERT_EQ(0, node->platform_view_id);
464 }
465 }
466
467 semantics_update_latch.Signal();
468 });
469
471 builder.SetSoftwareRendererConfig();
472 builder.SetDartEntrypoint("a11y_main");
473
474 auto engine = builder.LaunchEngine();
475 ASSERT_TRUE(engine.is_valid());
476
477 // 1: Wait for initial notifySemanticsEnabled(false).
478 fml::AutoResetWaitableEvent notify_semantics_enabled_latch;
479 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
480 Dart_Handle exception = nullptr;
481 bool enabled =
483 ASSERT_FALSE(enabled);
484 notify_semantics_enabled_latch.Signal();
485 };
486 notify_semantics_enabled_latch.Wait();
487
488 // Prepare notifyAccessibilityFeatures callback.
489 fml::AutoResetWaitableEvent notify_features_latch;
490 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
491 Dart_Handle exception = nullptr;
492 bool enabled =
494 ASSERT_FALSE(enabled);
495 notify_features_latch.Signal();
496 };
497
498 // 2: Enable semantics. Wait for notifySemanticsEnabled(true).
499 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_2;
500 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
501 Dart_Handle exception = nullptr;
502 bool enabled =
504 ASSERT_TRUE(enabled);
505 notify_semantics_enabled_latch_2.Signal();
506 };
509 notify_semantics_enabled_latch_2.Wait();
510
511 // 3: Wait for notifyAccessibilityFeatures (reduce_motion == false)
512 notify_features_latch.Wait();
513
514 // 4: Wait for notifyAccessibilityFeatures (reduce_motion == true)
515 fml::AutoResetWaitableEvent notify_features_latch_2;
516 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
517 Dart_Handle exception = nullptr;
518 bool enabled =
520 ASSERT_TRUE(enabled);
521 notify_features_latch_2.Signal();
522 };
526 notify_features_latch_2.Wait();
527
528 // 5: Wait for UpdateSemantics callback on platform (current) thread.
529 signal_native_latch.Wait();
531 semantics_update_latch.Wait();
532
533 // 6: Dispatch a tap to semantics node 42. Wait for NotifySemanticsAction.
534 fml::AutoResetWaitableEvent notify_semantics_action_latch;
535 notify_semantics_action_callback = [&](Dart_NativeArguments args) {
536 Dart_Handle exception = nullptr;
537 int64_t node_id =
539 ASSERT_EQ(42, node_id);
540
541 int64_t action_id =
543 ASSERT_EQ(static_cast<int32_t>(flutter::SemanticsAction::kTap), action_id);
544
545 std::vector<int64_t> semantic_args =
547 exception);
548 ASSERT_THAT(semantic_args, ElementsAre(2, 1));
549 notify_semantics_action_latch.Signal();
550 };
551 std::vector<uint8_t> bytes({2, 1});
553 engine.get(), 42, kFlutterSemanticsActionTap, &bytes[0], bytes.size());
555 notify_semantics_action_latch.Wait();
556
557 // 7: Disable semantics. Wait for NotifySemanticsEnabled(false).
558 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_3;
559 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
560 Dart_Handle exception = nullptr;
561 bool enabled =
563 ASSERT_FALSE(enabled);
564 notify_semantics_enabled_latch_3.Signal();
565 };
568 notify_semantics_enabled_latch_3.Wait();
569#endif // OS_FUCHSIA
570}
571
572TEST_F(EmbedderA11yTest, A11yTreeIsConsistentUsingV1Callbacks) {
573 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
574
575 fml::AutoResetWaitableEvent signal_native_latch;
576
577 // Called by the Dart text fixture on the UI thread to signal that the C++
578 // unittest should resume.
579 context.AddNativeCallback(
580 "SignalNativeTest",
581 CREATE_NATIVE_ENTRY(([&signal_native_latch](Dart_NativeArguments) {
582 signal_native_latch.Signal();
583 })));
584
585 // Called by test fixture on UI thread to pass data back to this test.
586 NativeEntry notify_semantics_enabled_callback;
587 context.AddNativeCallback(
588 "NotifySemanticsEnabled",
590 ([&notify_semantics_enabled_callback](Dart_NativeArguments args) {
591 ASSERT_NE(notify_semantics_enabled_callback, nullptr);
592 notify_semantics_enabled_callback(args);
593 })));
594
595 NativeEntry notify_accessibility_features_callback;
596 context.AddNativeCallback(
597 "NotifyAccessibilityFeatures",
599 [&notify_accessibility_features_callback](Dart_NativeArguments args) {
600 ASSERT_NE(notify_accessibility_features_callback, nullptr);
601 notify_accessibility_features_callback(args);
602 })));
603
604 NativeEntry notify_semantics_action_callback;
605 context.AddNativeCallback(
606 "NotifySemanticsAction",
608 ([&notify_semantics_action_callback](Dart_NativeArguments args) {
609 ASSERT_NE(notify_semantics_action_callback, nullptr);
610 notify_semantics_action_callback(args);
611 })));
612
613 fml::AutoResetWaitableEvent semantics_node_latch;
614 fml::AutoResetWaitableEvent semantics_action_latch;
615
616 int node_batch_end_count = 0;
617 int action_batch_end_count = 0;
618
619 int node_count = 0;
620 context.SetSemanticsNodeCallback([&](const FlutterSemanticsNode* node) {
621 if (node->id == kFlutterSemanticsNodeIdBatchEnd) {
622 ++node_batch_end_count;
623 semantics_node_latch.Signal();
624 } else {
625 // Batches should be completed after all nodes are received.
626 ASSERT_EQ(0, node_batch_end_count);
627 ASSERT_EQ(0, action_batch_end_count);
628
629 ++node_count;
630 ASSERT_EQ(1.0, node->transform.scaleX);
631 ASSERT_EQ(2.0, node->transform.skewX);
632 ASSERT_EQ(3.0, node->transform.transX);
633 ASSERT_EQ(4.0, node->transform.skewY);
634 ASSERT_EQ(5.0, node->transform.scaleY);
635 ASSERT_EQ(6.0, node->transform.transY);
636 ASSERT_EQ(7.0, node->transform.pers0);
637 ASSERT_EQ(8.0, node->transform.pers1);
638 ASSERT_EQ(9.0, node->transform.pers2);
639 ASSERT_EQ(std::strncmp(kTooltip, node->tooltip, sizeof(kTooltip) - 1), 0);
640
641 if (node->id == 128) {
642 ASSERT_EQ(0x3f3, node->platform_view_id);
643 } else {
644 ASSERT_EQ(0, node->platform_view_id);
645 }
646 }
647 });
648
649 int action_count = 0;
650 context.SetSemanticsCustomActionCallback(
653 ++action_batch_end_count;
654 semantics_action_latch.Signal();
655 } else {
656 // Batches should be completed after all actions are received.
657 ASSERT_EQ(0, node_batch_end_count);
658 ASSERT_EQ(0, action_batch_end_count);
659
660 ++action_count;
661 }
662 });
663
665 builder.SetSoftwareRendererConfig();
666 builder.SetDartEntrypoint("a11y_main");
667
668 auto engine = builder.LaunchEngine();
669 ASSERT_TRUE(engine.is_valid());
670
671 // 1: Wait for initial notifySemanticsEnabled(false).
672 fml::AutoResetWaitableEvent notify_semantics_enabled_latch;
673 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
674 Dart_Handle exception = nullptr;
675 bool enabled =
677 ASSERT_FALSE(enabled);
678 notify_semantics_enabled_latch.Signal();
679 };
680 notify_semantics_enabled_latch.Wait();
681
682 // Prepare notifyAccessibilityFeatures callback.
683 fml::AutoResetWaitableEvent notify_features_latch;
684 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
685 Dart_Handle exception = nullptr;
686 bool enabled =
688 ASSERT_FALSE(enabled);
689 notify_features_latch.Signal();
690 };
691
692 // 2: Enable semantics. Wait for notifySemanticsEnabled(true).
693 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_2;
694 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
695 Dart_Handle exception = nullptr;
696 bool enabled =
698 ASSERT_TRUE(enabled);
699 notify_semantics_enabled_latch_2.Signal();
700 };
703 notify_semantics_enabled_latch_2.Wait();
704
705 // 3: Wait for notifyAccessibilityFeatures (reduce_motion == false)
706 notify_features_latch.Wait();
707
708 // 4: Wait for notifyAccessibilityFeatures (reduce_motion == true)
709 fml::AutoResetWaitableEvent notify_features_latch_2;
710 notify_accessibility_features_callback = [&](Dart_NativeArguments args) {
711 Dart_Handle exception = nullptr;
712 bool enabled =
714 ASSERT_TRUE(enabled);
715 notify_features_latch_2.Signal();
716 };
720 notify_features_latch_2.Wait();
721
722 // 5: Wait for UpdateSemantics callback on platform (current) thread.
723 signal_native_latch.Wait();
725 semantics_node_latch.Wait();
726 semantics_action_latch.Wait();
727 ASSERT_EQ(4, node_count);
728 ASSERT_EQ(1, node_batch_end_count);
729 ASSERT_EQ(1, action_count);
730 ASSERT_EQ(1, action_batch_end_count);
731
732 // 6: Dispatch a tap to semantics node 42. Wait for NotifySemanticsAction.
733 fml::AutoResetWaitableEvent notify_semantics_action_latch;
734 notify_semantics_action_callback = [&](Dart_NativeArguments args) {
735 Dart_Handle exception = nullptr;
736 int64_t node_id =
738 ASSERT_EQ(42, node_id);
739
740 int64_t action_id =
742 ASSERT_EQ(static_cast<int32_t>(flutter::SemanticsAction::kTap), action_id);
743
744 std::vector<int64_t> semantic_args =
746 exception);
747 ASSERT_THAT(semantic_args, ElementsAre(2, 1));
748 notify_semantics_action_latch.Signal();
749 };
750 std::vector<uint8_t> bytes({2, 1});
752 engine.get(), 42, kFlutterSemanticsActionTap, &bytes[0], bytes.size());
754 notify_semantics_action_latch.Wait();
755
756 // 7: Disable semantics. Wait for NotifySemanticsEnabled(false).
757 fml::AutoResetWaitableEvent notify_semantics_enabled_latch_3;
758 notify_semantics_enabled_callback = [&](Dart_NativeArguments args) {
759 Dart_Handle exception = nullptr;
760 bool enabled =
762 ASSERT_FALSE(enabled);
763 notify_semantics_enabled_latch_3.Signal();
764 };
767 notify_semantics_enabled_latch_3.Wait();
768}
769
770} // namespace testing
771} // namespace flutter
772
773// NOLINTEND(clang-analyzer-core.StackAddressEscape)
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
void RunExpiredTasksNow()
Definition: message_loop.cc:72
struct _Dart_Handle * Dart_Handle
Definition: dart_api.h:258
struct _Dart_NativeArguments * Dart_NativeArguments
Definition: dart_api.h:3019
const int32_t kFlutterSemanticsNodeIdBatchEnd
Definition: embedder.cc:105
const int32_t kFlutterSemanticsCustomActionIdBatchEnd
Definition: embedder.cc:106
FlutterEngineResult FlutterEngineDispatchSemanticsAction(FLUTTER_API_SYMBOL(FlutterEngine) engine, uint64_t node_id, FlutterSemanticsAction action, const uint8_t *data, size_t data_length)
Dispatch a semantics action to the specified semantics node.
Definition: embedder.cc:2855
FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterAccessibilityFeature flags)
Sets additional accessibility features.
Definition: embedder.cc:2841
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled)
Enable or disable accessibility semantics.
Definition: embedder.cc:2827
@ kFlutterAccessibilityFeatureReduceMotion
Definition: embedder.h:103
@ kSuccess
Definition: embedder.h:73
@ kFlutterSemanticsActionTap
Definition: embedder.h:116
@ kSpellOut
Definition: embedder.h:1202
@ kLocale
Definition: embedder.h:1204
FlutterEngine engine
Definition: main.cc:68
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
TEST_F(DisplayListTest, Defaults)
testing::EmbedderTest EmbedderTest
static constexpr char kTooltip[]
std::function< void(Dart_NativeArguments)> NativeEntry
Definition: update.py:1
const char * tooltip
A textual tooltip attached to the node.
Definition: embedder.h:1397
FlutterPlatformViewIdentifier platform_view_id
Definition: embedder.h:1395
int32_t id
The unique identifier for this node.
Definition: embedder.h:1337
FlutterTransformation transform
Definition: embedder.h:1381
FlutterTransformation transform
Definition: embedder.h:1306
const char * tooltip
A textual tooltip attached to the node.
Definition: embedder.h:1322
FlutterPlatformViewIdentifier platform_view_id
Definition: embedder.h:1320
int32_t id
The unique identifier for this node.
Definition: embedder.h:1262
A batch of updates to semantics nodes and custom actions.
Definition: embedder.h:1504
double transY
vertical translation
Definition: embedder.h:289
double pers2
perspective scale factor
Definition: embedder.h:295
double skewX
horizontal skew factor
Definition: embedder.h:281
double pers0
input x-axis perspective factor
Definition: embedder.h:291
double scaleX
horizontal scale factor
Definition: embedder.h:279
double skewY
vertical skew factor
Definition: embedder.h:285
double scaleY
vertical scale factor
Definition: embedder.h:287
double pers1
input y-axis perspective factor
Definition: embedder.h:293
double transX
horizontal translation
Definition: embedder.h:283
#define CREATE_NATIVE_ENTRY(native_entry)
void * user_data