Flutter Engine
 
Loading...
Searching...
No Matches
embedder_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#define FML_USED_ON_EMBEDDER
6
7#include <string>
8#include <utility>
9#include <vector>
10
11#include "embedder.h"
12#include "embedder_engine.h"
15#include "flutter/fml/file.h"
17#include "flutter/fml/mapping.h"
19#include "flutter/fml/paths.h"
23#include "flutter/fml/thread.h"
34#include "third_party/skia/include/core/SkSurface.h"
36
37#if defined(FML_OS_MACOSX)
38#include <pthread.h>
39#endif
40
41// CREATE_NATIVE_ENTRY is leaky by design
42// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
43
44namespace {
45
46static uint64_t NanosFromEpoch(int millis_from_now) {
47 const auto now = fml::TimePoint::Now();
48 const auto delta = fml::TimeDelta::FromMilliseconds(millis_from_now);
49 return (now + delta).ToEpochDelta().ToNanoseconds();
50}
51
52} // namespace
53
54namespace flutter {
55namespace testing {
56
57using EmbedderTest = testing::EmbedderTest;
58
59TEST(EmbedderTestNoFixture, MustNotRunWithInvalidArgs) {
63 auto engine = builder.LaunchEngine();
64 ASSERT_FALSE(engine.is_valid());
65}
66
67TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
68 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
70 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
71 EmbedderConfigBuilder builder(context);
72 builder.SetSurface(DlISize(1, 1));
73 auto engine = builder.LaunchEngine();
74 ASSERT_TRUE(engine.is_valid());
75 // Wait for the root isolate to launch.
76 latch.Wait();
77 engine.reset();
78}
79
80// TODO(41999): Disabled because flaky.
81TEST_F(EmbedderTest, DISABLED_CanLaunchAndShutdownMultipleTimes) {
82 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
83 EmbedderConfigBuilder builder(context);
84 builder.SetSurface(DlISize(1, 1));
85 for (size_t i = 0; i < 3; ++i) {
86 auto engine = builder.LaunchEngine();
87 ASSERT_TRUE(engine.is_valid());
88 FML_LOG(INFO) << "Engine launch count: " << i + 1;
89 }
90}
91
92TEST_F(EmbedderTest, CanInvokeCustomEntrypoint) {
93 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
94 static fml::AutoResetWaitableEvent latch;
95 Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) {
96 latch.Signal();
97 };
98 context.AddNativeCallback("SayHiFromCustomEntrypoint", entrypoint);
99 EmbedderConfigBuilder builder(context);
100 builder.SetSurface(DlISize(1, 1));
101 builder.SetDartEntrypoint("customEntrypoint");
102 auto engine = builder.LaunchEngine();
103 latch.Wait();
104 ASSERT_TRUE(engine.is_valid());
105}
106
107TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
108 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
109
113
114 // Can be defined separately.
115 auto entry1 = [&latch1](Dart_NativeArguments args) {
116 FML_LOG(INFO) << "In Callback 1";
117 latch1.Signal();
118 };
119 auto native_entry1 = CREATE_NATIVE_ENTRY(entry1);
120 context.AddNativeCallback("SayHiFromCustomEntrypoint1", native_entry1);
121
122 // Can be wrapped in the args.
123 auto entry2 = [&latch2](Dart_NativeArguments args) {
124 FML_LOG(INFO) << "In Callback 2";
125 latch2.Signal();
126 };
127 context.AddNativeCallback("SayHiFromCustomEntrypoint2",
128 CREATE_NATIVE_ENTRY(entry2));
129
130 // Everything can be inline.
131 context.AddNativeCallback(
132 "SayHiFromCustomEntrypoint3",
133 CREATE_NATIVE_ENTRY([&latch3](Dart_NativeArguments args) {
134 FML_LOG(INFO) << "In Callback 3";
135 latch3.Signal();
136 }));
137
138 EmbedderConfigBuilder builder(context);
139 builder.SetSurface(DlISize(1, 1));
140 builder.SetDartEntrypoint("customEntrypoint1");
141 auto engine = builder.LaunchEngine();
142 latch1.Wait();
143 latch2.Wait();
144 latch3.Wait();
145 ASSERT_TRUE(engine.is_valid());
146}
147
148TEST_F(EmbedderTest, CanTerminateCleanly) {
149 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
150 EmbedderConfigBuilder builder(context);
151 builder.SetSurface(DlISize(1, 1));
152 builder.SetDartEntrypoint("terminateExitCodeHandler");
153 auto engine = builder.LaunchEngine();
154 ASSERT_TRUE(engine.is_valid());
155}
156
157TEST_F(EmbedderTest, ExecutableNameNotNull) {
158 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
159
160 // Supply a callback to Dart for the test fixture to pass Platform.executable
161 // back to us.
163 context.AddNativeCallback(
164 "NotifyStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
165 const auto dart_string = tonic::DartConverter<std::string>::FromDart(
166 Dart_GetNativeArgument(args, 0));
167 EXPECT_EQ("/path/to/binary", dart_string);
168 latch.Signal();
169 }));
170
171 EmbedderConfigBuilder builder(context);
172 builder.SetSurface(DlISize(1, 1));
173 builder.SetDartEntrypoint("executableNameNotNull");
174 builder.SetExecutableName("/path/to/binary");
175 auto engine = builder.LaunchEngine();
176 latch.Wait();
177}
178
179TEST_F(EmbedderTest, ImplicitViewNotNull) {
180 // TODO(loicsharma): Update this test when embedders can opt-out
181 // of the implicit view.
182 // See: https://github.com/flutter/flutter/issues/120306
183 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
184
185 bool implicitViewNotNull = false;
187 context.AddNativeCallback(
188 "NotifyBoolValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
189 implicitViewNotNull = tonic::DartConverter<bool>::FromDart(
190 Dart_GetNativeArgument(args, 0));
191 latch.Signal();
192 }));
193
194 EmbedderConfigBuilder builder(context);
195 builder.SetSurface(DlISize(1, 1));
196 builder.SetDartEntrypoint("implicitViewNotNull");
197 auto engine = builder.LaunchEngine();
198 latch.Wait();
199
200 EXPECT_TRUE(implicitViewNotNull);
201}
202
203std::atomic_size_t EmbedderTestTaskRunner::sEmbedderTaskRunnerIdentifiers = {};
204
205TEST_F(EmbedderTest, CanSpecifyCustomUITaskRunner) {
206 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
207 auto ui_task_runner = CreateNewThread("test_ui_thread");
208 auto platform_task_runner = CreateNewThread("test_platform_thread");
209 static std::mutex engine_mutex;
211
212 EmbedderTestTaskRunner test_ui_task_runner(
213 ui_task_runner, [&](FlutterTask task) {
214 std::scoped_lock lock(engine_mutex);
215 if (!engine.is_valid()) {
216 return;
217 }
218 FlutterEngineRunTask(engine.get(), &task);
219 });
220 EmbedderTestTaskRunner test_platform_task_runner(
221 platform_task_runner, [&](FlutterTask task) {
222 std::scoped_lock lock(engine_mutex);
223 if (!engine.is_valid()) {
224 return;
225 }
226 FlutterEngineRunTask(engine.get(), &task);
227 });
228
229 fml::AutoResetWaitableEvent signal_latch_ui;
230 fml::AutoResetWaitableEvent signal_latch_platform;
231
232 context.AddNativeCallback(
233 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
234 // Assert that the UI isolate is running on platform thread.
235 ASSERT_TRUE(ui_task_runner->RunsTasksOnCurrentThread());
236 signal_latch_ui.Signal();
237 }));
238
239 platform_task_runner->PostTask([&]() {
240 EmbedderConfigBuilder builder(context);
241 const auto ui_task_runner_description =
242 test_ui_task_runner.GetFlutterTaskRunnerDescription();
243 const auto platform_task_runner_description =
244 test_platform_task_runner.GetFlutterTaskRunnerDescription();
245 builder.SetSurface(DlISize(1, 1));
246 builder.SetUITaskRunner(&ui_task_runner_description);
247 builder.SetPlatformTaskRunner(&platform_task_runner_description);
248 builder.SetDartEntrypoint("canSpecifyCustomUITaskRunner");
250 [&](const FlutterPlatformMessage* message) {
251 ASSERT_TRUE(platform_task_runner->RunsTasksOnCurrentThread());
252 signal_latch_platform.Signal();
253 });
254 {
255 std::scoped_lock lock(engine_mutex);
256 engine = builder.InitializeEngine();
257 }
259 ASSERT_TRUE(engine.is_valid());
260 });
261 signal_latch_ui.Wait();
262 signal_latch_platform.Wait();
263
265 platform_task_runner->PostTask([&] {
266 engine.reset();
267 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
268 });
269 kill_latch.Wait();
270}
271
272TEST_F(EmbedderTest, IgnoresStaleTasks) {
273 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
274 auto ui_task_runner = CreateNewThread("test_ui_thread");
275 auto platform_task_runner = CreateNewThread("test_platform_thread");
276 static std::mutex engine_mutex;
278 FlutterEngine engine_ptr;
279
280 EmbedderTestTaskRunner test_ui_task_runner(
281 ui_task_runner, [&](FlutterTask task) {
282 // The check for engine.is_valid() is intentionally absent here.
283 // FlutterEngineRunTask must be able to detect and ignore stale tasks
284 // without crashing even if the engine pointer is not null.
285 // Because the engine is destroyed on platform thread,
286 // relying solely on engine.is_valid() in UI thread is not safe.
287 FlutterEngineRunTask(engine_ptr, &task);
288 });
289 EmbedderTestTaskRunner test_platform_task_runner(
290 platform_task_runner, [&](FlutterTask task) {
291 std::scoped_lock lock(engine_mutex);
292 if (!engine.is_valid()) {
293 return;
294 }
295 FlutterEngineRunTask(engine.get(), &task);
296 });
297
299
300 platform_task_runner->PostTask([&]() {
301 EmbedderConfigBuilder builder(context);
302 const auto ui_task_runner_description =
303 test_ui_task_runner.GetFlutterTaskRunnerDescription();
304 const auto platform_task_runner_description =
305 test_platform_task_runner.GetFlutterTaskRunnerDescription();
306 builder.SetUITaskRunner(&ui_task_runner_description);
307 builder.SetPlatformTaskRunner(&platform_task_runner_description);
308 {
309 std::scoped_lock lock(engine_mutex);
310 engine = builder.InitializeEngine();
311 }
312 init_latch.Signal();
313 });
314
315 init_latch.Wait();
316 engine_ptr = engine.get();
317
318 auto flutter_engine = reinterpret_cast<EmbedderEngine*>(engine.get());
319
320 // Schedule task on UI thread that will likely run after the engine has shut
321 // down.
324
326 platform_task_runner->PostTask([&] {
327 engine.reset();
328 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
329 });
330 kill_latch.Wait();
331
332 // Ensure that the schedule task indeed runs.
333 kill_latch.Reset();
334 ui_task_runner->PostDelayedTask([&]() { kill_latch.Signal(); },
336 kill_latch.Wait();
337}
338
339TEST_F(EmbedderTest, MergedPlatformUIThread) {
340 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
341 auto task_runner = CreateNewThread("test_thread");
343
344 EmbedderTestTaskRunner test_task_runner(task_runner, [&](FlutterTask task) {
345 if (!engine.is_valid()) {
346 return;
347 }
348 FlutterEngineRunTask(engine.get(), &task);
349 });
350
351 fml::AutoResetWaitableEvent signal_latch_ui;
352 fml::AutoResetWaitableEvent signal_latch_platform;
353
354 context.AddNativeCallback(
355 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
356 // Assert that the UI isolate is running on platform thread.
357 ASSERT_TRUE(task_runner->RunsTasksOnCurrentThread());
358 signal_latch_ui.Signal();
359 }));
360
361 task_runner->PostTask([&]() {
362 EmbedderConfigBuilder builder(context);
363 const auto task_runner_description =
364 test_task_runner.GetFlutterTaskRunnerDescription();
365 builder.SetSurface(DlISize(1, 1));
366 builder.SetUITaskRunner(&task_runner_description);
367 builder.SetPlatformTaskRunner(&task_runner_description);
368 builder.SetDartEntrypoint("mergedPlatformUIThread");
370 [&](const FlutterPlatformMessage* message) {
371 ASSERT_TRUE(task_runner->RunsTasksOnCurrentThread());
372 signal_latch_platform.Signal();
373 });
374 engine = builder.LaunchEngine();
375 ASSERT_TRUE(engine.is_valid());
376 });
377 signal_latch_ui.Wait();
378 signal_latch_platform.Wait();
379
381 task_runner->PostTask([&] {
382 engine.reset();
383 task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
384 });
385 kill_latch.Wait();
386}
387
388TEST_F(EmbedderTest, UITaskRunnerFlushesMicrotasks) {
389 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
390 auto ui_task_runner = CreateNewThread("test_ui_thread");
392
393 EmbedderTestTaskRunner test_task_runner(
394 // Assert that the UI isolate is running on platform thread.
395 ui_task_runner, [&](FlutterTask task) {
396 if (!engine.is_valid()) {
397 return;
398 }
399 FlutterEngineRunTask(engine.get(), &task);
400 });
401
402 fml::AutoResetWaitableEvent signal_latch;
403
404 context.AddNativeCallback(
405 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
406 ASSERT_TRUE(ui_task_runner->RunsTasksOnCurrentThread());
407 signal_latch.Signal();
408 }));
409
410 ui_task_runner->PostTask([&]() {
411 EmbedderConfigBuilder builder(context);
412 const auto task_runner_description =
413 test_task_runner.GetFlutterTaskRunnerDescription();
414 builder.SetSurface(DlISize(1, 1));
415 builder.SetUITaskRunner(&task_runner_description);
416 builder.SetDartEntrypoint("uiTaskRunnerFlushesMicrotasks");
417 engine = builder.LaunchEngine();
418 ASSERT_TRUE(engine.is_valid());
419 });
420 signal_latch.Wait();
421
423 ui_task_runner->PostTask([&] {
424 engine.reset();
425 ui_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
426 });
427 kill_latch.Wait();
428}
429
430TEST_F(EmbedderTest, CanSpecifyCustomPlatformTaskRunner) {
431 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
433
434 // Run the test on its own thread with a message loop so that it can safely
435 // pump its event loop while we wait for all the conditions to be checked.
436 auto platform_task_runner = CreateNewThread("test_platform_thread");
437 static std::mutex engine_mutex;
438 static bool signaled_once = false;
439 static std::atomic<bool> destruction_callback_called = false;
441
442 EmbedderTestTaskRunner test_task_runner(
443 platform_task_runner, [&](FlutterTask task) {
444 std::scoped_lock lock(engine_mutex);
445 if (!engine.is_valid()) {
446 return;
447 }
448 // There may be multiple tasks posted but we only need to check
449 // assertions once.
450 if (signaled_once) {
451 FlutterEngineRunTask(engine.get(), &task);
452 return;
453 }
454
455 signaled_once = true;
456 ASSERT_TRUE(engine.is_valid());
457 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
458 latch.Signal();
459 });
460 test_task_runner.SetDestructionCallback(
461 [](void* user_data) { destruction_callback_called = true; });
462
463 platform_task_runner->PostTask([&]() {
464 EmbedderConfigBuilder builder(context);
465 const auto task_runner_description =
466 test_task_runner.GetFlutterTaskRunnerDescription();
467 builder.SetSurface(DlISize(1, 1));
468 builder.SetPlatformTaskRunner(&task_runner_description);
469 builder.SetDartEntrypoint("invokePlatformTaskRunner");
470 std::scoped_lock lock(engine_mutex);
471 engine = builder.LaunchEngine();
472 ASSERT_TRUE(engine.is_valid());
473 });
474
475 // Signaled when all the assertions are checked.
476 latch.Wait();
477 ASSERT_TRUE(engine.is_valid());
478
479 // Since the engine was started on its own thread, it must be killed there as
480 // well.
482 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
483 std::scoped_lock lock(engine_mutex);
484 engine.reset();
485
486 // There may still be pending tasks on the platform thread that were queued
487 // by the test_task_runner. Signal the latch after these tasks have been
488 // consumed.
489 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
490 }));
491 kill_latch.Wait();
492
493 ASSERT_TRUE(signaled_once);
494 signaled_once = false;
495
496 ASSERT_TRUE(destruction_callback_called);
497 destruction_callback_called = false;
498}
499
500TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
501 auto point1 = fml::TimePoint::FromEpochDelta(
503 auto point2 = fml::TimePoint::Now();
504
505 ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1));
506}
507
508TEST_F(EmbedderTest, CanReloadSystemFonts) {
509 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
510 EmbedderConfigBuilder builder(context);
511 builder.SetSurface(DlISize(1, 1));
512 auto engine = builder.LaunchEngine();
513 ASSERT_TRUE(engine.is_valid());
514
515 auto result = FlutterEngineReloadSystemFonts(engine.get());
516 ASSERT_EQ(result, kSuccess);
517}
518
519TEST_F(EmbedderTest, IsolateServiceIdSent) {
520 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
522
523 fml::Thread thread;
525 std::string isolate_message;
526
527 thread.GetTaskRunner()->PostTask([&]() {
528 EmbedderConfigBuilder builder(context);
529 builder.SetSurface(DlISize(1, 1));
530 builder.SetDartEntrypoint("main");
532 [&](const FlutterPlatformMessage* message) {
533 if (strcmp(message->channel, "flutter/isolate") == 0) {
534 isolate_message = {reinterpret_cast<const char*>(message->message),
535 message->message_size};
536 latch.Signal();
537 }
538 });
539 engine = builder.LaunchEngine();
540 ASSERT_TRUE(engine.is_valid());
541 });
542
543 // Wait for the isolate ID message and check its format.
544 latch.Wait();
545 ASSERT_EQ(isolate_message.find("isolates/"), 0ul);
546
547 // Since the engine was started on its own thread, it must be killed there as
548 // well.
550 thread.GetTaskRunner()->PostTask(
551 fml::MakeCopyable([&engine, &kill_latch]() mutable {
552 engine.reset();
553 kill_latch.Signal();
554 }));
555 kill_latch.Wait();
556}
557
558//------------------------------------------------------------------------------
559/// Creates a platform message response callbacks, does NOT send them, and
560/// immediately collects the same.
561///
562TEST_F(EmbedderTest, CanCreateAndCollectCallbacks) {
563 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
564 EmbedderConfigBuilder builder(context);
565 builder.SetSurface(DlISize(1, 1));
566 builder.SetDartEntrypoint("platform_messages_response");
567 context.AddNativeCallback(
568 "SignalNativeTest",
569 CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
570
571 auto engine = builder.LaunchEngine();
572 ASSERT_TRUE(engine.is_valid());
573
574 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
575 auto callback = [](const uint8_t* data, size_t size,
576 void* user_data) -> void {};
578 engine.get(), callback, nullptr, &response_handle);
579 ASSERT_EQ(result, kSuccess);
580 ASSERT_NE(response_handle, nullptr);
581
583 response_handle);
584 ASSERT_EQ(result, kSuccess);
585}
586
587//------------------------------------------------------------------------------
588/// Sends platform messages to Dart code than simply echoes the contents of the
589/// message back to the embedder. The embedder registers a native callback to
590/// intercept that message.
591///
592TEST_F(EmbedderTest, PlatformMessagesCanReceiveResponse) {
593 struct Captures {
595 std::thread::id thread_id;
596 };
597 Captures captures;
598
599 CreateNewThread()->PostTask([&]() {
600 captures.thread_id = std::this_thread::get_id();
601 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
602 EmbedderConfigBuilder builder(context);
603 builder.SetSurface(DlISize(1, 1));
604 builder.SetDartEntrypoint("platform_messages_response");
605
607 context.AddNativeCallback(
608 "SignalNativeTest",
610 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
611
612 auto engine = builder.LaunchEngine();
613 ASSERT_TRUE(engine.is_valid());
614
615 static std::string kMessageData = "Hello from embedder.";
616
617 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
618 auto callback = [](const uint8_t* data, size_t size,
619 void* user_data) -> void {
620 ASSERT_EQ(size, kMessageData.size());
621 ASSERT_EQ(strncmp(reinterpret_cast<const char*>(kMessageData.data()),
622 reinterpret_cast<const char*>(data), size),
623 0);
624 auto captures = reinterpret_cast<Captures*>(user_data);
625 ASSERT_EQ(captures->thread_id, std::this_thread::get_id());
626 captures->latch.Signal();
627 };
629 engine.get(), callback, &captures, &response_handle);
630 ASSERT_EQ(result, kSuccess);
631
634 message.channel = "test_channel";
635 message.message = reinterpret_cast<const uint8_t*>(kMessageData.data());
636 message.message_size = kMessageData.size();
637 message.response_handle = response_handle;
638
639 ready.Wait();
641 ASSERT_EQ(result, kSuccess);
642
644 response_handle);
645 ASSERT_EQ(result, kSuccess);
646 });
647
648 captures.latch.Wait();
649}
650
651//------------------------------------------------------------------------------
652/// Tests that a platform message can be sent with no response handle. Instead
653/// of the platform message integrity checked via a response handle, a native
654/// callback with the response is invoked to assert integrity.
655///
656TEST_F(EmbedderTest, PlatformMessagesCanBeSentWithoutResponseHandles) {
657 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
658 EmbedderConfigBuilder builder(context);
659 builder.SetSurface(DlISize(1, 1));
660 builder.SetDartEntrypoint("platform_messages_no_response");
661
662 const std::string message_data = "Hello but don't call me back.";
663
665 context.AddNativeCallback(
666 "SignalNativeTest",
668 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
669 context.AddNativeCallback(
670 "SignalNativeMessage",
672 ([&message, &message_data](Dart_NativeArguments args) {
673 auto received_message = tonic::DartConverter<std::string>::FromDart(
674 Dart_GetNativeArgument(args, 0));
675 ASSERT_EQ(received_message, message_data);
676 message.Signal();
677 })));
678
679 auto engine = builder.LaunchEngine();
680
681 ASSERT_TRUE(engine.is_valid());
682 ready.Wait();
683
684 FlutterPlatformMessage platform_message = {};
685 platform_message.struct_size = sizeof(FlutterPlatformMessage);
686 platform_message.channel = "test_channel";
687 platform_message.message =
688 reinterpret_cast<const uint8_t*>(message_data.data());
689 platform_message.message_size = message_data.size();
690 platform_message.response_handle = nullptr; // No response needed.
691
692 auto result =
693 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
694 ASSERT_EQ(result, kSuccess);
695 message.Wait();
696}
697
698//------------------------------------------------------------------------------
699/// Tests that a null platform message can be sent.
700///
701TEST_F(EmbedderTest, NullPlatformMessagesCanBeSent) {
702 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
703 EmbedderConfigBuilder builder(context);
704 builder.SetSurface(DlISize(1, 1));
705 builder.SetDartEntrypoint("null_platform_messages");
706
708 context.AddNativeCallback(
709 "SignalNativeTest",
711 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
712 context.AddNativeCallback(
713 "SignalNativeMessage",
714 CREATE_NATIVE_ENTRY(([&message](Dart_NativeArguments args) {
715 auto received_message = tonic::DartConverter<std::string>::FromDart(
716 Dart_GetNativeArgument(args, 0));
717 ASSERT_EQ("true", received_message);
718 message.Signal();
719 })));
720
721 auto engine = builder.LaunchEngine();
722
723 ASSERT_TRUE(engine.is_valid());
724 ready.Wait();
725
726 FlutterPlatformMessage platform_message = {};
727 platform_message.struct_size = sizeof(FlutterPlatformMessage);
728 platform_message.channel = "test_channel";
729 platform_message.message = nullptr;
730 platform_message.message_size = 0;
731 platform_message.response_handle = nullptr; // No response needed.
732
733 auto result =
734 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
735 ASSERT_EQ(result, kSuccess);
736 message.Wait();
737}
738
739//------------------------------------------------------------------------------
740/// Tests that a null platform message cannot be send if the message_size
741/// isn't equals to 0.
742///
743TEST_F(EmbedderTest, InvalidPlatformMessages) {
744 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
745 EmbedderConfigBuilder builder(context);
746 builder.SetSurface(DlISize(1, 1));
747 auto engine = builder.LaunchEngine();
748
749 ASSERT_TRUE(engine.is_valid());
750
751 FlutterPlatformMessage platform_message = {};
752 platform_message.struct_size = sizeof(FlutterPlatformMessage);
753 platform_message.channel = "test_channel";
754 platform_message.message = nullptr;
755 platform_message.message_size = 1;
756 platform_message.response_handle = nullptr; // No response needed.
757
758 auto result =
759 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
760 ASSERT_EQ(result, kInvalidArguments);
761}
762
763//------------------------------------------------------------------------------
764/// Tests that setting a custom log callback works as expected and defaults to
765/// using tag "flutter".
766TEST_F(EmbedderTest, CanSetCustomLogMessageCallback) {
767 fml::AutoResetWaitableEvent callback_latch;
768 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
769 EmbedderConfigBuilder builder(context);
770 builder.SetDartEntrypoint("custom_logger");
771 builder.SetSurface(DlISize(1, 1));
772 context.SetLogMessageCallback(
773 [&callback_latch](const char* tag, const char* message) {
774 EXPECT_EQ(std::string(tag), "flutter");
775 EXPECT_EQ(std::string(message), "hello world");
776 callback_latch.Signal();
777 });
778 auto engine = builder.LaunchEngine();
779 ASSERT_TRUE(engine.is_valid());
780 callback_latch.Wait();
781}
782
783//------------------------------------------------------------------------------
784/// Tests that setting a custom log tag works.
785TEST_F(EmbedderTest, CanSetCustomLogTag) {
786 fml::AutoResetWaitableEvent callback_latch;
787 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
788 EmbedderConfigBuilder builder(context);
789 builder.SetDartEntrypoint("custom_logger");
790 builder.SetSurface(DlISize(1, 1));
791 builder.SetLogTag("butterfly");
792 context.SetLogMessageCallback(
793 [&callback_latch](const char* tag, const char* message) {
794 EXPECT_EQ(std::string(tag), "butterfly");
795 EXPECT_EQ(std::string(message), "hello world");
796 callback_latch.Signal();
797 });
798 auto engine = builder.LaunchEngine();
799 ASSERT_TRUE(engine.is_valid());
800 callback_latch.Wait();
801}
802
803//------------------------------------------------------------------------------
804/// Asserts behavior of FlutterProjectArgs::shutdown_dart_vm_when_done (which is
805/// set to true by default in these unit-tests).
806///
807TEST_F(EmbedderTest, VMShutsDownWhenNoEnginesInProcess) {
808 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
809 EmbedderConfigBuilder builder(context);
810 builder.SetSurface(DlISize(1, 1));
811 const auto launch_count = DartVM::GetVMLaunchCount();
812
813 {
814 auto engine = builder.LaunchEngine();
815 ASSERT_EQ(launch_count + 1u, DartVM::GetVMLaunchCount());
816 }
817
818 {
819 auto engine = builder.LaunchEngine();
820 ASSERT_EQ(launch_count + 2u, DartVM::GetVMLaunchCount());
821 }
822}
823
824//------------------------------------------------------------------------------
825///
826TEST_F(EmbedderTest, DartEntrypointArgs) {
827 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
828 EmbedderConfigBuilder builder(context);
829 builder.SetSurface(DlISize(1, 1));
830 builder.AddDartEntrypointArgument("foo");
831 builder.AddDartEntrypointArgument("bar");
832 builder.SetDartEntrypoint("dart_entrypoint_args");
833 fml::AutoResetWaitableEvent callback_latch;
834 std::vector<std::string> callback_args;
835 auto nativeArgumentsCallback = [&callback_args,
836 &callback_latch](Dart_NativeArguments args) {
837 Dart_Handle exception = nullptr;
838 callback_args =
840 args, 0, exception);
841 callback_latch.Signal();
842 };
843 context.AddNativeCallback("NativeArgumentsCallback",
844 CREATE_NATIVE_ENTRY(nativeArgumentsCallback));
845 auto engine = builder.LaunchEngine();
846 callback_latch.Wait();
847 ASSERT_EQ(callback_args[0], "foo");
848 ASSERT_EQ(callback_args[1], "bar");
849}
850
851//------------------------------------------------------------------------------
852/// These snapshots may be materialized from symbols and the size field may not
853/// be relevant. Since this information is redundant, engine launch should not
854/// be gated on a non-zero buffer size.
855///
856TEST_F(EmbedderTest, VMAndIsolateSnapshotSizesAreRedundantInAOTMode) {
858 GTEST_SKIP();
859 return;
860 }
861 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
862 EmbedderConfigBuilder builder(context);
863 builder.SetSurface(DlISize(1, 1));
864
865 // The fixture sets this up correctly. Intentionally mess up the args.
870
871 auto engine = builder.LaunchEngine();
872 ASSERT_TRUE(engine.is_valid());
873}
874
875TEST_F(EmbedderTest, CanRenderImplicitView) {
876 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
877
878 EmbedderConfigBuilder builder(context);
879 builder.SetSurface(DlISize(800, 600));
880 builder.SetCompositor();
881 builder.SetDartEntrypoint("render_implicit_view");
882 builder.SetRenderTargetType(
883 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
884
886
887 context.GetCompositor().SetNextPresentCallback(
889 size_t layers_count) {
891 latch.Signal();
892 });
893
894 auto engine = builder.LaunchEngine();
895
896 FlutterWindowMetricsEvent event = {};
897 event.struct_size = sizeof(event);
898 event.width = 300;
899 event.height = 200;
900 event.pixel_ratio = 1.0;
901 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
902 kSuccess);
903 ASSERT_TRUE(engine.is_valid());
904 latch.Wait();
905}
906
907TEST_F(EmbedderTest, CanRenderImplicitViewUsingPresentLayersCallback) {
908 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
909
910 EmbedderConfigBuilder builder(context);
911 builder.SetSurface(DlISize(800, 600));
912 builder.SetCompositor(/* avoid_backing_store_cache = */ false,
913 /* use_present_layers_callback = */ true);
914 builder.SetDartEntrypoint("render_implicit_view");
915 builder.SetRenderTargetType(
916 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
917
919
920 context.GetCompositor().SetNextPresentCallback(
922 size_t layers_count) {
924 latch.Signal();
925 });
926
927 auto engine = builder.LaunchEngine();
928
929 FlutterWindowMetricsEvent event = {};
930 event.struct_size = sizeof(event);
931 event.width = 300;
932 event.height = 200;
933 event.pixel_ratio = 1.0;
934 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
935 kSuccess);
936 ASSERT_TRUE(engine.is_valid());
937 latch.Wait();
938}
939
940//------------------------------------------------------------------------------
941/// Test the layer structure and pixels rendered when using a custom software
942/// compositor.
943///
944// TODO(143940): Convert this test to use SkiaGold.
945#if FML_OS_MACOSX && FML_ARCH_CPU_ARM64
946TEST_F(EmbedderTest,
947 DISABLED_CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
948#else
950 CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
951#endif // FML_OS_MACOSX && FML_ARCH_CPU_ARM64
952
953 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
954
955 EmbedderConfigBuilder builder(context);
956 builder.SetSurface(DlISize(800, 600));
957 builder.SetCompositor();
958 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
959
960 builder.SetRenderTargetType(
961 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
962
963 fml::CountDownLatch latch(5);
964
965 auto scene_image = context.GetNextSceneImage();
966
967 context.GetCompositor().SetNextPresentCallback(
969 size_t layers_count) {
970 ASSERT_EQ(layers_count, 5u);
971
972 // Layer Root
973 {
974 FlutterBackingStore backing_store = *layers[0]->backing_store;
976 backing_store.did_update = true;
977 backing_store.software.height = 600;
978
979 FlutterRect paint_region_rects[] = {
980 FlutterRectMakeLTRB(0, 0, 800, 600),
981 };
982 FlutterRegion paint_region = {
983 .struct_size = sizeof(FlutterRegion),
984 .rects_count = 1,
985 .rects = paint_region_rects,
986 };
987 FlutterBackingStorePresentInfo present_info = {
989 .paint_region = &paint_region,
990 };
991
992 FlutterLayer layer = {};
993 layer.struct_size = sizeof(layer);
995 layer.backing_store = &backing_store;
996 layer.size = FlutterSizeMake(800.0, 600.0);
997 layer.offset = FlutterPointMake(0.0, 0.0);
998 layer.backing_store_present_info = &present_info;
999
1000 ASSERT_EQ(*layers[0], layer);
1001 }
1002
1003 // Layer 1
1004 {
1007 platform_view.identifier = 1;
1008
1009 FlutterLayer layer = {};
1010 layer.struct_size = sizeof(layer);
1013 layer.size = FlutterSizeMake(50.0, 150.0);
1014 layer.offset = FlutterPointMake(20.0, 20.0);
1015
1016 ASSERT_EQ(*layers[1], layer);
1017 }
1018
1019 // Layer 2
1020 {
1021 FlutterBackingStore backing_store = *layers[2]->backing_store;
1022 backing_store.type = kFlutterBackingStoreTypeSoftware;
1023 backing_store.did_update = true;
1024 backing_store.software.height = 600;
1025
1026 FlutterRect paint_region_rects[] = {
1027 FlutterRectMakeLTRB(30, 30, 80, 180),
1028 };
1029 FlutterRegion paint_region = {
1030 .struct_size = sizeof(FlutterRegion),
1031 .rects_count = 1,
1032 .rects = paint_region_rects,
1033 };
1034 FlutterBackingStorePresentInfo present_info = {
1036 .paint_region = &paint_region,
1037 };
1038
1039 FlutterLayer layer = {};
1040 layer.struct_size = sizeof(layer);
1042 layer.backing_store = &backing_store;
1043 layer.size = FlutterSizeMake(800.0, 600.0);
1044 layer.offset = FlutterPointMake(0.0, 0.0);
1045 layer.backing_store_present_info = &present_info;
1046
1047 ASSERT_EQ(*layers[2], layer);
1048 }
1049
1050 // Layer 3
1051 {
1054 platform_view.identifier = 2;
1055
1056 FlutterLayer layer = {};
1057 layer.struct_size = sizeof(layer);
1060 layer.size = FlutterSizeMake(50.0, 150.0);
1061 layer.offset = FlutterPointMake(40.0, 40.0);
1062
1063 ASSERT_EQ(*layers[3], layer);
1064 }
1065
1066 // Layer 4
1067 {
1068 FlutterBackingStore backing_store = *layers[4]->backing_store;
1069 backing_store.type = kFlutterBackingStoreTypeSoftware;
1070 backing_store.did_update = true;
1071 backing_store.software.height = 600;
1072
1073 FlutterRect paint_region_rects[] = {
1074 FlutterRectMakeLTRB(50, 50, 100, 200),
1075 };
1076 FlutterRegion paint_region = {
1077 .struct_size = sizeof(FlutterRegion),
1078 .rects_count = 1,
1079 .rects = paint_region_rects,
1080 };
1081 FlutterBackingStorePresentInfo present_info = {
1083 .paint_region = &paint_region,
1084 };
1085
1086 FlutterLayer layer = {};
1087 layer.struct_size = sizeof(layer);
1089 layer.backing_store = &backing_store;
1090 layer.size = FlutterSizeMake(800.0, 600.0);
1091 layer.offset = FlutterPointMake(0.0, 0.0);
1092 layer.backing_store_present_info = &present_info;
1093
1094 ASSERT_EQ(*layers[4], layer);
1095 }
1096
1097 latch.CountDown();
1098 });
1099
1100 context.GetCompositor().SetPlatformViewRendererCallback(
1101 [&](const FlutterLayer& layer, GrDirectContext*
1102 /* don't use because software compositor */) -> sk_sp<SkImage> {
1104 layer, nullptr /* null because software compositor */);
1105 auto canvas = surface->getCanvas();
1106 FML_CHECK(canvas != nullptr);
1107
1108 switch (layer.platform_view->identifier) {
1109 case 1: {
1110 SkPaint paint;
1111 // See dart test for total order.
1112 paint.setColor(SK_ColorGREEN);
1113 paint.setAlpha(127);
1114 const auto& rect =
1115 SkRect::MakeWH(layer.size.width, layer.size.height);
1116 canvas->drawRect(rect, paint);
1117 latch.CountDown();
1118 } break;
1119 case 2: {
1120 SkPaint paint;
1121 // See dart test for total order.
1122 paint.setColor(SK_ColorMAGENTA);
1123 paint.setAlpha(127);
1124 const auto& rect =
1125 SkRect::MakeWH(layer.size.width, layer.size.height);
1126 canvas->drawRect(rect, paint);
1127 latch.CountDown();
1128 } break;
1129 default:
1130 // Asked to render an unknown platform view.
1131 FML_CHECK(false)
1132 << "Test was asked to composite an unknown platform view.";
1133 }
1134
1135 return surface->makeImageSnapshot();
1136 });
1137
1138 context.AddNativeCallback(
1139 "SignalNativeTest",
1141 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1142
1143 auto engine = builder.LaunchEngine();
1144
1145 // Send a window metrics events so frames may be scheduled.
1146 FlutterWindowMetricsEvent event = {};
1147 event.struct_size = sizeof(event);
1148 event.width = 800;
1149 event.height = 600;
1150 event.pixel_ratio = 1.0;
1151 event.physical_view_inset_top = 0.0;
1152 event.physical_view_inset_right = 0.0;
1153 event.physical_view_inset_bottom = 0.0;
1154 event.physical_view_inset_left = 0.0;
1155 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1156 kSuccess);
1157 ASSERT_TRUE(engine.is_valid());
1158
1159 latch.Wait();
1160
1161 ASSERT_TRUE(ImageMatchesFixture("compositor_software.png", scene_image));
1162
1163 // There should no present calls on the root surface.
1164 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1165}
1166
1167//------------------------------------------------------------------------------
1168/// Test the layer structure and pixels rendered when using a custom software
1169/// compositor, with a transparent overlay
1170///
1171TEST_F(EmbedderTest, NoLayerCreatedForTransparentOverlayOnTopOfPlatformLayer) {
1172 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1173
1174 EmbedderConfigBuilder builder(context);
1175 builder.SetSurface(DlISize(800, 600));
1176 builder.SetCompositor();
1177 builder.SetDartEntrypoint("can_composite_platform_views_transparent_overlay");
1178
1179 builder.SetRenderTargetType(
1180 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1181
1182 fml::CountDownLatch latch(4);
1183
1184 auto scene_image = context.GetNextSceneImage();
1185
1186 context.GetCompositor().SetNextPresentCallback(
1188 size_t layers_count) {
1189 ASSERT_EQ(layers_count, 2u);
1190
1191 // Layer Root
1192 {
1193 FlutterBackingStore backing_store = *layers[0]->backing_store;
1194 backing_store.type = kFlutterBackingStoreTypeSoftware;
1195 backing_store.did_update = true;
1196 backing_store.software.height = 600;
1197
1198 FlutterRect paint_region_rects[] = {
1199 FlutterRectMakeLTRB(0, 0, 800, 600),
1200 };
1201 FlutterRegion paint_region = {
1202 .struct_size = sizeof(FlutterRegion),
1203 .rects_count = 1,
1204 .rects = paint_region_rects,
1205 };
1206 FlutterBackingStorePresentInfo present_info = {
1208 .paint_region = &paint_region,
1209 };
1210
1211 FlutterLayer layer = {};
1212 layer.struct_size = sizeof(layer);
1214 layer.backing_store = &backing_store;
1215 layer.size = FlutterSizeMake(800.0, 600.0);
1216 layer.offset = FlutterPointMake(0.0, 0.0);
1217 layer.backing_store_present_info = &present_info;
1218
1219 ASSERT_EQ(*layers[0], layer);
1220 }
1221
1222 // Layer 1
1223 {
1226 platform_view.identifier = 1;
1227
1228 FlutterLayer layer = {};
1229 layer.struct_size = sizeof(layer);
1232 layer.size = FlutterSizeMake(50.0, 150.0);
1233 layer.offset = FlutterPointMake(20.0, 20.0);
1234
1235 ASSERT_EQ(*layers[1], layer);
1236 }
1237
1238 latch.CountDown();
1239 });
1240
1241 context.GetCompositor().SetPlatformViewRendererCallback(
1242 [&](const FlutterLayer& layer, GrDirectContext*
1243 /* don't use because software compositor */) -> sk_sp<SkImage> {
1245 layer, nullptr /* null because software compositor */);
1246 auto canvas = surface->getCanvas();
1247 FML_CHECK(canvas != nullptr);
1248
1249 switch (layer.platform_view->identifier) {
1250 case 1: {
1251 SkPaint paint;
1252 // See dart test for total order.
1253 paint.setColor(SK_ColorGREEN);
1254 paint.setAlpha(127);
1255 const auto& rect =
1256 SkRect::MakeWH(layer.size.width, layer.size.height);
1257 canvas->drawRect(rect, paint);
1258 latch.CountDown();
1259 } break;
1260 default:
1261 // Asked to render an unknown platform view.
1262 FML_CHECK(false)
1263 << "Test was asked to composite an unknown platform view.";
1264 }
1265
1266 return surface->makeImageSnapshot();
1267 });
1268
1269 context.AddNativeCallback(
1270 "SignalNativeTest",
1272 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1273
1274 auto engine = builder.LaunchEngine();
1275
1276 // Send a window metrics events so frames may be scheduled.
1277 FlutterWindowMetricsEvent event = {};
1278 event.struct_size = sizeof(event);
1279 event.width = 800;
1280 event.height = 600;
1281 event.pixel_ratio = 1.0;
1282 event.physical_view_inset_top = 0.0;
1283 event.physical_view_inset_right = 0.0;
1284 event.physical_view_inset_bottom = 0.0;
1285 event.physical_view_inset_left = 0.0;
1286 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1287 kSuccess);
1288 ASSERT_TRUE(engine.is_valid());
1289
1290 latch.Wait();
1291
1292 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1293 // platforms.
1294#if !defined(FML_OS_LINUX)
1295 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1296#endif // FML_OS_LINUX
1297 ASSERT_TRUE(ImageMatchesFixture(
1298 "compositor_platform_layer_with_no_overlay.png", scene_image));
1299
1300 // There should no present calls on the root surface.
1301 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1302}
1303
1304//------------------------------------------------------------------------------
1305/// Test the layer structure and pixels rendered when using a custom software
1306/// compositor, with a no overlay
1307///
1308TEST_F(EmbedderTest, NoLayerCreatedForNoOverlayOnTopOfPlatformLayer) {
1309 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1310
1311 EmbedderConfigBuilder builder(context);
1312 builder.SetSurface(DlISize(800, 600));
1313 builder.SetCompositor();
1314 builder.SetDartEntrypoint("can_composite_platform_views_no_overlay");
1315
1316 builder.SetRenderTargetType(
1317 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1318
1319 fml::CountDownLatch latch(4);
1320
1321 auto scene_image = context.GetNextSceneImage();
1322
1323 context.GetCompositor().SetNextPresentCallback(
1325 size_t layers_count) {
1326 ASSERT_EQ(layers_count, 2u);
1327
1328 // Layer Root
1329 {
1330 FlutterBackingStore backing_store = *layers[0]->backing_store;
1331 backing_store.type = kFlutterBackingStoreTypeSoftware;
1332 backing_store.did_update = true;
1333 backing_store.software.height = 600;
1334
1335 FlutterRect paint_region_rects[] = {
1336 FlutterRectMakeLTRB(0, 0, 800, 600),
1337 };
1338 FlutterRegion paint_region = {
1339 .struct_size = sizeof(FlutterRegion),
1340 .rects_count = 1,
1341 .rects = paint_region_rects,
1342 };
1343 FlutterBackingStorePresentInfo present_info = {
1345 .paint_region = &paint_region,
1346 };
1347
1348 FlutterLayer layer = {};
1349 layer.struct_size = sizeof(layer);
1351 layer.backing_store = &backing_store;
1352 layer.size = FlutterSizeMake(800.0, 600.0);
1353 layer.offset = FlutterPointMake(0.0, 0.0);
1354 layer.backing_store_present_info = &present_info;
1355
1356 ASSERT_EQ(*layers[0], layer);
1357 }
1358
1359 // Layer 1
1360 {
1363 platform_view.identifier = 1;
1364
1365 FlutterLayer layer = {};
1366 layer.struct_size = sizeof(layer);
1369 layer.size = FlutterSizeMake(50.0, 150.0);
1370 layer.offset = FlutterPointMake(20.0, 20.0);
1371
1372 ASSERT_EQ(*layers[1], layer);
1373 }
1374
1375 latch.CountDown();
1376 });
1377
1378 context.GetCompositor().SetPlatformViewRendererCallback(
1379 [&](const FlutterLayer& layer, GrDirectContext*
1380 /* don't use because software compositor */) -> sk_sp<SkImage> {
1382 layer, nullptr /* null because software compositor */);
1383 auto canvas = surface->getCanvas();
1384 FML_CHECK(canvas != nullptr);
1385
1386 switch (layer.platform_view->identifier) {
1387 case 1: {
1388 SkPaint paint;
1389 // See dart test for total order.
1390 paint.setColor(SK_ColorGREEN);
1391 paint.setAlpha(127);
1392 const auto& rect =
1393 SkRect::MakeWH(layer.size.width, layer.size.height);
1394 canvas->drawRect(rect, paint);
1395 latch.CountDown();
1396 } break;
1397 default:
1398 // Asked to render an unknown platform view.
1399 FML_CHECK(false)
1400 << "Test was asked to composite an unknown platform view.";
1401 }
1402
1403 return surface->makeImageSnapshot();
1404 });
1405
1406 context.AddNativeCallback(
1407 "SignalNativeTest",
1409 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1410
1411 auto engine = builder.LaunchEngine();
1412
1413 // Send a window metrics events so frames may be scheduled.
1414 FlutterWindowMetricsEvent event = {};
1415 event.struct_size = sizeof(event);
1416 event.width = 800;
1417 event.height = 600;
1418 event.pixel_ratio = 1.0;
1419 event.physical_view_inset_top = 0.0;
1420 event.physical_view_inset_right = 0.0;
1421 event.physical_view_inset_bottom = 0.0;
1422 event.physical_view_inset_left = 0.0;
1423 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1424 kSuccess);
1425 ASSERT_TRUE(engine.is_valid());
1426
1427 latch.Wait();
1428
1429 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1430 // platforms.
1431#if !defined(FML_OS_LINUX)
1432 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1433#endif // FML_OS_LINUX
1434 ASSERT_TRUE(ImageMatchesFixture(
1435 "compositor_platform_layer_with_no_overlay.png", scene_image));
1436
1437 // There should no present calls on the root surface.
1438 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1439}
1440
1441//------------------------------------------------------------------------------
1442/// Test that an engine can be initialized but not run.
1443///
1444TEST_F(EmbedderTest, CanCreateInitializedEngine) {
1445 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1446 EmbedderConfigBuilder builder(context);
1447 builder.SetSurface(DlISize(1, 1));
1448 auto engine = builder.InitializeEngine();
1449 ASSERT_TRUE(engine.is_valid());
1450 engine.reset();
1451}
1452
1453//------------------------------------------------------------------------------
1454/// Test that an initialized engine can be run exactly once.
1455///
1456TEST_F(EmbedderTest, CanRunInitializedEngine) {
1457 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1458 EmbedderConfigBuilder builder(context);
1459 builder.SetSurface(DlISize(1, 1));
1460 auto engine = builder.InitializeEngine();
1461 ASSERT_TRUE(engine.is_valid());
1462 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1463 // Cannot re-run an already running engine.
1465 engine.reset();
1466}
1467
1468//------------------------------------------------------------------------------
1469/// Test that an engine can be deinitialized.
1470///
1471TEST_F(EmbedderTest, CanDeinitializeAnEngine) {
1472 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1473 EmbedderConfigBuilder builder(context);
1474 builder.SetSurface(DlISize(1, 1));
1475 auto engine = builder.InitializeEngine();
1476 ASSERT_TRUE(engine.is_valid());
1477 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1478 // Cannot re-run an already running engine.
1480 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1481 // It is ok to deinitialize an engine multiple times.
1482 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1483
1484 // Sending events to a deinitialized engine fails.
1485 FlutterWindowMetricsEvent event = {};
1486 event.struct_size = sizeof(event);
1487 event.width = 800;
1488 event.height = 600;
1489 event.pixel_ratio = 1.0;
1490 event.physical_view_inset_top = 0.0;
1491 event.physical_view_inset_right = 0.0;
1492 event.physical_view_inset_bottom = 0.0;
1493 event.physical_view_inset_left = 0.0;
1494 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1496 engine.reset();
1497}
1498
1499//------------------------------------------------------------------------------
1500/// Test that a view can be added to a running engine.
1501///
1502TEST_F(EmbedderTest, CanAddView) {
1503 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1504 EmbedderConfigBuilder builder(context);
1505 builder.SetSurface(DlISize(1, 1));
1506 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1507
1508 fml::AutoResetWaitableEvent ready_latch, message_latch;
1509 context.AddNativeCallback(
1510 "SignalNativeTest",
1512 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1513
1514 std::string message;
1515 context.AddNativeCallback("SignalNativeMessage",
1516 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1517 message =
1519 Dart_GetNativeArgument(args, 0));
1520 message_latch.Signal();
1521 }));
1522
1523 auto engine = builder.LaunchEngine();
1524 ASSERT_TRUE(engine.is_valid());
1525
1526 ready_latch.Wait();
1527
1528 FlutterWindowMetricsEvent metrics = {};
1529 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1530 metrics.width = 800;
1531 metrics.height = 600;
1532 metrics.pixel_ratio = 1.0;
1533 metrics.view_id = 123;
1534
1535 FlutterAddViewInfo info = {};
1536 info.struct_size = sizeof(FlutterAddViewInfo);
1537 info.view_id = 123;
1538 info.view_metrics = &metrics;
1539 info.add_view_callback = [](const FlutterAddViewResult* result) {
1540 EXPECT_TRUE(result->added);
1541 };
1542 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1543 message_latch.Wait();
1544 ASSERT_EQ("View IDs: [0, 123]", message);
1545}
1546
1547//------------------------------------------------------------------------------
1548/// Test that adding a view schedules a frame.
1549///
1550TEST_F(EmbedderTest, AddViewSchedulesFrame) {
1551 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1552 EmbedderConfigBuilder builder(context);
1553 builder.SetSurface(DlISize(1, 1));
1554 builder.SetDartEntrypoint("add_view_schedules_frame");
1556 context.AddNativeCallback(
1557 "SignalNativeTest",
1559 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
1560
1561 fml::AutoResetWaitableEvent check_latch;
1562 context.AddNativeCallback(
1563 "SignalNativeCount",
1565 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
1566
1567 auto engine = builder.LaunchEngine();
1568 ASSERT_TRUE(engine.is_valid());
1569
1570 // Wait for the application to attach the listener.
1571 latch.Wait();
1572
1573 FlutterWindowMetricsEvent metrics = {};
1574 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1575 metrics.width = 800;
1576 metrics.height = 600;
1577 metrics.pixel_ratio = 1.0;
1578 metrics.view_id = 123;
1579
1580 FlutterAddViewInfo info = {};
1581 info.struct_size = sizeof(FlutterAddViewInfo);
1582 info.view_id = 123;
1583 info.view_metrics = &metrics;
1584 info.add_view_callback = [](const FlutterAddViewResult* result) {
1585 EXPECT_TRUE(result->added);
1586 };
1587 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1588
1589 check_latch.Wait();
1590}
1591
1592//------------------------------------------------------------------------------
1593/// Test that a view that was added can be removed.
1594///
1595TEST_F(EmbedderTest, CanRemoveView) {
1596 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1597 EmbedderConfigBuilder builder(context);
1598 builder.SetSurface(DlISize(1, 1));
1599 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1600
1601 fml::AutoResetWaitableEvent ready_latch, message_latch;
1602 context.AddNativeCallback(
1603 "SignalNativeTest",
1605 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1606
1607 std::string message;
1608 context.AddNativeCallback("SignalNativeMessage",
1609 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1610 message =
1612 Dart_GetNativeArgument(args, 0));
1613 message_latch.Signal();
1614 }));
1615
1616 auto engine = builder.LaunchEngine();
1617 ASSERT_TRUE(engine.is_valid());
1618
1619 ready_latch.Wait();
1620
1621 // Add view 123.
1622 FlutterWindowMetricsEvent metrics = {};
1623 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1624 metrics.width = 800;
1625 metrics.height = 600;
1626 metrics.pixel_ratio = 1.0;
1627 metrics.view_id = 123;
1628
1629 FlutterAddViewInfo add_info = {};
1630 add_info.struct_size = sizeof(FlutterAddViewInfo);
1631 add_info.view_id = 123;
1632 add_info.view_metrics = &metrics;
1633 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1634 ASSERT_TRUE(result->added);
1635 };
1636 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1637 message_latch.Wait();
1638 ASSERT_EQ(message, "View IDs: [0, 123]");
1639
1640 // Remove view 123.
1641 FlutterRemoveViewInfo remove_info = {};
1642 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1643 remove_info.view_id = 123;
1644 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1645 EXPECT_TRUE(result->removed);
1646 };
1647 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1648 message_latch.Wait();
1649 ASSERT_EQ(message, "View IDs: [0]");
1650}
1651
1652// Regression test for:
1653// https://github.com/flutter/flutter/issues/164564
1654TEST_F(EmbedderTest, RemoveViewCallbackIsInvokedAfterRasterThreadIsDone) {
1655 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1656 EmbedderConfigBuilder builder(context);
1657 std::mutex engine_mutex;
1659 auto render_thread = CreateNewThread("custom_render_thread");
1660 EmbedderTestTaskRunner render_task_runner(
1661 render_thread, [&](FlutterTask task) {
1662 std::scoped_lock engine_lock(engine_mutex);
1663 if (engine.is_valid()) {
1664 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
1665 }
1666 });
1667
1668 builder.SetSurface(DlISize(1, 1));
1669 builder.SetDartEntrypoint("remove_view_callback_too_early");
1670 builder.SetRenderTaskRunner(
1671 &render_task_runner.GetFlutterTaskRunnerDescription());
1672
1673 fml::AutoResetWaitableEvent ready_latch;
1674 context.AddNativeCallback(
1675 "SignalNativeTest",
1677 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1678
1679 {
1680 std::scoped_lock lock(engine_mutex);
1681 engine = builder.InitializeEngine();
1682 }
1683 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1684 ASSERT_TRUE(engine.is_valid());
1685
1686 ready_latch.Wait();
1687
1688 fml::AutoResetWaitableEvent add_view_latch;
1689 // Add view 123.
1690 FlutterWindowMetricsEvent metrics = {};
1691 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1692 metrics.width = 800;
1693 metrics.height = 600;
1694 metrics.pixel_ratio = 1.0;
1695 metrics.view_id = 123;
1696
1697 FlutterAddViewInfo add_info = {};
1698 add_info.struct_size = sizeof(FlutterAddViewInfo);
1699 add_info.view_id = 123;
1700 add_info.view_metrics = &metrics;
1701 add_info.user_data = &add_view_latch;
1702 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1703 ASSERT_TRUE(result->added);
1704 auto add_view_latch =
1705 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
1706 add_view_latch->Signal();
1707 };
1708 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1709 add_view_latch.Wait();
1710
1711 std::atomic_bool view_available = true;
1712
1713 // Simulate pending rasterization task scheduled before view removal request
1714 // that accesses view resources.
1715 fml::AutoResetWaitableEvent raster_thread_latch;
1716 render_thread->PostTask([&] {
1717 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1718 // View must be available.
1719 EXPECT_TRUE(view_available);
1720 raster_thread_latch.Signal();
1721 });
1722
1723 fml::AutoResetWaitableEvent remove_view_latch;
1724 FlutterRemoveViewInfo remove_view_info = {};
1725 remove_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
1726 remove_view_info.view_id = 123;
1727 remove_view_info.user_data = &remove_view_latch;
1728 remove_view_info.remove_view_callback =
1729 [](const FlutterRemoveViewResult* result) {
1730 ASSERT_TRUE(result->removed);
1731 auto remove_view_latch =
1732 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
1733 remove_view_latch->Signal();
1734 };
1735
1736 // Remove the view and wait until the callback is called.
1737 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_view_info), kSuccess);
1738 remove_view_latch.Wait();
1739
1740 // After FlutterEngineRemoveViewCallback is called it should be safe to
1741 // remove view - raster thread must not be accessing any view resources.
1742 view_available = false;
1743 raster_thread_latch.Wait();
1744
1746}
1747
1748//------------------------------------------------------------------------------
1749/// The implicit view is a special view that the engine and framework assume
1750/// can *always* be rendered to. Test that this view cannot be removed.
1751///
1752TEST_F(EmbedderTest, CannotRemoveImplicitView) {
1753 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1754 EmbedderConfigBuilder builder(context);
1755 builder.SetSurface(DlISize(1, 1));
1756
1757 auto engine = builder.LaunchEngine();
1758 ASSERT_TRUE(engine.is_valid());
1759
1760 FlutterRemoveViewInfo info = {};
1761 info.struct_size = sizeof(FlutterRemoveViewInfo);
1763 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1764 FAIL();
1765 };
1766 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &info), kInvalidArguments);
1767}
1768
1769//------------------------------------------------------------------------------
1770/// Test that a view cannot be added if its ID already exists.
1771///
1772TEST_F(EmbedderTest, CannotAddDuplicateViews) {
1773 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1774 EmbedderConfigBuilder builder(context);
1775 builder.SetSurface(DlISize(1, 1));
1776 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1777
1778 fml::AutoResetWaitableEvent ready_latch, message_latch;
1779 context.AddNativeCallback(
1780 "SignalNativeTest",
1782 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1783
1784 std::string message;
1785 context.AddNativeCallback("SignalNativeMessage",
1786 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1787 message =
1789 Dart_GetNativeArgument(args, 0));
1790 message_latch.Signal();
1791 }));
1792
1793 auto engine = builder.LaunchEngine();
1794 ASSERT_TRUE(engine.is_valid());
1795
1796 ready_latch.Wait();
1797
1798 // Add view 123.
1799 struct Captures {
1800 std::atomic<int> count = 0;
1801 fml::AutoResetWaitableEvent failure_latch;
1802 };
1803 Captures captures;
1804
1805 FlutterWindowMetricsEvent metrics = {};
1806 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1807 metrics.width = 800;
1808 metrics.height = 600;
1809 metrics.pixel_ratio = 1.0;
1810 metrics.view_id = 123;
1811
1812 FlutterAddViewInfo add_info = {};
1813 add_info.struct_size = sizeof(FlutterAddViewInfo);
1814 add_info.view_id = 123;
1815 add_info.view_metrics = &metrics;
1816 add_info.user_data = &captures;
1817 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1818 auto captures = reinterpret_cast<Captures*>(result->user_data);
1819
1820 int count = captures->count.fetch_add(1);
1821
1822 if (count == 0) {
1823 ASSERT_TRUE(result->added);
1824 } else {
1825 EXPECT_FALSE(result->added);
1826 captures->failure_latch.Signal();
1827 }
1828 };
1829 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1830 message_latch.Wait();
1831 ASSERT_EQ(message, "View IDs: [0, 123]");
1832 ASSERT_FALSE(captures.failure_latch.IsSignaledForTest());
1833
1834 // Add view 123 a second time.
1835 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1836 captures.failure_latch.Wait();
1837 ASSERT_EQ(captures.count, 2);
1838 ASSERT_FALSE(message_latch.IsSignaledForTest());
1839}
1840
1841//------------------------------------------------------------------------------
1842/// Test that a removed view's ID can be reused to add a new view.
1843///
1844TEST_F(EmbedderTest, CanReuseViewIds) {
1845 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1846 EmbedderConfigBuilder builder(context);
1847 builder.SetSurface(DlISize(1, 1));
1848 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1849
1850 fml::AutoResetWaitableEvent ready_latch, message_latch;
1851 context.AddNativeCallback(
1852 "SignalNativeTest",
1854 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1855
1856 std::string message;
1857 context.AddNativeCallback("SignalNativeMessage",
1858 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1859 message =
1861 Dart_GetNativeArgument(args, 0));
1862 message_latch.Signal();
1863 }));
1864
1865 auto engine = builder.LaunchEngine();
1866 ASSERT_TRUE(engine.is_valid());
1867
1868 ready_latch.Wait();
1869
1870 // Add view 123.
1871 FlutterWindowMetricsEvent metrics = {};
1872 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1873 metrics.width = 800;
1874 metrics.height = 600;
1875 metrics.pixel_ratio = 1.0;
1876 metrics.view_id = 123;
1877
1878 FlutterAddViewInfo add_info = {};
1879 add_info.struct_size = sizeof(FlutterAddViewInfo);
1880 add_info.view_id = 123;
1881 add_info.view_metrics = &metrics;
1882 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1883 ASSERT_TRUE(result->added);
1884 };
1885 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1886 message_latch.Wait();
1887 ASSERT_EQ(message, "View IDs: [0, 123]");
1888
1889 // Remove view 123.
1890 FlutterRemoveViewInfo remove_info = {};
1891 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1892 remove_info.view_id = 123;
1893 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1894 ASSERT_TRUE(result->removed);
1895 };
1896 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1897 message_latch.Wait();
1898 ASSERT_EQ(message, "View IDs: [0]");
1899
1900 // Re-add view 123.
1901 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1902 message_latch.Wait();
1903 ASSERT_EQ(message, "View IDs: [0, 123]");
1904}
1905
1906//------------------------------------------------------------------------------
1907/// Test that attempting to remove a view that does not exist fails as expected.
1908///
1909TEST_F(EmbedderTest, CannotRemoveUnknownView) {
1910 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1911 EmbedderConfigBuilder builder(context);
1912 builder.SetSurface(DlISize(1, 1));
1913
1914 auto engine = builder.LaunchEngine();
1915 ASSERT_TRUE(engine.is_valid());
1916
1918 FlutterRemoveViewInfo info = {};
1919 info.struct_size = sizeof(FlutterRemoveViewInfo);
1920 info.view_id = 123;
1921 info.user_data = &latch;
1922 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1923 EXPECT_FALSE(result->removed);
1924 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data)->Signal();
1925 };
1926 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &info), kSuccess);
1927 latch.Wait();
1928}
1929
1930//------------------------------------------------------------------------------
1931/// View operations - adding, removing, sending window metrics - must execute in
1932/// order even though they are asynchronous. This is necessary to ensure the
1933/// embedder's and engine's states remain synchronized.
1934///
1935TEST_F(EmbedderTest, ViewOperationsOrdered) {
1936 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1937 EmbedderConfigBuilder builder(context);
1938 builder.SetSurface(DlISize(1, 1));
1939 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1940
1941 fml::AutoResetWaitableEvent ready_latch;
1942 context.AddNativeCallback(
1943 "SignalNativeTest",
1945 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1946
1947 std::atomic<int> message_count = 0;
1948 context.AddNativeCallback("SignalNativeMessage",
1949 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1950 message_count.fetch_add(1);
1951 }));
1952
1953 auto engine = builder.LaunchEngine();
1954 ASSERT_TRUE(engine.is_valid());
1955
1956 ready_latch.Wait();
1957
1958 // Enqueue multiple view operations at once:
1959 //
1960 // 1. Add view 123 - This must succeed.
1961 // 2. Add duplicate view 123 - This must fail asynchronously.
1962 // 3. Add second view 456 - This must succeed.
1963 // 4. Remove second view 456 - This must succeed.
1964 //
1965 // The engine must execute view operations asynchronously in serial order.
1966 // If step 2 succeeds instead of step 1, this indicates the engine did not
1967 // execute the view operations in the correct order. If step 4 fails,
1968 // this indicates the engine did not wait until the add second view completed.
1969 FlutterWindowMetricsEvent metrics123 = {};
1970 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
1971 metrics123.width = 800;
1972 metrics123.height = 600;
1973 metrics123.pixel_ratio = 1.0;
1974 metrics123.view_id = 123;
1975
1976 FlutterWindowMetricsEvent metrics456 = {};
1977 metrics456.struct_size = sizeof(FlutterWindowMetricsEvent);
1978 metrics456.width = 800;
1979 metrics456.height = 600;
1980 metrics456.pixel_ratio = 1.0;
1981 metrics456.view_id = 456;
1982
1983 struct Captures {
1984 fml::AutoResetWaitableEvent add_first_view;
1985 fml::AutoResetWaitableEvent add_duplicate_view;
1986 fml::AutoResetWaitableEvent add_second_view;
1987 fml::AutoResetWaitableEvent remove_second_view;
1988 };
1989 Captures captures;
1990
1991 // Add view 123.
1992 FlutterAddViewInfo add_view_info = {};
1993 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
1994 add_view_info.view_id = 123;
1995 add_view_info.view_metrics = &metrics123;
1996 add_view_info.user_data = &captures;
1997 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
1998 auto captures = reinterpret_cast<Captures*>(result->user_data);
1999
2000 ASSERT_TRUE(result->added);
2001 ASSERT_FALSE(captures->add_first_view.IsSignaledForTest());
2002 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
2003 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2004 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2005
2006 captures->add_first_view.Signal();
2007 };
2008
2009 // Add duplicate view 123.
2010 FlutterAddViewInfo add_duplicate_view_info = {};
2011 add_duplicate_view_info.struct_size = sizeof(FlutterAddViewInfo);
2012 add_duplicate_view_info.view_id = 123;
2013 add_duplicate_view_info.view_metrics = &metrics123;
2014 add_duplicate_view_info.user_data = &captures;
2015 add_duplicate_view_info.add_view_callback =
2016 [](const FlutterAddViewResult* result) {
2017 auto captures = reinterpret_cast<Captures*>(result->user_data);
2018
2019 ASSERT_FALSE(result->added);
2020 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2021 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
2022 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2023 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2024
2025 captures->add_duplicate_view.Signal();
2026 };
2027
2028 // Add view 456.
2029 FlutterAddViewInfo add_second_view_info = {};
2030 add_second_view_info.struct_size = sizeof(FlutterAddViewInfo);
2031 add_second_view_info.view_id = 456;
2032 add_second_view_info.view_metrics = &metrics456;
2033 add_second_view_info.user_data = &captures;
2034 add_second_view_info.add_view_callback =
2035 [](const FlutterAddViewResult* result) {
2036 auto captures = reinterpret_cast<Captures*>(result->user_data);
2037
2038 ASSERT_TRUE(result->added);
2039 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2040 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
2041 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2042 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2043
2044 captures->add_second_view.Signal();
2045 };
2046
2047 // Remove view 456.
2048 FlutterRemoveViewInfo remove_second_view_info = {};
2049 remove_second_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
2050 remove_second_view_info.view_id = 456;
2051 remove_second_view_info.user_data = &captures;
2052 remove_second_view_info.remove_view_callback =
2053 [](const FlutterRemoveViewResult* result) {
2054 auto captures = reinterpret_cast<Captures*>(result->user_data);
2055
2056 ASSERT_TRUE(result->removed);
2057 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2058 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
2059 ASSERT_TRUE(captures->add_second_view.IsSignaledForTest());
2060 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2061
2062 captures->remove_second_view.Signal();
2063 };
2064
2065 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2066 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_duplicate_view_info),
2067 kSuccess);
2068 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_second_view_info),
2069 kSuccess);
2070 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_second_view_info),
2071 kSuccess);
2072 captures.remove_second_view.Wait();
2073 captures.add_second_view.Wait();
2074 captures.add_duplicate_view.Wait();
2075 captures.add_first_view.Wait();
2076 ASSERT_EQ(message_count, 3);
2077}
2078
2079//------------------------------------------------------------------------------
2080/// Test the engine can present to multiple views.
2081///
2082TEST_F(EmbedderTest, CanRenderMultipleViews) {
2083 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2084 EmbedderConfigBuilder builder(context);
2085 builder.SetSurface(DlISize(1, 1));
2086 builder.SetCompositor();
2087 builder.SetDartEntrypoint("render_all_views");
2088
2089 builder.SetRenderTargetType(
2090 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2091
2092 fml::AutoResetWaitableEvent latch0, latch123;
2093 context.GetCompositor().SetPresentCallback(
2095 size_t layers_count) {
2096 switch (view_id) {
2097 case 0:
2098 latch0.Signal();
2099 break;
2100 case 123:
2101 latch123.Signal();
2102 break;
2103 default:
2105 }
2106 },
2107 /* one_shot= */ false);
2108
2109 auto engine = builder.LaunchEngine();
2110 ASSERT_TRUE(engine.is_valid());
2111
2112 // Give the implicit view a non-zero size so that it renders something.
2113 FlutterWindowMetricsEvent metrics0 = {};
2114 metrics0.struct_size = sizeof(FlutterWindowMetricsEvent);
2115 metrics0.width = 800;
2116 metrics0.height = 600;
2117 metrics0.pixel_ratio = 1.0;
2118 metrics0.view_id = 0;
2119 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics0),
2120 kSuccess);
2121
2122 // Add view 123.
2123 FlutterWindowMetricsEvent metrics123 = {};
2124 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
2125 metrics123.width = 800;
2126 metrics123.height = 600;
2127 metrics123.pixel_ratio = 1.0;
2128 metrics123.view_id = 123;
2129
2130 FlutterAddViewInfo add_view_info = {};
2131 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
2132 add_view_info.view_id = 123;
2133 add_view_info.view_metrics = &metrics123;
2134 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
2135 ASSERT_TRUE(result->added);
2136 };
2137
2138 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2139
2140 latch0.Wait();
2141 latch123.Wait();
2142}
2143
2145 const FlutterViewFocusChangeRequest& rhs) {
2146 return lhs.view_id == rhs.view_id && lhs.state == rhs.state &&
2147 lhs.direction == rhs.direction;
2148}
2149
2150TEST_F(EmbedderTest, SendsViewFocusChangeRequest) {
2151 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2152 auto platform_task_runner = CreateNewThread("test_platform_thread");
2154 static std::mutex engine_mutex;
2155 EmbedderTestTaskRunner test_platform_task_runner(
2156 platform_task_runner, [&](FlutterTask task) {
2157 std::scoped_lock lock(engine_mutex);
2158 if (!engine.is_valid()) {
2159 return;
2160 }
2161 FlutterEngineRunTask(engine.get(), &task);
2162 });
2163 fml::CountDownLatch latch(3);
2164 std::vector<FlutterViewFocusChangeRequest> received_requests;
2165 platform_task_runner->PostTask([&]() {
2166 EmbedderConfigBuilder builder(context);
2167 builder.SetSurface(DlISize(1, 1));
2168 builder.SetDartEntrypoint("testSendViewFocusChangeRequest");
2169 const auto platform_task_runner_description =
2170 test_platform_task_runner.GetFlutterTaskRunnerDescription();
2171 builder.SetPlatformTaskRunner(&platform_task_runner_description);
2173 [&](const FlutterViewFocusChangeRequest* request) {
2174 EXPECT_TRUE(platform_task_runner->RunsTasksOnCurrentThread());
2175 received_requests.push_back(*request);
2176 latch.CountDown();
2177 });
2178 engine = builder.LaunchEngine();
2179 ASSERT_TRUE(engine.is_valid());
2180 });
2181 latch.Wait();
2182
2183 std::vector<FlutterViewFocusChangeRequest> expected_requests{
2184 {.view_id = 1, .state = kUnfocused, .direction = kUndefined},
2185 {.view_id = 2, .state = kFocused, .direction = kForward},
2186 {.view_id = 3, .state = kFocused, .direction = kBackward},
2187 };
2188
2189 ASSERT_EQ(received_requests.size(), expected_requests.size());
2190 for (size_t i = 0; i < received_requests.size(); ++i) {
2191 ASSERT_TRUE(received_requests[i] == expected_requests[i]);
2192 }
2193
2194 fml::AutoResetWaitableEvent kill_latch;
2195 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
2196 std::scoped_lock lock(engine_mutex);
2197 engine.reset();
2198
2199 // There may still be pending tasks on the platform thread that were queued
2200 // by the test_task_runner. Signal the latch after these tasks have been
2201 // consumed.
2202 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
2203 }));
2204 kill_latch.Wait();
2205}
2206
2207TEST_F(EmbedderTest, CanSendViewFocusEvent) {
2208 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2209 EmbedderConfigBuilder builder(context);
2210 builder.SetSurface(DlISize(1, 1));
2211 builder.SetDartEntrypoint("testSendViewFocusEvent");
2212
2214 std::string last_event;
2215
2216 context.AddNativeCallback(
2217 "SignalNativeTest",
2219 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
2220 context.AddNativeCallback("NotifyStringValue",
2221 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2222 const auto message_from_dart =
2224 Dart_GetNativeArgument(args, 0));
2225 last_event = message_from_dart;
2226 latch.Signal();
2227 }));
2228
2229 auto engine = builder.LaunchEngine();
2230 ASSERT_TRUE(engine.is_valid());
2231 // Wait until the focus change handler is attached.
2232 latch.Wait();
2233 latch.Reset();
2234
2235 FlutterViewFocusEvent event1{
2237 .view_id = 1,
2238 .state = kFocused,
2239 .direction = kUndefined,
2240 };
2241 FlutterEngineResult result =
2243 ASSERT_EQ(result, kSuccess);
2244 latch.Wait();
2245 ASSERT_EQ(last_event,
2246 "1 ViewFocusState.focused ViewFocusDirection.undefined");
2247
2248 FlutterViewFocusEvent event2{
2250 .view_id = 2,
2251 .state = kUnfocused,
2252 .direction = kBackward,
2253 };
2254 latch.Reset();
2255 result = FlutterEngineSendViewFocusEvent(engine.get(), &event2);
2256 ASSERT_EQ(result, kSuccess);
2257 latch.Wait();
2258 ASSERT_EQ(last_event,
2259 "2 ViewFocusState.unfocused ViewFocusDirection.backward");
2260}
2261
2262//------------------------------------------------------------------------------
2263/// Test that the backing store is created with the correct view ID, is used
2264/// for the correct view, and is cached according to their views.
2265///
2266/// The test involves two frames:
2267/// 1. The first frame renders the implicit view and the second view.
2268/// 2. The second frame renders the implicit view and the third view.
2269///
2270/// The test verifies that:
2271/// - Each backing store is created with a valid view ID.
2272/// - Each backing store is presented for the view that it was created for.
2273/// - Both frames render the expected sets of views.
2274/// - By the end of frame 1, only 2 backing stores were created.
2275/// - By the end of frame 2, only 3 backing stores were created. This ensures
2276/// that the backing store for the 2nd view is not reused for the 3rd view.
2277TEST_F(EmbedderTest, BackingStoresCorrespondToTheirViews) {
2278 constexpr FlutterViewId kSecondViewId = 123;
2279 constexpr FlutterViewId kThirdViewId = 456;
2280 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2281
2282 EmbedderConfigBuilder builder(context);
2283 builder.SetDartEntrypoint("render_all_views");
2284 builder.SetSurface(DlISize(800, 600));
2285 builder.SetCompositor();
2286
2288 context.GetCompositor().GetGrContext(),
2289 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2290
2291 // The variables needed by the callbacks of the compositor.
2292 struct CompositorUserData {
2294 // Each latch is signaled when its corresponding view is presented.
2295 fml::AutoResetWaitableEvent latch_implicit;
2296 fml::AutoResetWaitableEvent latch_second;
2297 fml::AutoResetWaitableEvent latch_third;
2298 // Whether the respective view should be rendered in the frame.
2299 bool second_expected;
2300 bool third_expected;
2301 // The total number of backing stores created to verify caching.
2302 int backing_stores_created;
2303 };
2304 CompositorUserData compositor_user_data{
2305 .producer = &producer,
2306 .backing_stores_created = 0,
2307 };
2308
2309 builder.GetCompositor() = FlutterCompositor{
2310 .struct_size = sizeof(FlutterCompositor),
2311 .user_data = reinterpret_cast<void*>(&compositor_user_data),
2312 .create_backing_store_callback =
2313 [](const FlutterBackingStoreConfig* config,
2314 FlutterBackingStore* backing_store_out, void* user_data) {
2315 // Verify that the backing store comes with the correct view ID.
2316 EXPECT_TRUE(config->view_id == 0 ||
2317 config->view_id == kSecondViewId ||
2318 config->view_id == kThirdViewId);
2319 auto compositor_user_data =
2320 reinterpret_cast<CompositorUserData*>(user_data);
2321 compositor_user_data->backing_stores_created += 1;
2322 bool result = compositor_user_data->producer->Create(
2323 config, backing_store_out);
2324 // The created backing store has a user_data that records the view
2325 // that the store is created for.
2326 backing_store_out->user_data =
2327 reinterpret_cast<void*>(config->view_id);
2328 return result;
2329 },
2330 .collect_backing_store_callback = [](const FlutterBackingStore* renderer,
2331 void* user_data) { return true; },
2332 .present_layers_callback = nullptr,
2333 .avoid_backing_store_cache = false,
2334 .present_view_callback =
2335 [](const FlutterPresentViewInfo* info) {
2336 EXPECT_EQ(info->layers_count, 1u);
2337 // Verify that the given layer's backing store has the same view ID
2338 // as the target view.
2339 int64_t store_view_id = reinterpret_cast<int64_t>(
2340 info->layers[0]->backing_store->user_data);
2341 EXPECT_EQ(store_view_id, info->view_id);
2342 auto compositor_user_data =
2343 reinterpret_cast<CompositorUserData*>(info->user_data);
2344 // Verify that the respective views are rendered.
2345 switch (info->view_id) {
2346 case 0:
2347 compositor_user_data->latch_implicit.Signal();
2348 break;
2349 case kSecondViewId:
2350 EXPECT_TRUE(compositor_user_data->second_expected);
2351 compositor_user_data->latch_second.Signal();
2352 break;
2353 case kThirdViewId:
2354 EXPECT_TRUE(compositor_user_data->third_expected);
2355 compositor_user_data->latch_third.Signal();
2356 break;
2357 default:
2359 }
2360 return true;
2361 },
2362 };
2363
2364 compositor_user_data.second_expected = true;
2365 compositor_user_data.third_expected = false;
2366
2367 /*=== First frame ===*/
2368
2369 auto engine = builder.LaunchEngine();
2370 ASSERT_TRUE(engine.is_valid());
2371
2372 // Give the implicit view a non-zero size so that it renders something.
2373 FlutterWindowMetricsEvent metrics_implicit = {
2375 .width = 800,
2376 .height = 600,
2377 .pixel_ratio = 1.0,
2378 .view_id = 0,
2379 };
2380 ASSERT_EQ(
2381 FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics_implicit),
2382 kSuccess);
2383
2384 // Add the second view.
2385 FlutterWindowMetricsEvent metrics_add = {
2387 .width = 800,
2388 .height = 600,
2389 .pixel_ratio = 1.0,
2390 .view_id = kSecondViewId,
2391 };
2392
2393 FlutterAddViewInfo add_view_info = {};
2394 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
2395 add_view_info.view_id = kSecondViewId;
2396 add_view_info.view_metrics = &metrics_add;
2397 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
2398 ASSERT_TRUE(result->added);
2399 };
2400
2401 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2402
2403 compositor_user_data.latch_implicit.Wait();
2404 compositor_user_data.latch_second.Wait();
2405
2406 /*=== Second frame ===*/
2407
2408 compositor_user_data.second_expected = false;
2409 compositor_user_data.third_expected = true;
2410 EXPECT_EQ(compositor_user_data.backing_stores_created, 2);
2411
2412 // Remove the second view
2413 FlutterRemoveViewInfo remove_view_info = {};
2414 remove_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
2415 remove_view_info.view_id = kSecondViewId;
2416 remove_view_info.remove_view_callback =
2417 [](const FlutterRemoveViewResult* result) {
2418 ASSERT_TRUE(result->removed);
2419 };
2420 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_view_info), kSuccess);
2421
2422 // Add the third view.
2423 add_view_info.view_id = kThirdViewId;
2424 metrics_add.view_id = kThirdViewId;
2425 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2426 // Adding the view should have scheduled a frame.
2427
2428 compositor_user_data.latch_implicit.Wait();
2429 compositor_user_data.latch_third.Wait();
2430 EXPECT_EQ(compositor_user_data.backing_stores_created, 3);
2431}
2432
2433TEST_F(EmbedderTest, CanUpdateLocales) {
2434 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2435 EmbedderConfigBuilder builder(context);
2436 builder.SetSurface(DlISize(1, 1));
2437 builder.SetDartEntrypoint("can_receive_locale_updates");
2439 context.AddNativeCallback(
2440 "SignalNativeTest",
2442 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
2443
2444 fml::AutoResetWaitableEvent check_latch;
2445 context.AddNativeCallback(
2446 "SignalNativeCount",
2447 CREATE_NATIVE_ENTRY([&check_latch](Dart_NativeArguments args) {
2449 Dart_GetNativeArgument(args, 0)),
2450 2);
2451 check_latch.Signal();
2452 }));
2453
2454 auto engine = builder.LaunchEngine();
2455 ASSERT_TRUE(engine.is_valid());
2456
2457 // Wait for the application to attach the listener.
2458 latch.Wait();
2459
2460 FlutterLocale locale1 = {};
2461 locale1.struct_size = sizeof(locale1);
2462 locale1.language_code = ""; // invalid
2463 locale1.country_code = "US";
2464 locale1.script_code = "";
2465 locale1.variant_code = nullptr;
2466
2467 FlutterLocale locale2 = {};
2468 locale2.struct_size = sizeof(locale2);
2469 locale2.language_code = "zh";
2470 locale2.country_code = "CN";
2471 locale2.script_code = "Hans";
2472 locale2.variant_code = nullptr;
2473
2474 std::vector<const FlutterLocale*> locales;
2475 locales.push_back(&locale1);
2476 locales.push_back(&locale2);
2477
2478 ASSERT_EQ(
2479 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2481
2482 // Fix the invalid code.
2483 locale1.language_code = "en";
2484
2485 ASSERT_EQ(
2486 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2487 kSuccess);
2488
2489 check_latch.Wait();
2490}
2491
2492TEST_F(EmbedderTest, LocalizationCallbacksCalled) {
2493 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2495 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2496 EmbedderConfigBuilder builder(context);
2497 builder.SetSurface(DlISize(1, 1));
2498 auto engine = builder.LaunchEngine();
2499 ASSERT_TRUE(engine.is_valid());
2500 // Wait for the root isolate to launch.
2501 latch.Wait();
2502
2504 std::vector<std::string> supported_locales;
2505 supported_locales.push_back("es");
2506 supported_locales.push_back("MX");
2507 supported_locales.push_back("");
2508 auto result = shell.GetPlatformView()->ComputePlatformResolvedLocales(
2509 supported_locales);
2510
2511 ASSERT_EQ((*result).size(), supported_locales.size()); // 3
2512 ASSERT_EQ((*result)[0], supported_locales[0]);
2513 ASSERT_EQ((*result)[1], supported_locales[1]);
2514 ASSERT_EQ((*result)[2], supported_locales[2]);
2515
2516 engine.reset();
2517}
2518
2523
2524TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) {
2525 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2526
2527 EmbedderConfigBuilder builder(context);
2528 builder.SetSurface(DlISize(1024, 600));
2529 builder.SetCompositor();
2530 builder.SetDartEntrypoint("verify_b143464703");
2531
2532 builder.SetRenderTargetType(
2533 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2534
2535 // setup the screenshot promise.
2536 auto rendered_scene = context.GetNextSceneImage();
2537
2538 fml::CountDownLatch latch(1);
2539 context.GetCompositor().SetNextPresentCallback(
2541 size_t layers_count) {
2542 ASSERT_EQ(layers_count, 2u);
2543
2544 // Layer 0 (Root)
2545 {
2546 FlutterBackingStore backing_store = *layers[0]->backing_store;
2547 backing_store.type = kFlutterBackingStoreTypeSoftware;
2548 backing_store.did_update = true;
2549
2550 FlutterRect paint_region_rects[] = {
2551 FlutterRectMakeLTRB(0, 0, 1024, 600),
2552 };
2553 FlutterRegion paint_region = {
2554 .struct_size = sizeof(FlutterRegion),
2555 .rects_count = 1,
2556 .rects = paint_region_rects,
2557 };
2558 FlutterBackingStorePresentInfo present_info = {
2560 .paint_region = &paint_region,
2561 };
2562
2563 FlutterLayer layer = {};
2564 layer.struct_size = sizeof(layer);
2566 layer.backing_store = &backing_store;
2567 layer.size = FlutterSizeMake(1024.0, 600.0);
2568 layer.offset = FlutterPointMake(0.0, 0.0);
2569 layer.backing_store_present_info = &present_info;
2570
2571 ASSERT_EQ(*layers[0], layer);
2572 }
2573
2574 // Layer 1
2575 {
2578 platform_view.identifier = 42;
2579
2580 FlutterLayer layer = {};
2581 layer.struct_size = sizeof(layer);
2584 layer.size = FlutterSizeMake(1024.0, 540.0);
2585 layer.offset = FlutterPointMake(135.0, 60.0);
2586
2587 ASSERT_EQ(*layers[1], layer);
2588 }
2589
2590 latch.CountDown();
2591 });
2592
2593 context.GetCompositor().SetPlatformViewRendererCallback(
2594 [](const FlutterLayer& layer,
2595 GrDirectContext* context) -> sk_sp<SkImage> {
2597 layer, nullptr /* null because software compositor */);
2598 auto canvas = surface->getCanvas();
2599 FML_CHECK(canvas != nullptr);
2600
2601 switch (layer.platform_view->identifier) {
2602 case 42: {
2603 SkPaint paint;
2604 // See dart test for total order.
2605 paint.setColor(SK_ColorGREEN);
2606 paint.setAlpha(127);
2607 const auto& rect =
2608 SkRect::MakeWH(layer.size.width, layer.size.height);
2609 canvas->drawRect(rect, paint);
2610 } break;
2611 default:
2612 // Asked to render an unknown platform view.
2613 FML_CHECK(false)
2614 << "Test was asked to composite an unknown platform view.";
2615 }
2616
2617 return surface->makeImageSnapshot();
2618 });
2619
2620 auto engine = builder.LaunchEngine();
2621
2622 // Send a window metrics events so frames may be scheduled.
2623 FlutterWindowMetricsEvent event = {};
2624 event.struct_size = sizeof(event);
2625 event.width = 1024;
2626 event.height = 600;
2627 event.pixel_ratio = 1.0;
2628 event.physical_view_inset_top = 0.0;
2629 event.physical_view_inset_right = 0.0;
2630 event.physical_view_inset_bottom = 0.0;
2631 event.physical_view_inset_left = 0.0;
2632 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2633 kSuccess);
2634 ASSERT_TRUE(engine.is_valid());
2635
2636 // wait for scene to be rendered.
2637 latch.Wait();
2638
2639 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
2640 // platforms.
2641#if !defined(FML_OS_LINUX)
2642 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
2643#endif // FML_OS_LINUX
2644 ASSERT_TRUE(
2645 ImageMatchesFixture("verifyb143464703_soft_noxform.png", rendered_scene));
2646}
2647
2648TEST_F(EmbedderTest, CanSendLowMemoryNotification) {
2649 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2650
2651 EmbedderConfigBuilder builder(context);
2652 builder.SetSurface(DlISize(1, 1));
2653
2654 auto engine = builder.LaunchEngine();
2655
2656 ASSERT_TRUE(engine.is_valid());
2657
2658 // TODO(chinmaygarde): The shell ought to have a mechanism for notification
2659 // dispatch that engine subsystems can register handlers to. This would allow
2660 // the raster cache and the secondary context caches to respond to
2661 // notifications. Once that is in place, this test can be updated to actually
2662 // ensure that the dispatched message is visible to engine subsystems.
2664}
2665
2666TEST_F(EmbedderTest, CanPostTaskToAllNativeThreads) {
2668 size_t worker_count = 0;
2669 fml::AutoResetWaitableEvent sync_latch;
2670
2671 // One of the threads that the callback will be posted to is the platform
2672 // thread. So we cannot wait for assertions to complete on the platform
2673 // thread. Create a new thread to manage the engine instance and wait for
2674 // assertions on the test thread.
2675 auto platform_task_runner = CreateNewThread("platform_thread");
2676
2677 platform_task_runner->PostTask([&]() {
2678 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2679 EmbedderConfigBuilder builder(context);
2680 builder.SetSurface(DlISize(1, 1));
2681
2682 engine = builder.LaunchEngine();
2683
2684 ASSERT_TRUE(engine.is_valid());
2685
2686 worker_count = ToEmbedderEngine(engine.get())
2687 ->GetShell()
2688 .GetDartVM()
2690 ->GetWorkerCount();
2691
2692 sync_latch.Signal();
2693 });
2694
2695 sync_latch.Wait();
2696
2697 const auto engine_threads_count = worker_count + 4u;
2698
2699 struct Captures {
2700 // Waits the adequate number of callbacks to fire.
2701 fml::CountDownLatch latch;
2702
2703 // This class will be accessed from multiple threads concurrently to track
2704 // thread specific information that is later checked. All updates to fields
2705 // in this struct must be made with this mutex acquired.
2706
2707 std::mutex captures_mutex;
2708 // Ensures that the expect number of distinct threads were serviced.
2709 std::set<std::thread::id> thread_ids;
2710
2711 size_t platform_threads_count = 0;
2712 size_t render_threads_count = 0;
2713 size_t ui_threads_count = 0;
2714 size_t worker_threads_count = 0;
2715
2716 explicit Captures(size_t count) : latch(count) {}
2717 };
2718
2719 Captures captures(engine_threads_count);
2720
2721 platform_task_runner->PostTask([&]() {
2723 engine.get(),
2724 [](FlutterNativeThreadType type, void* baton) {
2725 auto captures = reinterpret_cast<Captures*>(baton);
2726 {
2727 std::scoped_lock lock(captures->captures_mutex);
2728 switch (type) {
2729 case kFlutterNativeThreadTypeRender:
2730 captures->render_threads_count++;
2731 break;
2732 case kFlutterNativeThreadTypeWorker:
2733 captures->worker_threads_count++;
2734 break;
2735 case kFlutterNativeThreadTypeUI:
2736 captures->ui_threads_count++;
2737 break;
2738 case kFlutterNativeThreadTypePlatform:
2739 captures->platform_threads_count++;
2740 break;
2741 }
2742 captures->thread_ids.insert(std::this_thread::get_id());
2743 }
2744 captures->latch.CountDown();
2745 },
2746 &captures),
2747 kSuccess);
2748 });
2749
2750 captures.latch.Wait();
2751 ASSERT_EQ(captures.thread_ids.size(), engine_threads_count);
2752 ASSERT_EQ(captures.platform_threads_count, 1u);
2753 ASSERT_EQ(captures.render_threads_count, 1u);
2754 ASSERT_EQ(captures.ui_threads_count, 1u);
2755 ASSERT_EQ(captures.worker_threads_count, worker_count + 1u /* for IO */);
2756 EXPECT_GE(captures.worker_threads_count - 1, 2u);
2757 EXPECT_LE(captures.worker_threads_count - 1, 4u);
2758
2759 platform_task_runner->PostTask([&]() {
2760 engine.reset();
2761 sync_latch.Signal();
2762 });
2763 sync_latch.Wait();
2764
2765 // The engine should have already been destroyed on the platform task runner.
2766 ASSERT_FALSE(engine.is_valid());
2767}
2768
2769TEST_F(EmbedderTest, InvalidAOTDataSourcesMustReturnError) {
2771 GTEST_SKIP();
2772 return;
2773 }
2774 FlutterEngineAOTDataSource data_in = {};
2775 FlutterEngineAOTData data_out = nullptr;
2776
2777 // Null source specified.
2778 ASSERT_EQ(FlutterEngineCreateAOTData(nullptr, &data_out), kInvalidArguments);
2779 ASSERT_EQ(data_out, nullptr);
2780
2781 // Null data_out specified.
2782 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, nullptr), kInvalidArguments);
2783
2784 // Invalid FlutterEngineAOTDataSourceType type specified.
2785 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2786 data_in.type = static_cast<FlutterEngineAOTDataSourceType>(-1);
2787 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2788 ASSERT_EQ(data_out, nullptr);
2789
2790 // Invalid ELF path specified.
2792 data_in.elf_path = nullptr;
2793 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2794 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2795 ASSERT_EQ(data_in.elf_path, nullptr);
2796 ASSERT_EQ(data_out, nullptr);
2797
2798 // Invalid ELF path specified.
2799 data_in.elf_path = "";
2800 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2801 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2802 ASSERT_EQ(data_in.elf_path, "");
2803 ASSERT_EQ(data_out, nullptr);
2804
2805 // Could not find VM snapshot data.
2806 data_in.elf_path = "/bin/true";
2807 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2808 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2809 ASSERT_EQ(data_in.elf_path, "/bin/true");
2810 ASSERT_EQ(data_out, nullptr);
2811}
2812
2813TEST_F(EmbedderTest, MustNotRunWithMultipleAOTSources) {
2815 GTEST_SKIP();
2816 return;
2817 }
2818 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2819
2820 EmbedderConfigBuilder builder(
2821 context,
2822 EmbedderConfigBuilder::InitializationPreference::kMultiAOTInitialize);
2823
2824 builder.SetSurface(DlISize(1, 1));
2825
2826 auto engine = builder.LaunchEngine();
2827 ASSERT_FALSE(engine.is_valid());
2828}
2829
2830TEST_F(EmbedderTest, CanCreateAndCollectAValidElfSource) {
2832 GTEST_SKIP();
2833 return;
2834 }
2835 FlutterEngineAOTDataSource data_in = {};
2836 FlutterEngineAOTData data_out = nullptr;
2837
2838 // Collecting a null object should be allowed
2839 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2840
2841 const auto elf_path =
2843
2845 data_in.elf_path = elf_path.c_str();
2846
2847 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kSuccess);
2848 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2849 ASSERT_EQ(data_in.elf_path, elf_path.c_str());
2850 ASSERT_NE(data_out, nullptr);
2851
2852 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2853}
2854
2855TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) {
2857 GTEST_SKIP();
2858 return;
2859 }
2860 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2861
2863 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2864
2865 EmbedderConfigBuilder builder(
2866 context,
2867 EmbedderConfigBuilder::InitializationPreference::kAOTDataInitialize);
2868
2869 builder.SetSurface(DlISize(1, 1));
2870
2871 auto engine = builder.LaunchEngine();
2872 ASSERT_TRUE(engine.is_valid());
2873
2874 // Wait for the root isolate to launch.
2875 latch.Wait();
2876 engine.reset();
2877}
2878
2879#if defined(__clang_analyzer__)
2880#define TEST_VM_SNAPSHOT_DATA "vm_data"
2881#define TEST_VM_SNAPSHOT_INSTRUCTIONS "vm_instructions"
2882#define TEST_ISOLATE_SNAPSHOT_DATA "isolate_data"
2883#define TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS "isolate_instructions"
2884#endif
2885
2886//------------------------------------------------------------------------------
2887/// PopulateJITSnapshotMappingCallbacks should successfully change the callbacks
2888/// of the snapshots in the engine's settings when JIT snapshots are explicitly
2889/// defined.
2890///
2891TEST_F(EmbedderTest, CanSuccessfullyPopulateSpecificJITSnapshotCallbacks) {
2892// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2893#if defined(OS_FUCHSIA)
2894 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2895#else
2896
2897 // This test is only relevant in JIT mode.
2899 GTEST_SKIP();
2900 return;
2901 }
2902
2903 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2904 EmbedderConfigBuilder builder(context);
2905 builder.SetSurface(DlISize(1, 1));
2906
2907 // Construct the location of valid JIT snapshots.
2908 const std::string src_path = GetSourcePath();
2909 const std::string vm_snapshot_data =
2910 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2911 const std::string vm_snapshot_instructions =
2912 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2913 const std::string isolate_snapshot_data =
2914 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2915 const std::string isolate_snapshot_instructions =
2916 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2917
2918 // Explicitly define the locations of the JIT snapshots
2920 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2922 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2924 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2926 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2927
2928 auto engine = builder.LaunchEngine();
2929
2931 const Settings settings = shell.GetSettings();
2932
2933 ASSERT_NE(settings.vm_snapshot_data(), nullptr);
2934 ASSERT_NE(settings.vm_snapshot_instr(), nullptr);
2935 ASSERT_NE(settings.isolate_snapshot_data(), nullptr);
2936 ASSERT_NE(settings.isolate_snapshot_instr(), nullptr);
2937 ASSERT_NE(settings.dart_library_sources_kernel(), nullptr);
2938#endif // OS_FUCHSIA
2939}
2940
2941//------------------------------------------------------------------------------
2942/// PopulateJITSnapshotMappingCallbacks should still be able to successfully
2943/// change the callbacks of the snapshots in the engine's settings when JIT
2944/// snapshots are explicitly defined. However, if those snapshot locations are
2945/// invalid, the callbacks should return a nullptr.
2946///
2947TEST_F(EmbedderTest, JITSnapshotCallbacksFailWithInvalidLocation) {
2948// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2949#if defined(OS_FUCHSIA)
2950 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2951#else
2952
2953 // This test is only relevant in JIT mode.
2955 GTEST_SKIP();
2956 return;
2957 }
2958
2959 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2960 EmbedderConfigBuilder builder(context);
2961 builder.SetSurface(DlISize(1, 1));
2962
2963 // Explicitly define the locations of the invalid JIT snapshots
2965 reinterpret_cast<const uint8_t*>("invalid_vm_data");
2967 reinterpret_cast<const uint8_t*>("invalid_vm_instructions");
2969 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2971 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2972
2973 auto engine = builder.LaunchEngine();
2974
2976 const Settings settings = shell.GetSettings();
2977
2978 ASSERT_EQ(settings.vm_snapshot_data(), nullptr);
2979 ASSERT_EQ(settings.vm_snapshot_instr(), nullptr);
2980 ASSERT_EQ(settings.isolate_snapshot_data(), nullptr);
2981 ASSERT_EQ(settings.isolate_snapshot_instr(), nullptr);
2982#endif // OS_FUCHSIA
2983}
2984
2985//------------------------------------------------------------------------------
2986/// The embedder must be able to run explicitly specified snapshots in JIT mode
2987/// (i.e. when those are present in known locations).
2988///
2989TEST_F(EmbedderTest, CanLaunchEngineWithSpecifiedJITSnapshots) {
2990 // This test is only relevant in JIT mode.
2992 GTEST_SKIP();
2993 return;
2994 }
2995
2996 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2997 EmbedderConfigBuilder builder(context);
2998 builder.SetSurface(DlISize(1, 1));
2999
3000 // Construct the location of valid JIT snapshots.
3001 const std::string src_path = GetSourcePath();
3002 const std::string vm_snapshot_data =
3003 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
3004 const std::string vm_snapshot_instructions =
3005 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
3006 const std::string isolate_snapshot_data =
3007 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
3008 const std::string isolate_snapshot_instructions =
3009 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
3010
3011 // Explicitly define the locations of the JIT snapshots
3013 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
3015 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
3017 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
3019 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
3020
3021 auto engine = builder.LaunchEngine();
3022 ASSERT_TRUE(engine.is_valid());
3023}
3024
3025//------------------------------------------------------------------------------
3026/// The embedder must be able to run in JIT mode when only some snapshots are
3027/// specified.
3028///
3029TEST_F(EmbedderTest, CanLaunchEngineWithSomeSpecifiedJITSnapshots) {
3030 // This test is only relevant in JIT mode.
3032 GTEST_SKIP();
3033 return;
3034 }
3035
3036 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3037 EmbedderConfigBuilder builder(context);
3038 builder.SetSurface(DlISize(1, 1));
3039
3040 // Construct the location of valid JIT snapshots.
3041 const std::string src_path = GetSourcePath();
3042 const std::string vm_snapshot_data =
3043 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
3044 const std::string vm_snapshot_instructions =
3045 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
3046
3047 // Explicitly define the locations of the JIT snapshots
3049 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
3051 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
3052
3053 auto engine = builder.LaunchEngine();
3054 ASSERT_TRUE(engine.is_valid());
3055}
3056
3057//------------------------------------------------------------------------------
3058/// The embedder must be able to run in JIT mode even when the specfied
3059/// snapshots are invalid. It should be able to resolve them as it would when
3060/// the snapshots are not specified.
3061///
3062TEST_F(EmbedderTest, CanLaunchEngineWithInvalidJITSnapshots) {
3063 // This test is only relevant in JIT mode.
3065 GTEST_SKIP();
3066 return;
3067 }
3068
3069 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3070 EmbedderConfigBuilder builder(context);
3071 builder.SetSurface(DlISize(1, 1));
3072
3073 // Explicitly define the locations of the JIT snapshots
3075 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
3077 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
3078
3079 auto engine = builder.LaunchEngine();
3080 ASSERT_TRUE(engine.is_valid());
3082}
3083
3084//------------------------------------------------------------------------------
3085/// The embedder must be able to launch even when the snapshots are not
3086/// explicitly defined in JIT mode. It must be able to resolve those snapshots.
3087///
3088TEST_F(EmbedderTest, CanLaunchEngineWithUnspecifiedJITSnapshots) {
3089 // This test is only relevant in JIT mode.
3091 GTEST_SKIP();
3092 return;
3093 }
3094
3095 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3096 EmbedderConfigBuilder builder(context);
3097 builder.SetSurface(DlISize(1, 1));
3098
3099 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_data, nullptr);
3100 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_instructions, nullptr);
3101 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_data, nullptr);
3102 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_instructions, nullptr);
3103
3104 auto engine = builder.LaunchEngine();
3105 ASSERT_TRUE(engine.is_valid());
3106}
3107
3108TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) {
3109 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3110 EmbedderConfigBuilder builder(context);
3111 builder.SetSurface(DlISize(1, 1));
3112 auto engine = builder.LaunchEngine();
3113
3114 ASSERT_TRUE(engine.is_valid());
3115
3116 FlutterWindowMetricsEvent event = {};
3117 event.struct_size = sizeof(event);
3118 event.width = 800;
3119 event.height = 600;
3120 event.pixel_ratio = 0.0;
3121 event.physical_view_inset_top = 0.0;
3122 event.physical_view_inset_right = 0.0;
3123 event.physical_view_inset_bottom = 0.0;
3124 event.physical_view_inset_left = 0.0;
3125
3126 // Pixel ratio must be positive.
3127 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3129
3130 event.pixel_ratio = 1.0;
3131 event.physical_view_inset_top = -1.0;
3132 event.physical_view_inset_right = -1.0;
3133 event.physical_view_inset_bottom = -1.0;
3134 event.physical_view_inset_left = -1.0;
3135
3136 // Physical view insets must be non-negative.
3137 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3139
3140 event.physical_view_inset_top = 700;
3141 event.physical_view_inset_right = 900;
3142 event.physical_view_inset_bottom = 700;
3143 event.physical_view_inset_left = 900;
3144
3145 // Top/bottom insets cannot be greater than height.
3146 // Left/right insets cannot be greater than width.
3147 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3149}
3150
3152 EmbedderTest& test,
3153 std::string entrypoint,
3155 const std::vector<uint8_t>& bytes) {
3156 auto& context = test.GetEmbedderContext<EmbedderTestContextSoftware>();
3157
3158 EmbedderConfigBuilder builder(context);
3160 bool matches = false;
3161
3162 builder.SetSurface(DlISize(1, 1));
3163 builder.SetCompositor();
3164 builder.SetDartEntrypoint(std::move(entrypoint));
3165 builder.SetRenderTargetType(
3166 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer2,
3167 pixfmt);
3168
3169 auto engine = builder.LaunchEngine();
3170 ASSERT_TRUE(engine.is_valid());
3171
3172 context.GetCompositor().SetNextPresentCallback(
3173 [&context, &matches, &bytes, &latch](FlutterViewId view_id,
3174 const FlutterLayer** layers,
3175 size_t layers_count) {
3177 ASSERT_EQ(layers[0]->backing_store->type,
3179 sk_sp<SkSurface> surface =
3180 context.GetCompositor().GetSurface(layers[0]->backing_store);
3181 matches = SurfacePixelDataMatchesBytes(surface.get(), bytes);
3182 latch.Signal();
3183 });
3184
3185 // Send a window metrics events so frames may be scheduled.
3186 FlutterWindowMetricsEvent event = {};
3187 event.struct_size = sizeof(event);
3188 event.width = 1;
3189 event.height = 1;
3190 event.pixel_ratio = 1.0;
3191 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3192 kSuccess);
3193
3194 latch.Wait();
3195 ASSERT_TRUE(matches);
3196
3197 engine.reset();
3198}
3199
3200template <typename T>
3202 EmbedderTest& test,
3203 std::string entrypoint,
3205 T pixelvalue) {
3206 uint8_t* bytes = reinterpret_cast<uint8_t*>(&pixelvalue);
3208 test, std::move(entrypoint), pixfmt,
3209 std::vector<uint8_t>(bytes, bytes + sizeof(T)));
3210}
3211
3212#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher) \
3213 TEST_F(EmbedderTest, SoftwareRenderingPixelFormats##test_name) { \
3214 expectSoftwareRenderingOutputMatches(*this, #dart_entrypoint, pixfmt, \
3215 matcher); \
3216 }
3217
3218// Don't test the pixel formats that contain padding (so an X) and the
3219// kFlutterSoftwarePixelFormatNative32 pixel format here, so we don't add any
3220// flakiness.
3221SW_PIXFMT_TEST_F(RedRGBA565xF800,
3222 draw_solid_red,
3224 (uint16_t)0xF800);
3225SW_PIXFMT_TEST_F(RedRGBA4444xF00F,
3226 draw_solid_red,
3228 (uint16_t)0xF00F);
3229SW_PIXFMT_TEST_F(RedRGBA8888xFFx00x00xFF,
3230 draw_solid_red,
3232 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3233SW_PIXFMT_TEST_F(RedBGRA8888x00x00xFFxFF,
3234 draw_solid_red,
3236 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3238 draw_solid_red,
3240 (uint8_t)0x36);
3241
3242SW_PIXFMT_TEST_F(GreenRGB565x07E0,
3243 draw_solid_green,
3245 (uint16_t)0x07E0);
3246SW_PIXFMT_TEST_F(GreenRGBA4444x0F0F,
3247 draw_solid_green,
3249 (uint16_t)0x0F0F);
3250SW_PIXFMT_TEST_F(GreenRGBA8888x00xFFx00xFF,
3251 draw_solid_green,
3253 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3254SW_PIXFMT_TEST_F(GreenBGRA8888x00xFFx00xFF,
3255 draw_solid_green,
3257 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3258SW_PIXFMT_TEST_F(GreenGray8xB6,
3259 draw_solid_green,
3261 (uint8_t)0xB6);
3262
3263SW_PIXFMT_TEST_F(BlueRGB565x001F,
3264 draw_solid_blue,
3266 (uint16_t)0x001F);
3267SW_PIXFMT_TEST_F(BlueRGBA4444x00FF,
3268 draw_solid_blue,
3270 (uint16_t)0x00FF);
3271SW_PIXFMT_TEST_F(BlueRGBA8888x00x00xFFxFF,
3272 draw_solid_blue,
3274 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3275SW_PIXFMT_TEST_F(BlueBGRA8888xFFx00x00xFF,
3276 draw_solid_blue,
3278 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3279SW_PIXFMT_TEST_F(BlueGray8x12,
3280 draw_solid_blue,
3282 (uint8_t)0x12);
3283
3284//------------------------------------------------------------------------------
3285// Key Data
3286//------------------------------------------------------------------------------
3287
3288typedef struct {
3289 std::shared_ptr<fml::AutoResetWaitableEvent> latch;
3292
3293// Convert `kind` in integer form to its enum form.
3294//
3295// It performs a revesed mapping from `_serializeKeyEventType`
3296// in shell/platform/embedder/fixtures/main.dart.
3298 switch (kind) {
3299 case 1:
3301 case 2:
3303 case 3:
3305 default:
3308 }
3309}
3310
3311// Convert `source` in integer form to its enum form.
3312//
3313// It performs a revesed mapping from `_serializeKeyEventDeviceType`
3314// in shell/platform/embedder/fixtures/main.dart.
3316 switch (source) {
3317 case 1:
3319 case 2:
3321 case 3:
3323 case 4:
3325 case 5:
3327 default:
3330 }
3331}
3332
3333// Checks the equality of two `FlutterKeyEvent` by each of their members except
3334// for `character`. The `character` must be checked separately.
3336 const FlutterKeyEvent& baseline) {
3337 EXPECT_EQ(subject.timestamp, baseline.timestamp);
3338 EXPECT_EQ(subject.type, baseline.type);
3339 EXPECT_EQ(subject.physical, baseline.physical);
3340 EXPECT_EQ(subject.logical, baseline.logical);
3341 EXPECT_EQ(subject.synthesized, baseline.synthesized);
3342 EXPECT_EQ(subject.device_type, baseline.device_type);
3343}
3344
3345TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) {
3346 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3347 uint64_t echoed_char;
3348 FlutterKeyEvent echoed_event;
3349 echoed_event.struct_size = sizeof(FlutterKeyEvent);
3350
3351 auto native_echo_event = [&](Dart_NativeArguments args) {
3352 echoed_event.type =
3354 Dart_GetNativeArgument(args, 0)));
3355 echoed_event.timestamp =
3356 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3357 Dart_GetNativeArgument(args, 1)));
3359 Dart_GetNativeArgument(args, 2));
3361 Dart_GetNativeArgument(args, 3));
3363 Dart_GetNativeArgument(args, 4));
3364 echoed_event.synthesized =
3365 tonic::DartConverter<bool>::FromDart(Dart_GetNativeArgument(args, 5));
3366 echoed_event.device_type =
3368 Dart_GetNativeArgument(args, 6)));
3369
3370 message_latch->Signal();
3371 };
3372
3373 auto platform_task_runner = CreateNewThread("platform_thread");
3374
3377 platform_task_runner->PostTask([&]() {
3378 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3379 EmbedderConfigBuilder builder(context);
3380 builder.SetSurface(DlISize(1, 1));
3381 builder.SetDartEntrypoint("key_data_echo");
3383 [&](const FlutterPlatformMessage* message) {
3385 engine.get(), message->response_handle, nullptr, 0);
3386 });
3387 context.AddNativeCallback(
3388 "SignalNativeTest",
3390 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3391
3392 context.AddNativeCallback("EchoKeyEvent",
3393 CREATE_NATIVE_ENTRY(native_echo_event));
3394
3395 engine = builder.LaunchEngine();
3396 ASSERT_TRUE(engine.is_valid());
3397 });
3398
3399 ready.Wait();
3400
3401 // A normal down event
3402 const FlutterKeyEvent down_event_upper_a{
3403 .struct_size = sizeof(FlutterKeyEvent),
3404 .timestamp = 1,
3406 .physical = 0x00070004,
3407 .logical = 0x00000000061,
3408 .character = "A",
3409 .synthesized = false,
3411 };
3412 platform_task_runner->PostTask([&]() {
3413 FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, nullptr,
3414 nullptr);
3415 });
3416 message_latch->Wait();
3417
3418 ExpectKeyEventEq(echoed_event, down_event_upper_a);
3419 EXPECT_EQ(echoed_char, 0x41llu);
3420
3421 // A repeat event with multi-byte character
3422 const FlutterKeyEvent repeat_event_wide_char{
3423 .struct_size = sizeof(FlutterKeyEvent),
3424 .timestamp = 1000,
3426 .physical = 0x00070005,
3427 .logical = 0x00000000062,
3428 .character = "∆",
3429 .synthesized = false,
3431 };
3432 platform_task_runner->PostTask([&]() {
3433 FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, nullptr,
3434 nullptr);
3435 });
3436 message_latch->Wait();
3437
3438 ExpectKeyEventEq(echoed_event, repeat_event_wide_char);
3439 EXPECT_EQ(echoed_char, 0x2206llu);
3440
3441 // An up event with no character, synthesized
3442 const FlutterKeyEvent up_event{
3443 .struct_size = sizeof(FlutterKeyEvent),
3444 .timestamp = 1000000,
3446 .physical = 0x00070006,
3447 .logical = 0x00000000063,
3448 .character = nullptr,
3449 .synthesized = true,
3451 };
3452 platform_task_runner->PostTask([&]() {
3453 FlutterEngineSendKeyEvent(engine.get(), &up_event, nullptr, nullptr);
3454 });
3455 message_latch->Wait();
3456
3457 ExpectKeyEventEq(echoed_event, up_event);
3458 EXPECT_EQ(echoed_char, 0llu);
3459
3460 fml::AutoResetWaitableEvent shutdown_latch;
3461 platform_task_runner->PostTask([&]() {
3462 engine.reset();
3463 shutdown_latch.Signal();
3464 });
3465 shutdown_latch.Wait();
3466}
3467
3468TEST_F(EmbedderTest, KeyDataAreBuffered) {
3469 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3470 std::vector<FlutterKeyEvent> echoed_events;
3471
3472 auto native_echo_event = [&](Dart_NativeArguments args) {
3473 echoed_events.push_back(FlutterKeyEvent{
3474 .timestamp =
3475 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3476 Dart_GetNativeArgument(args, 1))),
3477 .type =
3479 Dart_GetNativeArgument(args, 0))),
3481 Dart_GetNativeArgument(args, 2)),
3483 Dart_GetNativeArgument(args, 3)),
3485 Dart_GetNativeArgument(args, 5)),
3486 .device_type = UnserializeKeyEventDeviceType(
3488 Dart_GetNativeArgument(args, 6))),
3489 });
3490
3491 message_latch->Signal();
3492 };
3493
3494 auto platform_task_runner = CreateNewThread("platform_thread");
3495
3498 platform_task_runner->PostTask([&]() {
3499 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3500 EmbedderConfigBuilder builder(context);
3501 builder.SetSurface(DlISize(1, 1));
3502 builder.SetDartEntrypoint("key_data_late_echo");
3504 [&](const FlutterPlatformMessage* message) {
3506 engine.get(), message->response_handle, nullptr, 0);
3507 });
3508 context.AddNativeCallback(
3509 "SignalNativeTest",
3511 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3512
3513 context.AddNativeCallback("EchoKeyEvent",
3514 CREATE_NATIVE_ENTRY(native_echo_event));
3515
3516 engine = builder.LaunchEngine();
3517 ASSERT_TRUE(engine.is_valid());
3518 });
3519 ready.Wait();
3520
3521 FlutterKeyEvent sample_event{
3522 .struct_size = sizeof(FlutterKeyEvent),
3524 .physical = 0x00070004,
3525 .logical = 0x00000000061,
3526 .character = "A",
3527 .synthesized = false,
3529 };
3530
3531 // Send an event.
3532 sample_event.timestamp = 1.0l;
3533 platform_task_runner->PostTask([&]() {
3534 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3535 message_latch->Signal();
3536 });
3537 message_latch->Wait();
3538
3539 // Should not receive echos because the callback is not set yet.
3540 EXPECT_EQ(echoed_events.size(), 0u);
3541
3542 // Send an empty message to 'test/starts_echo' to start echoing.
3543 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
3545 engine.get(), [](const uint8_t* data, size_t size, void* user_data) {},
3546 nullptr, &response_handle);
3547
3550 .channel = "test/starts_echo",
3551 .message = nullptr,
3552 .message_size = 0,
3553 .response_handle = response_handle,
3554 };
3555
3556 platform_task_runner->PostTask([&]() {
3557 FlutterEngineResult result =
3559 ASSERT_EQ(result, kSuccess);
3560
3562 });
3563
3564 // message_latch->Wait();
3565 message_latch->Wait();
3566 // All previous events should be received now.
3567 EXPECT_EQ(echoed_events.size(), 1u);
3568
3569 // Send a second event.
3570 sample_event.timestamp = 10.0l;
3571 platform_task_runner->PostTask([&]() {
3572 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3573 });
3574 message_latch->Wait();
3575
3576 // The event should be echoed, too.
3577 EXPECT_EQ(echoed_events.size(), 2u);
3578
3579 fml::AutoResetWaitableEvent shutdown_latch;
3580 platform_task_runner->PostTask([&]() {
3581 engine.reset();
3582 shutdown_latch.Signal();
3583 });
3584 shutdown_latch.Wait();
3585}
3586
3587TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) {
3589 fml::AutoResetWaitableEvent sync_latch;
3591
3592 // One of the threads that the key data callback will be posted to is the
3593 // platform thread. So we cannot wait for assertions to complete on the
3594 // platform thread. Create a new thread to manage the engine instance and wait
3595 // for assertions on the test thread.
3596 auto platform_task_runner = CreateNewThread("platform_thread");
3597
3598 platform_task_runner->PostTask([&]() {
3599 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3600 EmbedderConfigBuilder builder(context);
3601 builder.SetSurface(DlISize(1, 1));
3602 builder.SetDartEntrypoint("key_data_echo");
3603 context.AddNativeCallback(
3604 "SignalNativeTest",
3606 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3607 context.AddNativeCallback(
3608 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3609
3610 engine = builder.LaunchEngine();
3611 ASSERT_TRUE(engine.is_valid());
3612
3613 sync_latch.Signal();
3614 });
3615 sync_latch.Wait();
3616 ready.Wait();
3617
3618 // Dispatch a single event
3619 FlutterKeyEvent event{
3620 .struct_size = sizeof(FlutterKeyEvent),
3621 .timestamp = 1000,
3623 .physical = 0x00070005,
3624 .logical = 0x00000000062,
3625 .character = nullptr,
3627 };
3628
3629 KeyEventUserData user_data1{
3630 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3631 };
3632 // Entrypoint `key_data_echo` returns `event.synthesized` as `handled`.
3633 event.synthesized = true;
3634 platform_task_runner->PostTask([&]() {
3635 // Test when the response callback is empty.
3636 // It should not cause a crash.
3637 FlutterEngineSendKeyEvent(engine.get(), &event, nullptr, nullptr);
3638
3639 // Test when the response callback is non-empty.
3640 // It should be invoked (so that the latch can be unlocked.)
3642 engine.get(), &event,
3643 [](bool handled, void* untyped_user_data) {
3644 KeyEventUserData* user_data =
3645 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3646 EXPECT_EQ(handled, true);
3647 user_data->latch->Signal();
3648 },
3649 &user_data1);
3650 });
3651 user_data1.latch->Wait();
3652 fml::AutoResetWaitableEvent shutdown_latch;
3653 platform_task_runner->PostTask([&]() {
3654 engine.reset();
3655 shutdown_latch.Signal();
3656 });
3657 shutdown_latch.Wait();
3658}
3659
3660TEST_F(EmbedderTest, BackToBackKeyEventResponsesCorrectlyInvoked) {
3662 fml::AutoResetWaitableEvent sync_latch;
3664
3665 // One of the threads that the callback will be posted to is the platform
3666 // thread. So we cannot wait for assertions to complete on the platform
3667 // thread. Create a new thread to manage the engine instance and wait for
3668 // assertions on the test thread.
3669 auto platform_task_runner = CreateNewThread("platform_thread");
3670
3671 platform_task_runner->PostTask([&]() {
3672 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3673 EmbedderConfigBuilder builder(context);
3674 builder.SetSurface(DlISize(1, 1));
3675 builder.SetDartEntrypoint("key_data_echo");
3676 context.AddNativeCallback(
3677 "SignalNativeTest",
3679 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3680
3681 context.AddNativeCallback(
3682 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3683
3684 engine = builder.LaunchEngine();
3685 ASSERT_TRUE(engine.is_valid());
3686
3687 sync_latch.Signal();
3688 });
3689 sync_latch.Wait();
3690 ready.Wait();
3691
3692 // Dispatch a single event
3693 FlutterKeyEvent event{
3694 .struct_size = sizeof(FlutterKeyEvent),
3695 .timestamp = 1000,
3697 .physical = 0x00070005,
3698 .logical = 0x00000000062,
3699 .character = nullptr,
3700 .synthesized = false,
3702 };
3703
3704 // Dispatch two events back to back, using the same callback on different
3705 // user_data
3706 KeyEventUserData user_data2{
3707 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3708 .returned = false,
3709 };
3710 KeyEventUserData user_data3{
3711 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3712 .returned = false,
3713 };
3714 auto callback23 = [](bool handled, void* untyped_user_data) {
3716 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3717 EXPECT_EQ(handled, false);
3718 user_data->returned = true;
3719 user_data->latch->Signal();
3720 };
3721 platform_task_runner->PostTask([&]() {
3722 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2);
3723 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3);
3724 });
3725 user_data2.latch->Wait();
3726 user_data3.latch->Wait();
3727
3728 EXPECT_TRUE(user_data2.returned);
3729 EXPECT_TRUE(user_data3.returned);
3730
3731 fml::AutoResetWaitableEvent shutdown_latch;
3732 platform_task_runner->PostTask([&]() {
3733 engine.reset();
3734 shutdown_latch.Signal();
3735 });
3736 shutdown_latch.Wait();
3737}
3738
3739//------------------------------------------------------------------------------
3740// Vsync waiter
3741//------------------------------------------------------------------------------
3742
3743// This test schedules a frame for the future and asserts that vsync waiter
3744// posts the event at the right frame start time (which is in the future).
3745TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) {
3747 fml::AutoResetWaitableEvent present_latch;
3748 fml::AutoResetWaitableEvent vsync_latch;
3749
3750 // One of the threads that the callback (FlutterEngineOnVsync) will be posted
3751 // to is the platform thread. So we cannot wait for assertions to complete on
3752 // the platform thread. Create a new thread to manage the engine instance and
3753 // wait for assertions on the test thread.
3754 auto platform_task_runner = CreateNewThread("platform_thread");
3755
3756 platform_task_runner->PostTask([&]() {
3757 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3758 context.SetVsyncCallback([&](intptr_t baton) {
3759 platform_task_runner->PostTask([baton = baton, &engine, &vsync_latch]() {
3760 FlutterEngineOnVsync(engine.get(), baton, NanosFromEpoch(16),
3761 NanosFromEpoch(32));
3762 vsync_latch.Signal();
3763 });
3764 });
3765 context.AddNativeCallback(
3766 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3767 present_latch.Signal();
3768 }));
3769
3770 EmbedderConfigBuilder builder(context);
3771 builder.SetSurface(DlISize(1, 1));
3772 builder.SetupVsyncCallback();
3773 builder.SetDartEntrypoint("empty_scene");
3774 engine = builder.LaunchEngine();
3775 ASSERT_TRUE(engine.is_valid());
3776
3777 // Send a window metrics events so frames may be scheduled.
3778 FlutterWindowMetricsEvent event = {};
3779 event.struct_size = sizeof(event);
3780 event.width = 800;
3781 event.height = 600;
3782 event.pixel_ratio = 1.0;
3783
3784 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3785 kSuccess);
3786 });
3787
3788 vsync_latch.Wait();
3789 present_latch.Wait();
3790
3791 fml::AutoResetWaitableEvent shutdown_latch;
3792 platform_task_runner->PostTask([&]() {
3793 engine.reset();
3794 shutdown_latch.Signal();
3795 });
3796 shutdown_latch.Wait();
3797}
3798
3799TEST_F(EmbedderTest, CanScheduleFrame) {
3800 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3801 EmbedderConfigBuilder builder(context);
3802 builder.SetSurface(DlISize(1, 1));
3803 builder.SetDartEntrypoint("can_schedule_frame");
3805 context.AddNativeCallback(
3806 "SignalNativeTest",
3808 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
3809
3810 fml::AutoResetWaitableEvent check_latch;
3811 context.AddNativeCallback(
3812 "SignalNativeCount",
3814 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
3815
3816 auto engine = builder.LaunchEngine();
3817 ASSERT_TRUE(engine.is_valid());
3818
3819 // Wait for the application to attach the listener.
3820 latch.Wait();
3821
3822 ASSERT_EQ(FlutterEngineScheduleFrame(engine.get()), kSuccess);
3823
3824 check_latch.Wait();
3825}
3826
3827TEST_F(EmbedderTest, CanSetNextFrameCallback) {
3828 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3829 EmbedderConfigBuilder builder(context);
3830 builder.SetSurface(DlISize(1, 1));
3831 builder.SetDartEntrypoint("draw_solid_red");
3832
3833 auto engine = builder.LaunchEngine();
3834 ASSERT_TRUE(engine.is_valid());
3835
3836 // Register the callback that is executed once the next frame is drawn.
3837 fml::AutoResetWaitableEvent callback_latch;
3838 VoidCallback callback = [](void* user_data) {
3839 fml::AutoResetWaitableEvent* callback_latch =
3841
3842 callback_latch->Signal();
3843 };
3844
3846 &callback_latch);
3847 ASSERT_EQ(result, kSuccess);
3848
3849 // Send a window metrics events so frames may be scheduled.
3850 FlutterWindowMetricsEvent event = {};
3851 event.struct_size = sizeof(event);
3852 event.width = 800;
3853 event.height = 600;
3854 event.pixel_ratio = 1.0;
3855 event.physical_view_inset_top = 0.0;
3856 event.physical_view_inset_right = 0.0;
3857 event.physical_view_inset_bottom = 0.0;
3858 event.physical_view_inset_left = 0.0;
3859 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3860 kSuccess);
3861
3862 callback_latch.Wait();
3863}
3864
3865#if defined(FML_OS_MACOSX)
3866
3867static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
3868 pthread_t tid = pthread_self();
3869 struct sched_param param;
3870 int policy = SCHED_OTHER;
3871 switch (config.priority) {
3873 param.sched_priority = 10;
3874 break;
3875 default:
3876 param.sched_priority = 1;
3877 }
3878 pthread_setschedparam(tid, policy, &param);
3879}
3880
3881TEST_F(EmbedderTest, EmbedderThreadHostUseCustomThreadConfig) {
3882 auto thread_host =
3884 nullptr, MockThreadConfigSetter);
3885
3887 int ui_policy;
3888 struct sched_param ui_param;
3889
3890 thread_host->GetTaskRunners().GetUITaskRunner()->PostTask([&] {
3891 pthread_t current_thread = pthread_self();
3892 pthread_getschedparam(current_thread, &ui_policy, &ui_param);
3893 ASSERT_EQ(ui_param.sched_priority, 10);
3894 ui_latch.Signal();
3895 });
3896
3898 int io_policy;
3899 struct sched_param io_param;
3900 thread_host->GetTaskRunners().GetIOTaskRunner()->PostTask([&] {
3901 pthread_t current_thread = pthread_self();
3902 pthread_getschedparam(current_thread, &io_policy, &io_param);
3903 ASSERT_EQ(io_param.sched_priority, 1);
3904 io_latch.Signal();
3905 });
3906
3907 ui_latch.Wait();
3908 io_latch.Wait();
3909}
3910#endif
3911
3912/// Send a pointer event to Dart and wait until the Dart code signals
3913/// it received the event.
3914TEST_F(EmbedderTest, CanSendPointer) {
3915 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3916 EmbedderConfigBuilder builder(context);
3917 builder.SetSurface(DlISize(1, 1));
3918 builder.SetDartEntrypoint("pointer_data_packet");
3919
3920 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
3921 context.AddNativeCallback(
3922 "SignalNativeTest",
3924 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3925 context.AddNativeCallback(
3926 "SignalNativeCount",
3927 CREATE_NATIVE_ENTRY([&count_latch](Dart_NativeArguments args) {
3929 Dart_GetNativeArgument(args, 0));
3930 ASSERT_EQ(count, 1);
3931 count_latch.Signal();
3932 }));
3933 context.AddNativeCallback(
3934 "SignalNativeMessage",
3935 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
3937 Dart_GetNativeArgument(args, 0));
3938 ASSERT_EQ("PointerData(viewId: 0, x: 123.0, y: 456.0)", message);
3939 message_latch.Signal();
3940 }));
3941
3942 auto engine = builder.LaunchEngine();
3943 ASSERT_TRUE(engine.is_valid());
3944
3945 ready_latch.Wait();
3946
3947 FlutterPointerEvent pointer_event = {};
3948 pointer_event.struct_size = sizeof(FlutterPointerEvent);
3949 pointer_event.phase = FlutterPointerPhase::kAdd;
3950 pointer_event.x = 123;
3951 pointer_event.y = 456;
3952 pointer_event.timestamp = static_cast<size_t>(1234567890);
3953 pointer_event.view_id = 0;
3954
3955 FlutterEngineResult result =
3956 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
3957 ASSERT_EQ(result, kSuccess);
3958
3959 count_latch.Wait();
3960 message_latch.Wait();
3961}
3962
3963/// Send a pointer event to Dart and wait until the Dart code echos with the
3964/// view ID.
3965TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
3966 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3967 EmbedderConfigBuilder builder(context);
3968 builder.SetSurface(DlISize(1, 1));
3969 builder.SetDartEntrypoint("pointer_data_packet_view_id");
3970
3971 fml::AutoResetWaitableEvent ready_latch, add_view_latch, message_latch;
3972 context.AddNativeCallback(
3973 "SignalNativeTest",
3975 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3976 context.AddNativeCallback(
3977 "SignalNativeMessage",
3978 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
3980 Dart_GetNativeArgument(args, 0));
3981 ASSERT_EQ("ViewID: 2", message);
3982 message_latch.Signal();
3983 }));
3984
3985 auto engine = builder.LaunchEngine();
3986 ASSERT_TRUE(engine.is_valid());
3987 ready_latch.Wait();
3988
3989 // Add view 2
3990 FlutterWindowMetricsEvent metrics = {};
3991 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
3992 metrics.width = 800;
3993 metrics.height = 600;
3994 metrics.pixel_ratio = 1.0;
3995 metrics.view_id = 2;
3996
3997 FlutterAddViewInfo info = {};
3998 info.struct_size = sizeof(FlutterAddViewInfo);
3999 info.view_id = 2;
4000 info.view_metrics = &metrics;
4001 info.add_view_callback = [](const FlutterAddViewResult* result) {
4002 EXPECT_TRUE(result->added);
4003 fml::AutoResetWaitableEvent* add_view_latch =
4004 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
4005 add_view_latch->Signal();
4006 };
4007 info.user_data = &add_view_latch;
4008 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
4009 add_view_latch.Wait();
4010
4011 // Send a pointer event for view 2
4012 FlutterPointerEvent pointer_event = {};
4013 pointer_event.struct_size = sizeof(FlutterPointerEvent);
4014 pointer_event.phase = FlutterPointerPhase::kAdd;
4015 pointer_event.x = 123;
4016 pointer_event.y = 456;
4017 pointer_event.timestamp = static_cast<size_t>(1234567890);
4018 pointer_event.view_id = 2;
4019
4020 FlutterEngineResult result =
4021 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
4022 ASSERT_EQ(result, kSuccess);
4023
4024 message_latch.Wait();
4025}
4026
4027TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
4028 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4029 EmbedderConfigBuilder builder(context);
4030 builder.SetSurface(DlISize(1, 1));
4031 builder.SetDartEntrypoint("window_metrics_event_view_id");
4032
4033 fml::AutoResetWaitableEvent ready_latch, message_latch;
4034 context.AddNativeCallback(
4035 "SignalNativeTest",
4037 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4038 context.AddNativeCallback(
4039 "SignalNativeMessage",
4040 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4042 Dart_GetNativeArgument(args, 0));
4043 ASSERT_EQ("Changed: [0]", message);
4044 message_latch.Signal();
4045 }));
4046
4047 auto engine = builder.LaunchEngine();
4048 ASSERT_TRUE(engine.is_valid());
4049
4050 ready_latch.Wait();
4051
4052 FlutterWindowMetricsEvent event = {};
4053 // Simulate an event that comes from an old version of embedder.h that doesn't
4054 // have the view_id field.
4056 event.width = 200;
4057 event.height = 300;
4058 event.pixel_ratio = 1.5;
4059 // Skip assigning event.view_id here to test the default behavior.
4060
4061 FlutterEngineResult result =
4063 ASSERT_EQ(result, kSuccess);
4064
4065 message_latch.Wait();
4066}
4067
4068TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
4069 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4070 EmbedderConfigBuilder builder(context);
4071 builder.SetSurface(DlISize(1, 1));
4072 builder.SetDartEntrypoint("window_metrics_event_view_id");
4073
4074 fml::AutoResetWaitableEvent ready_latch, message_latch;
4075 context.AddNativeCallback(
4076 "SignalNativeTest",
4078 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4079
4080 context.AddNativeCallback(
4081 "SignalNativeMessage",
4082 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4084 Dart_GetNativeArgument(args, 0));
4085 // Message latch should only be signaled once as the bad
4086 // view metric should be dropped by the engine.
4087 ASSERT_FALSE(message_latch.IsSignaledForTest());
4088 ASSERT_EQ("Changed: [0]", message);
4089 message_latch.Signal();
4090 }));
4091
4092 auto engine = builder.LaunchEngine();
4093 ASSERT_TRUE(engine.is_valid());
4094
4095 ready_latch.Wait();
4096
4097 // Send a window metric for a nonexistent view, which should be dropped by the
4098 // engine.
4099 FlutterWindowMetricsEvent bad_event = {};
4100 bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
4101 bad_event.width = 200;
4102 bad_event.height = 300;
4103 bad_event.pixel_ratio = 1.5;
4104 bad_event.view_id = 100;
4105
4106 FlutterEngineResult result =
4108 ASSERT_EQ(result, kSuccess);
4109
4110 // Send a window metric for a valid view. The engine notifies the Dart app.
4111 FlutterWindowMetricsEvent event = {};
4112 event.struct_size = sizeof(FlutterWindowMetricsEvent);
4113 event.width = 200;
4114 event.height = 300;
4115 event.pixel_ratio = 1.5;
4116 event.view_id = 0;
4117
4118 result = FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
4119 ASSERT_EQ(result, kSuccess);
4120
4121 message_latch.Wait();
4122}
4123
4124TEST_F(EmbedderTest, RegisterChannelListener) {
4125 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4126
4129 bool listening = false;
4130 context.AddNativeCallback(
4131 "SignalNativeTest",
4132 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { latch.Signal(); }));
4133 context.SetChannelUpdateCallback([&](const FlutterChannelUpdate* update) {
4134 EXPECT_STREQ(update->channel, "test/listen");
4135 EXPECT_TRUE(update->listening);
4136 listening = true;
4137 latch2.Signal();
4138 });
4139
4140 EmbedderConfigBuilder builder(context);
4141 builder.SetSurface(DlISize(1, 1));
4142 builder.SetDartEntrypoint("channel_listener_response");
4143
4144 auto engine = builder.LaunchEngine();
4145 ASSERT_TRUE(engine.is_valid());
4146
4147 latch.Wait();
4148 // Drain tasks posted to platform thread task runner.
4150 latch2.Wait();
4151
4152 ASSERT_TRUE(listening);
4153}
4154
4155TEST_F(EmbedderTest, PlatformThreadIsolatesWithCustomPlatformTaskRunner) {
4156 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4157 static fml::AutoResetWaitableEvent latch;
4158
4159 static std::thread::id ffi_call_thread_id;
4160 static void (*ffi_signal_native_test)() = []() -> void {
4161 ffi_call_thread_id = std::this_thread::get_id();
4162 latch.Signal();
4163 };
4164
4165 Dart_FfiNativeResolver ffi_resolver = [](const char* name,
4166 uintptr_t args_n) -> void* {
4167 if (std::string_view(name) == "FFISignalNativeTest") {
4168 return reinterpret_cast<void*>(ffi_signal_native_test);
4169 }
4170 return nullptr;
4171 };
4172
4173 // The test's Dart code will call this native function which overrides the
4174 // FFI resolver. After that, the Dart code will invoke the FFI function
4175 // using runOnPlatformThread.
4176 context.AddNativeCallback(
4177 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4178 Dart_SetFfiNativeResolver(Dart_RootLibrary(), ffi_resolver);
4179 }));
4180
4181 auto platform_task_runner = CreateNewThread("test_platform_thread");
4182
4184
4185 EmbedderTestTaskRunner test_task_runner(
4186 platform_task_runner, [&](FlutterTask task) {
4187 if (!engine.is_valid()) {
4188 return;
4189 }
4190 FlutterEngineRunTask(engine.get(), &task);
4191 });
4192
4193 std::thread::id platform_thread_id;
4194 platform_task_runner->PostTask([&]() {
4195 platform_thread_id = std::this_thread::get_id();
4196
4197 EmbedderConfigBuilder builder(context);
4198 const auto task_runner_description =
4199 test_task_runner.GetFlutterTaskRunnerDescription();
4200 builder.SetSurface(DlISize(1, 1));
4201 builder.SetPlatformTaskRunner(&task_runner_description);
4202 builder.SetDartEntrypoint("invokePlatformThreadIsolate");
4203 builder.AddCommandLineArgument("--enable-platform-isolates");
4204 engine = builder.LaunchEngine();
4205 ASSERT_TRUE(engine.is_valid());
4206 });
4207
4208 latch.Wait();
4209
4210 fml::AutoResetWaitableEvent kill_latch;
4211 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
4212 engine.reset();
4213
4214 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
4215 }));
4216 kill_latch.Wait();
4217
4218 // Check that the FFI call was executed on the platform thread.
4219 ASSERT_EQ(platform_thread_id, ffi_call_thread_id);
4220}
4221
4222} // namespace testing
4223} // namespace flutter
4224
4225// NOLINTEND(clang-analyzer-core.StackAddressEscape)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
GLenum type
static size_t GetVMLaunchCount()
The number of times the VM has been launched in the process. This call is inherently racy because the...
Definition dart_vm.cc:263
static bool IsRunningPrecompiledCode()
Checks if VM instances in the process can run precompiled code. This call can be made at any time and...
Definition dart_vm.cc:176
std::shared_ptr< fml::ConcurrentMessageLoop > GetConcurrentMessageLoop()
The concurrent message loop hosts threads that are used by the engine to perform tasks long running b...
Definition dart_vm.cc:543
const TaskRunners & GetTaskRunners() const
static std::unique_ptr< EmbedderThreadHost > CreateEmbedderOrEngineManagedThreadHost(const FlutterCustomTaskRunners *custom_task_runners, const flutter::ThreadConfigSetter &config_setter=fml::Thread::SetCurrentThreadName)
DartVM * GetDartVM()
Get a pointer to the Dart VM used by this running shell instance.
Definition shell.cc:940
const Settings & GetSettings() const override
Definition shell.cc:907
fml::WeakPtr< PlatformView > GetPlatformView()
Platform views may only be accessed on the platform task runner.
Definition shell.cc:930
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
void SetPlatformTaskRunner(const FlutterTaskRunnerDescription *runner)
void SetExecutableName(std::string executable_name)
void SetRenderTargetType(EmbedderTestBackingStoreProducer::RenderTargetType type, FlutterSoftwarePixelFormat software_pixfmt=kFlutterSoftwarePixelFormatNative32)
void SetViewFocusChangeRequestCallback(const std::function< void(const FlutterViewFocusChangeRequest *)> &callback)
void SetRenderTaskRunner(const FlutterTaskRunnerDescription *runner)
void SetUITaskRunner(const FlutterTaskRunnerDescription *runner)
void SetCompositor(bool avoid_backing_store_cache=false, bool use_present_layers_callback=false)
void SetPlatformMessageCallback(const std::function< void(const FlutterPlatformMessage *)> &callback)
A task runner that we expect the embedder to provide but whose implementation is a real FML task runn...
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
void RunExpiredTasksNow()
virtual void PostTask(const fml::closure &task) override
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
@ kDisplay
Suitable for threads which generate data for the display.
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition thread.cc:164
static constexpr TimeDelta FromNanoseconds(int64_t nanos)
Definition time_delta.h:40
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition time_delta.h:46
static TimePoint Now()
Definition time_point.cc:49
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition time_point.h:43
FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterTask *task)
Inform the engine to run the specified task. This task has been given to the embedder via the Flutter...
Definition embedder.cc:3313
FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(FlutterEngine) engine, intptr_t baton, uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos)
Notify the engine that a vsync event occurred. A baton passed to the platform via the vsync callback ...
Definition embedder.cc:3229
FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterLocale **locales, size_t locales_count)
Notify a running engine instance that the locale has been updated. The preferred locale must be the f...
Definition embedder.cc:3365
FlutterEngineResult FlutterEngineSendViewFocusEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterViewFocusEvent *event)
Notifies the engine that platform view focus state has changed.
Definition embedder.cc:2603
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Schedule a new frame to redraw the content.
Definition embedder.cc:3654
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2660
uint64_t FlutterEngineGetCurrentTime()
Get the current time in nanoseconds from the clock used by the flutter engine. This is the system mon...
Definition embedder.cc:3309
FlutterEngineResult FlutterEngineSetNextFrameCallback(FLUTTER_API_SYMBOL(FlutterEngine) engine, VoidCallback callback, void *user_data)
Schedule a callback to be called after the next frame is drawn. This must be called from the platform...
Definition embedder.cc:3666
FLUTTER_EXPORT FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Stops running the Flutter engine instance. After this call, the embedder is also guaranteed that no m...
Definition embedder.cc:2636
FlutterEngineResult FlutterEnginePostCallbackOnAllNativeThreads(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterNativeThreadCallback callback, void *user_data)
Schedule a callback to be run on all engine managed threads. The engine will attempt to service this ...
Definition embedder.cc:3574
FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterAddViewInfo *info)
Adds a view.
Definition embedder.cc:2500
FlutterEngineResult FlutterPlatformMessageCreateResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterDataCallback data_callback, void *user_data, FlutterPlatformMessageResponseHandle **response_out)
Creates a platform message response handle that allows the embedder to set a native callback for a re...
Definition embedder.cc:3028
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data)
Collects the AOT data.
Definition embedder.cc:1736
FlutterEngineResult FlutterEngineSendPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *flutter_message)
Definition embedder.cc:2977
bool FlutterEngineRunsAOTCompiledDartCode(void)
Returns if the Flutter engine instance will run AOT compiled Dart code. This call has no threading re...
Definition embedder.cc:3424
FlutterEngineResult FlutterEngineReloadSystemFonts(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Reloads the system fonts in engine.
Definition embedder.cc:3256
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2769
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition embedder.cc:2461
FlutterEngineResult FlutterEngineNotifyLowMemoryWarning(FLUTTER_API_SYMBOL(FlutterEngine) raw_engine)
Posts a low memory notification to a running engine instance. The engine will do its best to release ...
Definition embedder.cc:3552
FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *user_data)
Sends a key event to the engine. The framework will decide whether to handle this event in a synchron...
Definition embedder.cc:2921
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessageResponseHandle *handle, const uint8_t *data, size_t data_length)
Send a response from the native side to a platform message from the Dart Flutter application.
Definition embedder.cc:3078
FLUTTER_EXPORT FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterRemoveViewInfo *info)
Removes a view.
Definition embedder.cc:2561
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterPlatformMessageResponseHandle *response)
Collects the handle created using FlutterPlatformMessageCreateResponseHandle.
Definition embedder.cc:3063
FlutterEngineResult FlutterEngineCreateAOTData(const FlutterEngineAOTDataSource *source, FlutterEngineAOTData *data_out)
Creates the necessary data structures to launch a Flutter Dart application in AOT mode....
Definition embedder.cc:1682
FlutterKeyEventDeviceType
Definition embedder.h:1352
@ kFlutterKeyEventDeviceTypeKeyboard
Definition embedder.h:1353
@ kFlutterKeyEventDeviceTypeDirectionalPad
Definition embedder.h:1354
@ kFlutterKeyEventDeviceTypeHdmi
Definition embedder.h:1357
@ kFlutterKeyEventDeviceTypeJoystick
Definition embedder.h:1356
@ kFlutterKeyEventDeviceTypeGamepad
Definition embedder.h:1355
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition embedder.h:2104
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2102
FlutterEngineAOTDataSourceType
AOT data source type.
Definition embedder.h:2427
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2428
@ kAdd
Definition embedder.h:1262
FlutterSoftwarePixelFormat
Definition embedder.h:450
@ kFlutterSoftwarePixelFormatRGBA4444
Definition embedder.h:473
@ kFlutterSoftwarePixelFormatRGBA8888
Definition embedder.h:483
@ kFlutterSoftwarePixelFormatBGRA8888
Definition embedder.h:502
@ kFlutterSoftwarePixelFormatGray8
Definition embedder.h:454
@ kFlutterSoftwarePixelFormatRGB565
Definition embedder.h:463
FlutterEngineResult
Definition embedder.h:72
@ kInvalidArguments
Definition embedder.h:75
FlutterNativeThreadType
Definition embedder.h:2404
void(* VoidCallback)(void *)
Definition embedder.h:409
FlutterKeyEventType
Definition embedder.h:1346
@ kFlutterKeyEventTypeDown
Definition embedder.h:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1349
@ kFlutterBackingStoreTypeSoftware2
Definition embedder.h:2060
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:2053
FlutterSize FlutterSizeMake(double width, double height)
FlutterRect FlutterRectMakeLTRB(double l, double t, double r, double b)
FlutterPoint FlutterPointMake(double x, double y)
flutter::EmbedderEngine * ToEmbedderEngine(const FlutterEngine &engine)
#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher)
FlutterEngine engine
Definition main.cc:84
VkSurfaceKHR surface
Definition main.cc:65
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
G_BEGIN_DECLS GBytes * message
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_UNREACHABLE()
Definition logging.h:128
TEST_F(DisplayListTest, Defaults)
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
sk_sp< SkSurface > CreateRenderSurface(const FlutterLayer &layer, GrDirectContext *context)
FlutterKeyEventType UnserializeKeyEventType(uint64_t kind)
bool SurfacePixelDataMatchesBytes(SkSurface *surface, const std::vector< uint8_t > &bytes)
const char * GetSourcePath()
testing::EmbedderTest EmbedderTest
void ExpectKeyEventEq(const FlutterKeyEvent &subject, const FlutterKeyEvent &baseline)
constexpr const char * kDefaultAOTAppELFFileName
Definition elf_loader.h:16
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image)
TEST(NativeAssetsManagerTest, NoAvailableAssets)
static void expectSoftwareRenderingOutputMatches(EmbedderTest &test, std::string entrypoint, FlutterSoftwarePixelFormat pixfmt, const std::vector< uint8_t > &bytes)
FlutterKeyEventDeviceType UnserializeKeyEventDeviceType(uint64_t source)
constexpr int64_t kFlutterImplicitViewId
Definition constants.h:35
impeller::ISize32 DlISize
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network policy
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
bool operator==(const ViewportMetrics &a, const ViewportMetrics &b)
int64_t FlutterViewId
TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders)
std::string JoinPaths(std::initializer_list< std::string > components)
Definition paths.cc:14
internal::CopyableLambda< T > MakeCopyable(T lambda)
int32_t height
int32_t width
FlutterAddViewCallback add_view_callback
Definition embedder.h:1117
FlutterViewId view_id
The identifier for the view to add. This must be unique.
Definition embedder.h:1097
const FlutterWindowMetricsEvent * view_metrics
Definition embedder.h:1102
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2071
FlutterSoftwareBackingStore software
The description of the software backing store.
Definition embedder.h:2079
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStorePresentInfo).
Definition embedder.h:2121
An update to whether a message channel has a listener set or not.
Definition embedder.h:1829
bool listening
True if a listener has been set, false if one has been cleared.
Definition embedder.h:1835
const char * channel
The name of the channel.
Definition embedder.h:1833
FlutterEngineAOTDataSourceType type
Definition embedder.h:2434
const char * elf_path
Absolute path to an ELF library file.
Definition embedder.h:2437
uint64_t logical
Definition embedder.h:1406
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1384
uint64_t physical
Definition embedder.h:1398
FlutterKeyEventDeviceType device_type
The source device for the key event.
Definition embedder.h:1424
FlutterKeyEventType type
The event kind.
Definition embedder.h:1390
FlutterPoint offset
Definition embedder.h:2145
FlutterLayerContentType type
Definition embedder.h:2134
const FlutterBackingStore * backing_store
Definition embedder.h:2138
FlutterBackingStorePresentInfo * backing_store_present_info
Definition embedder.h:2151
const FlutterPlatformView * platform_view
Definition embedder.h:2141
size_t struct_size
This size of this struct. Must be sizeof(FlutterLayer).
Definition embedder.h:2131
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2147
const char * language_code
Definition embedder.h:2259
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2255
const char * script_code
Definition embedder.h:2269
const char * country_code
Definition embedder.h:2264
const char * variant_code
Definition embedder.h:2274
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1436
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1446
const char * channel
Definition embedder.h:1437
const uint8_t * message
Definition embedder.h:1438
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformView).
Definition embedder.h:2025
FlutterPlatformViewIdentifier identifier
Definition embedder.h:2029
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1308
FlutterViewId view_id
The identifier of the view that received the pointer event.
Definition embedder.h:1343
double y
The y coordinate of the pointer event in physical pixels.
Definition embedder.h:1317
double x
The x coordinate of the pointer event in physical pixels.
Definition embedder.h:1315
FlutterPointerPhase phase
Definition embedder.h:1309
const uint8_t * isolate_snapshot_data
Definition embedder.h:2531
const uint8_t * vm_snapshot_data
Definition embedder.h:2515
size_t isolate_snapshot_instructions_size
Definition embedder.h:2542
const uint8_t * vm_snapshot_instructions
Definition embedder.h:2523
size_t isolate_snapshot_data_size
Definition embedder.h:2534
size_t vm_snapshot_instructions_size
Definition embedder.h:2526
size_t vm_snapshot_data_size
Definition embedder.h:2518
const uint8_t * isolate_snapshot_instructions
Definition embedder.h:2539
A structure to represent a rectangle.
Definition embedder.h:641
A region represented by a collection of non-overlapping rectangles.
Definition embedder.h:2108
size_t struct_size
The size of this struct. Must be sizeof(FlutterRegion).
Definition embedder.h:2110
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:1165
FlutterViewId view_id
Definition embedder.h:1148
double height
Definition embedder.h:629
double width
Definition embedder.h:628
size_t height
The number of rows in the allocation.
Definition embedder.h:1934
FlutterViewFocusState state
The focus state of the view.
Definition embedder.h:1230
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1227
FlutterViewFocusDirection direction
The direction in which the focus transitioned across [FlutterView]s.
Definition embedder.h:1233
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1047
size_t height
Physical height of the window.
Definition embedder.h:1051
int64_t view_id
The view that this event is describing.
Definition embedder.h:1069
double pixel_ratio
Scale factor for the physical screen.
Definition embedder.h:1053
size_t width
Physical width of the window.
Definition embedder.h:1049
MappingCallback isolate_snapshot_instr
Definition settings.h:123
MappingCallback isolate_snapshot_data
Definition settings.h:121
MappingCallback vm_snapshot_data
Definition settings.h:116
MappingCallback vm_snapshot_instr
Definition settings.h:118
MappingCallback dart_library_sources_kernel
Definition settings.h:129
std::shared_ptr< fml::AutoResetWaitableEvent > latch
The ThreadConfig is the thread info include thread name, thread priority.
Definition thread.h:35
ThreadPriority priority
Definition thread.h:45
#define CREATE_NATIVE_ENTRY(native_entry)