Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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
3151TEST_F(EmbedderTest, WindowMetricsEventWithConstraints) {
3152 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3153 EmbedderConfigBuilder builder(context);
3154 builder.SetSurface(DlISize(1, 1));
3155 auto engine = builder.LaunchEngine();
3156
3157 ASSERT_TRUE(engine.is_valid());
3158
3159 // Test with has_constraints = true and valid constraints
3160 FlutterWindowMetricsEvent event = {};
3161 event.struct_size = sizeof(event);
3162 event.width = 800;
3163 event.height = 600;
3164 event.pixel_ratio = 1.0;
3165 event.has_constraints = true;
3166 event.min_width_constraint = 400;
3167 event.max_width_constraint = 1200;
3168 event.min_height_constraint = 300;
3169 event.max_height_constraint = 900;
3170
3171 // Should succeed with valid constraints
3172 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3173 kSuccess);
3174
3175 // Test with has_constraints = false
3176 // Constraints should be ignored and set to current width/height
3177 FlutterWindowMetricsEvent event_no_constraints = {};
3178 event_no_constraints.struct_size = sizeof(event_no_constraints);
3179 event_no_constraints.width = 1024;
3180 event_no_constraints.height = 768;
3181 event_no_constraints.pixel_ratio = 1.0;
3182 event_no_constraints.has_constraints = false;
3183 // These constraint values should be ignored
3184 event_no_constraints.min_width_constraint = 0;
3185 event_no_constraints.max_width_constraint = 0;
3186 event_no_constraints.min_height_constraint = 0;
3187 event_no_constraints.max_height_constraint = 0;
3188
3189 // Should succeed even with invalid constraint values because has_constraints
3190 // is false
3191 ASSERT_EQ(
3192 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_no_constraints),
3193 kSuccess);
3194
3195 // Test with has_constraints = true but width violates min constraint
3196 FlutterWindowMetricsEvent event_invalid_min = {};
3197 event_invalid_min.struct_size = sizeof(event_invalid_min);
3198 event_invalid_min.width = 300; // Less than min_width_constraint
3199 event_invalid_min.height = 600;
3200 event_invalid_min.pixel_ratio = 1.0;
3201 event_invalid_min.has_constraints = true;
3202 event_invalid_min.min_width_constraint = 400;
3203 event_invalid_min.max_width_constraint = 1200;
3204 event_invalid_min.min_height_constraint = 300;
3205 event_invalid_min.max_height_constraint = 900;
3206
3207 // Should fail because width < min_width_constraint
3208 ASSERT_EQ(
3209 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_min),
3211
3212 // Test with has_constraints = true but width violates max constraint
3213 FlutterWindowMetricsEvent event_invalid_max = {};
3214 event_invalid_max.struct_size = sizeof(event_invalid_max);
3215 event_invalid_max.width = 1300; // Greater than max_width_constraint
3216 event_invalid_max.height = 600;
3217 event_invalid_max.pixel_ratio = 1.0;
3218 event_invalid_max.has_constraints = true;
3219 event_invalid_max.min_width_constraint = 400;
3220 event_invalid_max.max_width_constraint = 1200;
3221 event_invalid_max.min_height_constraint = 300;
3222 event_invalid_max.max_height_constraint = 900;
3223
3224 // Should fail because width > max_width_constraint
3225 ASSERT_EQ(
3226 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_max),
3228
3229 // Test with has_constraints = true but height violates constraints
3230 FlutterWindowMetricsEvent event_invalid_height = {};
3231 event_invalid_height.struct_size = sizeof(event_invalid_height);
3232 event_invalid_height.width = 800;
3233 event_invalid_height.height = 200; // Less than min_height_constraint
3234 event_invalid_height.pixel_ratio = 1.0;
3235 event_invalid_height.has_constraints = true;
3236 event_invalid_height.min_width_constraint = 400;
3237 event_invalid_height.max_width_constraint = 1200;
3238 event_invalid_height.min_height_constraint = 300;
3239 event_invalid_height.max_height_constraint = 900;
3240
3241 // Should fail because height < min_height_constraint
3242 ASSERT_EQ(
3243 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_height),
3245}
3246
3248 EmbedderTest& test,
3249 std::string entrypoint,
3251 const std::vector<uint8_t>& bytes) {
3252 auto& context = test.GetEmbedderContext<EmbedderTestContextSoftware>();
3253
3254 EmbedderConfigBuilder builder(context);
3256 bool matches = false;
3257
3258 builder.SetSurface(DlISize(1, 1));
3259 builder.SetCompositor();
3260 builder.SetDartEntrypoint(std::move(entrypoint));
3261 builder.SetRenderTargetType(
3262 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer2,
3263 pixfmt);
3264
3265 auto engine = builder.LaunchEngine();
3266 ASSERT_TRUE(engine.is_valid());
3267
3268 context.GetCompositor().SetNextPresentCallback(
3269 [&context, &matches, &bytes, &latch](FlutterViewId view_id,
3270 const FlutterLayer** layers,
3271 size_t layers_count) {
3273 ASSERT_EQ(layers[0]->backing_store->type,
3275 sk_sp<SkSurface> surface =
3276 context.GetCompositor().GetSurface(layers[0]->backing_store);
3277 matches = SurfacePixelDataMatchesBytes(surface.get(), bytes);
3278 latch.Signal();
3279 });
3280
3281 // Send a window metrics events so frames may be scheduled.
3282 FlutterWindowMetricsEvent event = {};
3283 event.struct_size = sizeof(event);
3284 event.width = 1;
3285 event.height = 1;
3286 event.pixel_ratio = 1.0;
3287 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3288 kSuccess);
3289
3290 latch.Wait();
3291 ASSERT_TRUE(matches);
3292
3293 engine.reset();
3294}
3295
3296template <typename T>
3298 EmbedderTest& test,
3299 std::string entrypoint,
3301 T pixelvalue) {
3302 uint8_t* bytes = reinterpret_cast<uint8_t*>(&pixelvalue);
3304 test, std::move(entrypoint), pixfmt,
3305 std::vector<uint8_t>(bytes, bytes + sizeof(T)));
3306}
3307
3308#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher) \
3309 TEST_F(EmbedderTest, SoftwareRenderingPixelFormats##test_name) { \
3310 expectSoftwareRenderingOutputMatches(*this, #dart_entrypoint, pixfmt, \
3311 matcher); \
3312 }
3313
3314// Don't test the pixel formats that contain padding (so an X) and the
3315// kFlutterSoftwarePixelFormatNative32 pixel format here, so we don't add any
3316// flakiness.
3317SW_PIXFMT_TEST_F(RedRGBA565xF800,
3318 draw_solid_red,
3320 (uint16_t)0xF800);
3321SW_PIXFMT_TEST_F(RedRGBA4444xF00F,
3322 draw_solid_red,
3324 (uint16_t)0xF00F);
3325SW_PIXFMT_TEST_F(RedRGBA8888xFFx00x00xFF,
3326 draw_solid_red,
3328 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3329SW_PIXFMT_TEST_F(RedBGRA8888x00x00xFFxFF,
3330 draw_solid_red,
3332 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3334 draw_solid_red,
3336 (uint8_t)0x36);
3337
3338SW_PIXFMT_TEST_F(GreenRGB565x07E0,
3339 draw_solid_green,
3341 (uint16_t)0x07E0);
3342SW_PIXFMT_TEST_F(GreenRGBA4444x0F0F,
3343 draw_solid_green,
3345 (uint16_t)0x0F0F);
3346SW_PIXFMT_TEST_F(GreenRGBA8888x00xFFx00xFF,
3347 draw_solid_green,
3349 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3350SW_PIXFMT_TEST_F(GreenBGRA8888x00xFFx00xFF,
3351 draw_solid_green,
3353 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3354SW_PIXFMT_TEST_F(GreenGray8xB6,
3355 draw_solid_green,
3357 (uint8_t)0xB6);
3358
3359SW_PIXFMT_TEST_F(BlueRGB565x001F,
3360 draw_solid_blue,
3362 (uint16_t)0x001F);
3363SW_PIXFMT_TEST_F(BlueRGBA4444x00FF,
3364 draw_solid_blue,
3366 (uint16_t)0x00FF);
3367SW_PIXFMT_TEST_F(BlueRGBA8888x00x00xFFxFF,
3368 draw_solid_blue,
3370 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3371SW_PIXFMT_TEST_F(BlueBGRA8888xFFx00x00xFF,
3372 draw_solid_blue,
3374 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3375SW_PIXFMT_TEST_F(BlueGray8x12,
3376 draw_solid_blue,
3378 (uint8_t)0x12);
3379
3380//------------------------------------------------------------------------------
3381// Key Data
3382//------------------------------------------------------------------------------
3383
3384typedef struct {
3385 std::shared_ptr<fml::AutoResetWaitableEvent> latch;
3388
3389// Convert `kind` in integer form to its enum form.
3390//
3391// It performs a revesed mapping from `_serializeKeyEventType`
3392// in shell/platform/embedder/fixtures/main.dart.
3394 switch (kind) {
3395 case 1:
3397 case 2:
3399 case 3:
3401 default:
3404 }
3405}
3406
3407// Convert `source` in integer form to its enum form.
3408//
3409// It performs a revesed mapping from `_serializeKeyEventDeviceType`
3410// in shell/platform/embedder/fixtures/main.dart.
3412 switch (source) {
3413 case 1:
3415 case 2:
3417 case 3:
3419 case 4:
3421 case 5:
3423 default:
3426 }
3427}
3428
3429// Checks the equality of two `FlutterKeyEvent` by each of their members except
3430// for `character`. The `character` must be checked separately.
3432 const FlutterKeyEvent& baseline) {
3433 EXPECT_EQ(subject.timestamp, baseline.timestamp);
3434 EXPECT_EQ(subject.type, baseline.type);
3435 EXPECT_EQ(subject.physical, baseline.physical);
3436 EXPECT_EQ(subject.logical, baseline.logical);
3437 EXPECT_EQ(subject.synthesized, baseline.synthesized);
3438 EXPECT_EQ(subject.device_type, baseline.device_type);
3439}
3440
3441TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) {
3442 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3443 uint64_t echoed_char;
3444 FlutterKeyEvent echoed_event;
3445 echoed_event.struct_size = sizeof(FlutterKeyEvent);
3446
3447 auto native_echo_event = [&](Dart_NativeArguments args) {
3448 echoed_event.type =
3450 Dart_GetNativeArgument(args, 0)));
3451 echoed_event.timestamp =
3452 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3453 Dart_GetNativeArgument(args, 1)));
3455 Dart_GetNativeArgument(args, 2));
3457 Dart_GetNativeArgument(args, 3));
3459 Dart_GetNativeArgument(args, 4));
3460 echoed_event.synthesized =
3461 tonic::DartConverter<bool>::FromDart(Dart_GetNativeArgument(args, 5));
3462 echoed_event.device_type =
3464 Dart_GetNativeArgument(args, 6)));
3465
3466 message_latch->Signal();
3467 };
3468
3469 auto platform_task_runner = CreateNewThread("platform_thread");
3470
3473 platform_task_runner->PostTask([&]() {
3474 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3475 EmbedderConfigBuilder builder(context);
3476 builder.SetSurface(DlISize(1, 1));
3477 builder.SetDartEntrypoint("key_data_echo");
3479 [&](const FlutterPlatformMessage* message) {
3481 engine.get(), message->response_handle, nullptr, 0);
3482 });
3483 context.AddNativeCallback(
3484 "SignalNativeTest",
3486 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3487
3488 context.AddNativeCallback("EchoKeyEvent",
3489 CREATE_NATIVE_ENTRY(native_echo_event));
3490
3491 engine = builder.LaunchEngine();
3492 ASSERT_TRUE(engine.is_valid());
3493 });
3494
3495 ready.Wait();
3496
3497 // A normal down event
3498 const FlutterKeyEvent down_event_upper_a{
3499 .struct_size = sizeof(FlutterKeyEvent),
3500 .timestamp = 1,
3502 .physical = 0x00070004,
3503 .logical = 0x00000000061,
3504 .character = "A",
3505 .synthesized = false,
3507 };
3508 platform_task_runner->PostTask([&]() {
3509 FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, nullptr,
3510 nullptr);
3511 });
3512 message_latch->Wait();
3513
3514 ExpectKeyEventEq(echoed_event, down_event_upper_a);
3515 EXPECT_EQ(echoed_char, 0x41llu);
3516
3517 // A repeat event with multi-byte character
3518 const FlutterKeyEvent repeat_event_wide_char{
3519 .struct_size = sizeof(FlutterKeyEvent),
3520 .timestamp = 1000,
3522 .physical = 0x00070005,
3523 .logical = 0x00000000062,
3524 .character = "∆",
3525 .synthesized = false,
3527 };
3528 platform_task_runner->PostTask([&]() {
3529 FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, nullptr,
3530 nullptr);
3531 });
3532 message_latch->Wait();
3533
3534 ExpectKeyEventEq(echoed_event, repeat_event_wide_char);
3535 EXPECT_EQ(echoed_char, 0x2206llu);
3536
3537 // An up event with no character, synthesized
3538 const FlutterKeyEvent up_event{
3539 .struct_size = sizeof(FlutterKeyEvent),
3540 .timestamp = 1000000,
3542 .physical = 0x00070006,
3543 .logical = 0x00000000063,
3544 .character = nullptr,
3545 .synthesized = true,
3547 };
3548 platform_task_runner->PostTask([&]() {
3549 FlutterEngineSendKeyEvent(engine.get(), &up_event, nullptr, nullptr);
3550 });
3551 message_latch->Wait();
3552
3553 ExpectKeyEventEq(echoed_event, up_event);
3554 EXPECT_EQ(echoed_char, 0llu);
3555
3556 fml::AutoResetWaitableEvent shutdown_latch;
3557 platform_task_runner->PostTask([&]() {
3558 engine.reset();
3559 shutdown_latch.Signal();
3560 });
3561 shutdown_latch.Wait();
3562}
3563
3564TEST_F(EmbedderTest, KeyDataAreBuffered) {
3565 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3566 std::vector<FlutterKeyEvent> echoed_events;
3567
3568 auto native_echo_event = [&](Dart_NativeArguments args) {
3569 echoed_events.push_back(FlutterKeyEvent{
3570 .timestamp =
3571 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3572 Dart_GetNativeArgument(args, 1))),
3573 .type =
3575 Dart_GetNativeArgument(args, 0))),
3577 Dart_GetNativeArgument(args, 2)),
3579 Dart_GetNativeArgument(args, 3)),
3581 Dart_GetNativeArgument(args, 5)),
3582 .device_type = UnserializeKeyEventDeviceType(
3584 Dart_GetNativeArgument(args, 6))),
3585 });
3586
3587 message_latch->Signal();
3588 };
3589
3590 auto platform_task_runner = CreateNewThread("platform_thread");
3591
3594 platform_task_runner->PostTask([&]() {
3595 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3596 EmbedderConfigBuilder builder(context);
3597 builder.SetSurface(DlISize(1, 1));
3598 builder.SetDartEntrypoint("key_data_late_echo");
3600 [&](const FlutterPlatformMessage* message) {
3602 engine.get(), message->response_handle, nullptr, 0);
3603 });
3604 context.AddNativeCallback(
3605 "SignalNativeTest",
3607 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3608
3609 context.AddNativeCallback("EchoKeyEvent",
3610 CREATE_NATIVE_ENTRY(native_echo_event));
3611
3612 engine = builder.LaunchEngine();
3613 ASSERT_TRUE(engine.is_valid());
3614 });
3615 ready.Wait();
3616
3617 FlutterKeyEvent sample_event{
3618 .struct_size = sizeof(FlutterKeyEvent),
3620 .physical = 0x00070004,
3621 .logical = 0x00000000061,
3622 .character = "A",
3623 .synthesized = false,
3625 };
3626
3627 // Send an event.
3628 sample_event.timestamp = 1.0l;
3629 platform_task_runner->PostTask([&]() {
3630 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3631 message_latch->Signal();
3632 });
3633 message_latch->Wait();
3634
3635 // Should not receive echos because the callback is not set yet.
3636 EXPECT_EQ(echoed_events.size(), 0u);
3637
3638 // Send an empty message to 'test/starts_echo' to start echoing.
3639 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
3641 engine.get(), [](const uint8_t* data, size_t size, void* user_data) {},
3642 nullptr, &response_handle);
3643
3646 .channel = "test/starts_echo",
3647 .message = nullptr,
3648 .message_size = 0,
3649 .response_handle = response_handle,
3650 };
3651
3652 platform_task_runner->PostTask([&]() {
3653 FlutterEngineResult result =
3655 ASSERT_EQ(result, kSuccess);
3656
3658 });
3659
3660 // message_latch->Wait();
3661 message_latch->Wait();
3662 // All previous events should be received now.
3663 EXPECT_EQ(echoed_events.size(), 1u);
3664
3665 // Send a second event.
3666 sample_event.timestamp = 10.0l;
3667 platform_task_runner->PostTask([&]() {
3668 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3669 });
3670 message_latch->Wait();
3671
3672 // The event should be echoed, too.
3673 EXPECT_EQ(echoed_events.size(), 2u);
3674
3675 fml::AutoResetWaitableEvent shutdown_latch;
3676 platform_task_runner->PostTask([&]() {
3677 engine.reset();
3678 shutdown_latch.Signal();
3679 });
3680 shutdown_latch.Wait();
3681}
3682
3683TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) {
3685 fml::AutoResetWaitableEvent sync_latch;
3687
3688 // One of the threads that the key data callback will be posted to is the
3689 // platform thread. So we cannot wait for assertions to complete on the
3690 // platform thread. Create a new thread to manage the engine instance and wait
3691 // for assertions on the test thread.
3692 auto platform_task_runner = CreateNewThread("platform_thread");
3693
3694 platform_task_runner->PostTask([&]() {
3695 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3696 EmbedderConfigBuilder builder(context);
3697 builder.SetSurface(DlISize(1, 1));
3698 builder.SetDartEntrypoint("key_data_echo");
3699 context.AddNativeCallback(
3700 "SignalNativeTest",
3702 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3703 context.AddNativeCallback(
3704 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3705
3706 engine = builder.LaunchEngine();
3707 ASSERT_TRUE(engine.is_valid());
3708
3709 sync_latch.Signal();
3710 });
3711 sync_latch.Wait();
3712 ready.Wait();
3713
3714 // Dispatch a single event
3715 FlutterKeyEvent event{
3716 .struct_size = sizeof(FlutterKeyEvent),
3717 .timestamp = 1000,
3719 .physical = 0x00070005,
3720 .logical = 0x00000000062,
3721 .character = nullptr,
3723 };
3724
3725 KeyEventUserData user_data1{
3726 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3727 };
3728 // Entrypoint `key_data_echo` returns `event.synthesized` as `handled`.
3729 event.synthesized = true;
3730 platform_task_runner->PostTask([&]() {
3731 // Test when the response callback is empty.
3732 // It should not cause a crash.
3733 FlutterEngineSendKeyEvent(engine.get(), &event, nullptr, nullptr);
3734
3735 // Test when the response callback is non-empty.
3736 // It should be invoked (so that the latch can be unlocked.)
3738 engine.get(), &event,
3739 [](bool handled, void* untyped_user_data) {
3740 KeyEventUserData* user_data =
3741 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3742 EXPECT_EQ(handled, true);
3743 user_data->latch->Signal();
3744 },
3745 &user_data1);
3746 });
3747 user_data1.latch->Wait();
3748 fml::AutoResetWaitableEvent shutdown_latch;
3749 platform_task_runner->PostTask([&]() {
3750 engine.reset();
3751 shutdown_latch.Signal();
3752 });
3753 shutdown_latch.Wait();
3754}
3755
3756TEST_F(EmbedderTest, BackToBackKeyEventResponsesCorrectlyInvoked) {
3758 fml::AutoResetWaitableEvent sync_latch;
3760
3761 // One of the threads that the callback will be posted to is the platform
3762 // thread. So we cannot wait for assertions to complete on the platform
3763 // thread. Create a new thread to manage the engine instance and wait for
3764 // assertions on the test thread.
3765 auto platform_task_runner = CreateNewThread("platform_thread");
3766
3767 platform_task_runner->PostTask([&]() {
3768 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3769 EmbedderConfigBuilder builder(context);
3770 builder.SetSurface(DlISize(1, 1));
3771 builder.SetDartEntrypoint("key_data_echo");
3772 context.AddNativeCallback(
3773 "SignalNativeTest",
3775 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3776
3777 context.AddNativeCallback(
3778 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3779
3780 engine = builder.LaunchEngine();
3781 ASSERT_TRUE(engine.is_valid());
3782
3783 sync_latch.Signal();
3784 });
3785 sync_latch.Wait();
3786 ready.Wait();
3787
3788 // Dispatch a single event
3789 FlutterKeyEvent event{
3790 .struct_size = sizeof(FlutterKeyEvent),
3791 .timestamp = 1000,
3793 .physical = 0x00070005,
3794 .logical = 0x00000000062,
3795 .character = nullptr,
3796 .synthesized = false,
3798 };
3799
3800 // Dispatch two events back to back, using the same callback on different
3801 // user_data
3802 KeyEventUserData user_data2{
3803 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3804 .returned = false,
3805 };
3806 KeyEventUserData user_data3{
3807 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3808 .returned = false,
3809 };
3810 auto callback23 = [](bool handled, void* untyped_user_data) {
3812 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3813 EXPECT_EQ(handled, false);
3814 user_data->returned = true;
3815 user_data->latch->Signal();
3816 };
3817 platform_task_runner->PostTask([&]() {
3818 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2);
3819 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3);
3820 });
3821 user_data2.latch->Wait();
3822 user_data3.latch->Wait();
3823
3824 EXPECT_TRUE(user_data2.returned);
3825 EXPECT_TRUE(user_data3.returned);
3826
3827 fml::AutoResetWaitableEvent shutdown_latch;
3828 platform_task_runner->PostTask([&]() {
3829 engine.reset();
3830 shutdown_latch.Signal();
3831 });
3832 shutdown_latch.Wait();
3833}
3834
3835//------------------------------------------------------------------------------
3836// Vsync waiter
3837//------------------------------------------------------------------------------
3838
3839// This test schedules a frame for the future and asserts that vsync waiter
3840// posts the event at the right frame start time (which is in the future).
3841TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) {
3843 fml::AutoResetWaitableEvent present_latch;
3844 fml::AutoResetWaitableEvent vsync_latch;
3845
3846 // One of the threads that the callback (FlutterEngineOnVsync) will be posted
3847 // to is the platform thread. So we cannot wait for assertions to complete on
3848 // the platform thread. Create a new thread to manage the engine instance and
3849 // wait for assertions on the test thread.
3850 auto platform_task_runner = CreateNewThread("platform_thread");
3851
3852 platform_task_runner->PostTask([&]() {
3853 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3854 context.SetVsyncCallback([&](intptr_t baton) {
3855 platform_task_runner->PostTask([baton = baton, &engine, &vsync_latch]() {
3856 FlutterEngineOnVsync(engine.get(), baton, NanosFromEpoch(16),
3857 NanosFromEpoch(32));
3858 vsync_latch.Signal();
3859 });
3860 });
3861 context.AddNativeCallback(
3862 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3863 present_latch.Signal();
3864 }));
3865
3866 EmbedderConfigBuilder builder(context);
3867 builder.SetSurface(DlISize(1, 1));
3868 builder.SetupVsyncCallback();
3869 builder.SetDartEntrypoint("empty_scene");
3870 engine = builder.LaunchEngine();
3871 ASSERT_TRUE(engine.is_valid());
3872
3873 // Send a window metrics events so frames may be scheduled.
3874 FlutterWindowMetricsEvent event = {};
3875 event.struct_size = sizeof(event);
3876 event.width = 800;
3877 event.height = 600;
3878 event.pixel_ratio = 1.0;
3879
3880 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3881 kSuccess);
3882 });
3883
3884 vsync_latch.Wait();
3885 present_latch.Wait();
3886
3887 fml::AutoResetWaitableEvent shutdown_latch;
3888 platform_task_runner->PostTask([&]() {
3889 engine.reset();
3890 shutdown_latch.Signal();
3891 });
3892 shutdown_latch.Wait();
3893}
3894
3895TEST_F(EmbedderTest, CanScheduleFrame) {
3896 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3897 EmbedderConfigBuilder builder(context);
3898 builder.SetSurface(DlISize(1, 1));
3899 builder.SetDartEntrypoint("can_schedule_frame");
3901 context.AddNativeCallback(
3902 "SignalNativeTest",
3904 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
3905
3906 fml::AutoResetWaitableEvent check_latch;
3907 context.AddNativeCallback(
3908 "SignalNativeCount",
3910 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
3911
3912 auto engine = builder.LaunchEngine();
3913 ASSERT_TRUE(engine.is_valid());
3914
3915 // Wait for the application to attach the listener.
3916 latch.Wait();
3917
3918 ASSERT_EQ(FlutterEngineScheduleFrame(engine.get()), kSuccess);
3919
3920 check_latch.Wait();
3921}
3922
3923TEST_F(EmbedderTest, CanSetNextFrameCallback) {
3924 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3925 EmbedderConfigBuilder builder(context);
3926 builder.SetSurface(DlISize(1, 1));
3927 builder.SetDartEntrypoint("draw_solid_red");
3928
3929 auto engine = builder.LaunchEngine();
3930 ASSERT_TRUE(engine.is_valid());
3931
3932 // Register the callback that is executed once the next frame is drawn.
3933 fml::AutoResetWaitableEvent callback_latch;
3934 VoidCallback callback = [](void* user_data) {
3935 fml::AutoResetWaitableEvent* callback_latch =
3937
3938 callback_latch->Signal();
3939 };
3940
3942 &callback_latch);
3943 ASSERT_EQ(result, kSuccess);
3944
3945 // Send a window metrics events so frames may be scheduled.
3946 FlutterWindowMetricsEvent event = {};
3947 event.struct_size = sizeof(event);
3948 event.width = 800;
3949 event.height = 600;
3950 event.pixel_ratio = 1.0;
3951 event.physical_view_inset_top = 0.0;
3952 event.physical_view_inset_right = 0.0;
3953 event.physical_view_inset_bottom = 0.0;
3954 event.physical_view_inset_left = 0.0;
3955 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3956 kSuccess);
3957
3958 callback_latch.Wait();
3959}
3960
3961#if defined(FML_OS_MACOSX)
3962
3963static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
3964 pthread_t tid = pthread_self();
3965 struct sched_param param;
3966 int policy = SCHED_OTHER;
3967 switch (config.priority) {
3969 param.sched_priority = 10;
3970 break;
3971 default:
3972 param.sched_priority = 1;
3973 }
3974 pthread_setschedparam(tid, policy, &param);
3975}
3976
3977TEST_F(EmbedderTest, EmbedderThreadHostUseCustomThreadConfig) {
3978 auto thread_host =
3980 nullptr, MockThreadConfigSetter);
3981
3983 int ui_policy;
3984 struct sched_param ui_param;
3985
3986 thread_host->GetTaskRunners().GetUITaskRunner()->PostTask([&] {
3987 pthread_t current_thread = pthread_self();
3988 pthread_getschedparam(current_thread, &ui_policy, &ui_param);
3989 ASSERT_EQ(ui_param.sched_priority, 10);
3990 ui_latch.Signal();
3991 });
3992
3994 int io_policy;
3995 struct sched_param io_param;
3996 thread_host->GetTaskRunners().GetIOTaskRunner()->PostTask([&] {
3997 pthread_t current_thread = pthread_self();
3998 pthread_getschedparam(current_thread, &io_policy, &io_param);
3999 ASSERT_EQ(io_param.sched_priority, 1);
4000 io_latch.Signal();
4001 });
4002
4003 ui_latch.Wait();
4004 io_latch.Wait();
4005}
4006#endif
4007
4008/// Send a pointer event to Dart and wait until the Dart code signals
4009/// it received the event.
4010TEST_F(EmbedderTest, CanSendPointer) {
4011 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4012 EmbedderConfigBuilder builder(context);
4013 builder.SetSurface(DlISize(1, 1));
4014 builder.SetDartEntrypoint("pointer_data_packet");
4015
4016 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
4017 context.AddNativeCallback(
4018 "SignalNativeTest",
4020 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4021 context.AddNativeCallback(
4022 "SignalNativeCount",
4023 CREATE_NATIVE_ENTRY([&count_latch](Dart_NativeArguments args) {
4025 Dart_GetNativeArgument(args, 0));
4026 ASSERT_EQ(count, 1);
4027 count_latch.Signal();
4028 }));
4029 context.AddNativeCallback(
4030 "SignalNativeMessage",
4031 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4033 Dart_GetNativeArgument(args, 0));
4034 ASSERT_EQ("PointerData(viewId: 0, x: 123.0, y: 456.0)", message);
4035 message_latch.Signal();
4036 }));
4037
4038 auto engine = builder.LaunchEngine();
4039 ASSERT_TRUE(engine.is_valid());
4040
4041 ready_latch.Wait();
4042
4043 FlutterPointerEvent pointer_event = {};
4044 pointer_event.struct_size = sizeof(FlutterPointerEvent);
4045 pointer_event.phase = FlutterPointerPhase::kAdd;
4046 pointer_event.x = 123;
4047 pointer_event.y = 456;
4048 pointer_event.timestamp = static_cast<size_t>(1234567890);
4049 pointer_event.view_id = 0;
4050
4051 FlutterEngineResult result =
4052 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
4053 ASSERT_EQ(result, kSuccess);
4054
4055 count_latch.Wait();
4056 message_latch.Wait();
4057}
4058
4059/// Send a pointer event to Dart and wait until the Dart code echos with the
4060/// view ID.
4061TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
4062 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4063 EmbedderConfigBuilder builder(context);
4064 builder.SetSurface(DlISize(1, 1));
4065 builder.SetDartEntrypoint("pointer_data_packet_view_id");
4066
4067 fml::AutoResetWaitableEvent ready_latch, add_view_latch, message_latch;
4068 context.AddNativeCallback(
4069 "SignalNativeTest",
4071 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4072 context.AddNativeCallback(
4073 "SignalNativeMessage",
4074 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4076 Dart_GetNativeArgument(args, 0));
4077 ASSERT_EQ("ViewID: 2", message);
4078 message_latch.Signal();
4079 }));
4080
4081 auto engine = builder.LaunchEngine();
4082 ASSERT_TRUE(engine.is_valid());
4083 ready_latch.Wait();
4084
4085 // Add view 2
4086 FlutterWindowMetricsEvent metrics = {};
4087 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
4088 metrics.width = 800;
4089 metrics.height = 600;
4090 metrics.pixel_ratio = 1.0;
4091 metrics.view_id = 2;
4092
4093 FlutterAddViewInfo info = {};
4094 info.struct_size = sizeof(FlutterAddViewInfo);
4095 info.view_id = 2;
4096 info.view_metrics = &metrics;
4097 info.add_view_callback = [](const FlutterAddViewResult* result) {
4098 EXPECT_TRUE(result->added);
4099 fml::AutoResetWaitableEvent* add_view_latch =
4100 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
4101 add_view_latch->Signal();
4102 };
4103 info.user_data = &add_view_latch;
4104 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
4105 add_view_latch.Wait();
4106
4107 // Send a pointer event for view 2
4108 FlutterPointerEvent pointer_event = {};
4109 pointer_event.struct_size = sizeof(FlutterPointerEvent);
4110 pointer_event.phase = FlutterPointerPhase::kAdd;
4111 pointer_event.x = 123;
4112 pointer_event.y = 456;
4113 pointer_event.timestamp = static_cast<size_t>(1234567890);
4114 pointer_event.view_id = 2;
4115
4116 FlutterEngineResult result =
4117 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
4118 ASSERT_EQ(result, kSuccess);
4119
4120 message_latch.Wait();
4121}
4122
4123TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
4124 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4125 EmbedderConfigBuilder builder(context);
4126 builder.SetSurface(DlISize(1, 1));
4127 builder.SetDartEntrypoint("window_metrics_event_view_id");
4128
4129 fml::AutoResetWaitableEvent ready_latch, message_latch;
4130 context.AddNativeCallback(
4131 "SignalNativeTest",
4133 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4134 context.AddNativeCallback(
4135 "SignalNativeMessage",
4136 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4138 Dart_GetNativeArgument(args, 0));
4139 ASSERT_EQ("Changed: [0]", message);
4140 message_latch.Signal();
4141 }));
4142
4143 auto engine = builder.LaunchEngine();
4144 ASSERT_TRUE(engine.is_valid());
4145
4146 ready_latch.Wait();
4147
4148 FlutterWindowMetricsEvent event = {};
4149 // Simulate an event that comes from an old version of embedder.h that doesn't
4150 // have the view_id field.
4152 event.width = 200;
4153 event.height = 300;
4154 event.pixel_ratio = 1.5;
4155 // Skip assigning event.view_id here to test the default behavior.
4156
4157 FlutterEngineResult result =
4159 ASSERT_EQ(result, kSuccess);
4160
4161 message_latch.Wait();
4162}
4163
4164TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
4165 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4166 EmbedderConfigBuilder builder(context);
4167 builder.SetSurface(DlISize(1, 1));
4168 builder.SetDartEntrypoint("window_metrics_event_view_id");
4169
4170 fml::AutoResetWaitableEvent ready_latch, message_latch;
4171 context.AddNativeCallback(
4172 "SignalNativeTest",
4174 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4175
4176 context.AddNativeCallback(
4177 "SignalNativeMessage",
4178 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4180 Dart_GetNativeArgument(args, 0));
4181 // Message latch should only be signaled once as the bad
4182 // view metric should be dropped by the engine.
4183 ASSERT_FALSE(message_latch.IsSignaledForTest());
4184 ASSERT_EQ("Changed: [0]", message);
4185 message_latch.Signal();
4186 }));
4187
4188 auto engine = builder.LaunchEngine();
4189 ASSERT_TRUE(engine.is_valid());
4190
4191 ready_latch.Wait();
4192
4193 // Send a window metric for a nonexistent view, which should be dropped by the
4194 // engine.
4195 FlutterWindowMetricsEvent bad_event = {};
4196 bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
4197 bad_event.width = 200;
4198 bad_event.height = 300;
4199 bad_event.pixel_ratio = 1.5;
4200 bad_event.view_id = 100;
4201
4202 FlutterEngineResult result =
4204 ASSERT_EQ(result, kSuccess);
4205
4206 // Send a window metric for a valid view. The engine notifies the Dart app.
4207 FlutterWindowMetricsEvent event = {};
4208 event.struct_size = sizeof(FlutterWindowMetricsEvent);
4209 event.width = 200;
4210 event.height = 300;
4211 event.pixel_ratio = 1.5;
4212 event.view_id = 0;
4213
4214 result = FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
4215 ASSERT_EQ(result, kSuccess);
4216
4217 message_latch.Wait();
4218}
4219
4220TEST_F(EmbedderTest, RegisterChannelListener) {
4221 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4222
4225 bool listening = false;
4226 context.AddNativeCallback(
4227 "SignalNativeTest",
4228 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { latch.Signal(); }));
4229 context.SetChannelUpdateCallback([&](const FlutterChannelUpdate* update) {
4230 EXPECT_STREQ(update->channel, "test/listen");
4231 EXPECT_TRUE(update->listening);
4232 listening = true;
4233 latch2.Signal();
4234 });
4235
4236 EmbedderConfigBuilder builder(context);
4237 builder.SetSurface(DlISize(1, 1));
4238 builder.SetDartEntrypoint("channel_listener_response");
4239
4240 auto engine = builder.LaunchEngine();
4241 ASSERT_TRUE(engine.is_valid());
4242
4243 latch.Wait();
4244 // Drain tasks posted to platform thread task runner.
4246 latch2.Wait();
4247
4248 ASSERT_TRUE(listening);
4249}
4250
4251TEST_F(EmbedderTest, PlatformThreadIsolatesWithCustomPlatformTaskRunner) {
4252 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4253 static fml::AutoResetWaitableEvent latch;
4254
4255 static std::thread::id ffi_call_thread_id;
4256 static void (*ffi_signal_native_test)() = []() -> void {
4257 ffi_call_thread_id = std::this_thread::get_id();
4258 latch.Signal();
4259 };
4260
4261 Dart_FfiNativeResolver ffi_resolver = [](const char* name,
4262 uintptr_t args_n) -> void* {
4263 if (std::string_view(name) == "FFISignalNativeTest") {
4264 return reinterpret_cast<void*>(ffi_signal_native_test);
4265 }
4266 return nullptr;
4267 };
4268
4269 // The test's Dart code will call this native function which overrides the
4270 // FFI resolver. After that, the Dart code will invoke the FFI function
4271 // using runOnPlatformThread.
4272 context.AddNativeCallback(
4273 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4274 Dart_SetFfiNativeResolver(Dart_RootLibrary(), ffi_resolver);
4275 }));
4276
4277 auto platform_task_runner = CreateNewThread("test_platform_thread");
4278
4280
4281 EmbedderTestTaskRunner test_task_runner(
4282 platform_task_runner, [&](FlutterTask task) {
4283 if (!engine.is_valid()) {
4284 return;
4285 }
4286 FlutterEngineRunTask(engine.get(), &task);
4287 });
4288
4289 std::thread::id platform_thread_id;
4290 platform_task_runner->PostTask([&]() {
4291 platform_thread_id = std::this_thread::get_id();
4292
4293 EmbedderConfigBuilder builder(context);
4294 const auto task_runner_description =
4295 test_task_runner.GetFlutterTaskRunnerDescription();
4296 builder.SetSurface(DlISize(1, 1));
4297 builder.SetPlatformTaskRunner(&task_runner_description);
4298 builder.SetDartEntrypoint("invokePlatformThreadIsolate");
4299 builder.AddCommandLineArgument("--enable-platform-isolates");
4300 engine = builder.LaunchEngine();
4301 ASSERT_TRUE(engine.is_valid());
4302 });
4303
4304 latch.Wait();
4305
4306 fml::AutoResetWaitableEvent kill_latch;
4307 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
4308 engine.reset();
4309
4310 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
4311 }));
4312 kill_latch.Wait();
4313
4314 // Check that the FFI call was executed on the platform thread.
4315 ASSERT_EQ(platform_thread_id, ffi_call_thread_id);
4316}
4317
4318} // namespace testing
4319} // namespace flutter
4320
4321// 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:945
const Settings & GetSettings() const override
Definition shell.cc:912
fml::WeakPtr< PlatformView > GetPlatformView()
Platform views may only be accessed on the platform task runner.
Definition shell.cc:935
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:3333
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:3249
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:3385
FlutterEngineResult FlutterEngineSendViewFocusEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterViewFocusEvent *event)
Notifies the engine that platform view focus state has changed.
Definition embedder.cc:2623
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Schedule a new frame to redraw the content.
Definition embedder.cc:3674
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2680
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:3329
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:3686
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:2656
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:3594
FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterAddViewInfo *info)
Adds a view.
Definition embedder.cc:2520
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:3048
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data)
Collects the AOT data.
Definition embedder.cc:1756
FlutterEngineResult FlutterEngineSendPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *flutter_message)
Definition embedder.cc:2997
bool FlutterEngineRunsAOTCompiledDartCode(void)
Returns if the Flutter engine instance will run AOT compiled Dart code. This call has no threading re...
Definition embedder.cc:3444
FlutterEngineResult FlutterEngineReloadSystemFonts(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Reloads the system fonts in engine.
Definition embedder.cc:3276
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2789
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition embedder.cc:2481
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:3572
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:2941
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:3098
FLUTTER_EXPORT FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterRemoveViewInfo *info)
Removes a view.
Definition embedder.cc:2581
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterPlatformMessageResponseHandle *response)
Collects the handle created using FlutterPlatformMessageCreateResponseHandle.
Definition embedder.cc:3083
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:1702
FlutterKeyEventDeviceType
Definition embedder.h:1382
@ kFlutterKeyEventDeviceTypeKeyboard
Definition embedder.h:1383
@ kFlutterKeyEventDeviceTypeDirectionalPad
Definition embedder.h:1384
@ kFlutterKeyEventDeviceTypeHdmi
Definition embedder.h:1387
@ kFlutterKeyEventDeviceTypeJoystick
Definition embedder.h:1386
@ kFlutterKeyEventDeviceTypeGamepad
Definition embedder.h:1385
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition embedder.h:2134
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2132
FlutterEngineAOTDataSourceType
AOT data source type.
Definition embedder.h:2457
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2458
@ kAdd
Definition embedder.h:1292
FlutterSoftwarePixelFormat
Definition embedder.h:457
@ kFlutterSoftwarePixelFormatRGBA4444
Definition embedder.h:480
@ kFlutterSoftwarePixelFormatRGBA8888
Definition embedder.h:490
@ kFlutterSoftwarePixelFormatBGRA8888
Definition embedder.h:509
@ kFlutterSoftwarePixelFormatGray8
Definition embedder.h:461
@ kFlutterSoftwarePixelFormatRGB565
Definition embedder.h:470
FlutterEngineResult
Definition embedder.h:72
@ kInvalidArguments
Definition embedder.h:75
FlutterNativeThreadType
Definition embedder.h:2434
void(* VoidCallback)(void *)
Definition embedder.h:416
FlutterKeyEventType
Definition embedder.h:1376
@ kFlutterKeyEventTypeDown
Definition embedder.h:1378
@ kFlutterKeyEventTypeUp
Definition embedder.h:1377
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1379
@ kFlutterBackingStoreTypeSoftware2
Definition embedder.h:2090
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:2083
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 char * message
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
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:1147
FlutterViewId view_id
The identifier for the view to add. This must be unique.
Definition embedder.h:1127
const FlutterWindowMetricsEvent * view_metrics
Definition embedder.h:1132
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2101
FlutterSoftwareBackingStore software
The description of the software backing store.
Definition embedder.h:2109
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStorePresentInfo).
Definition embedder.h:2151
An update to whether a message channel has a listener set or not.
Definition embedder.h:1859
bool listening
True if a listener has been set, false if one has been cleared.
Definition embedder.h:1865
const char * channel
The name of the channel.
Definition embedder.h:1863
FlutterEngineAOTDataSourceType type
Definition embedder.h:2464
const char * elf_path
Absolute path to an ELF library file.
Definition embedder.h:2467
uint64_t logical
Definition embedder.h:1436
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1414
uint64_t physical
Definition embedder.h:1428
FlutterKeyEventDeviceType device_type
The source device for the key event.
Definition embedder.h:1454
FlutterKeyEventType type
The event kind.
Definition embedder.h:1420
FlutterPoint offset
Definition embedder.h:2175
FlutterLayerContentType type
Definition embedder.h:2164
const FlutterBackingStore * backing_store
Definition embedder.h:2168
FlutterBackingStorePresentInfo * backing_store_present_info
Definition embedder.h:2181
const FlutterPlatformView * platform_view
Definition embedder.h:2171
size_t struct_size
This size of this struct. Must be sizeof(FlutterLayer).
Definition embedder.h:2161
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2177
const char * language_code
Definition embedder.h:2289
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2285
const char * script_code
Definition embedder.h:2299
const char * country_code
Definition embedder.h:2294
const char * variant_code
Definition embedder.h:2304
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1466
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1476
const char * channel
Definition embedder.h:1467
const uint8_t * message
Definition embedder.h:1468
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformView).
Definition embedder.h:2055
FlutterPlatformViewIdentifier identifier
Definition embedder.h:2059
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1338
FlutterViewId view_id
The identifier of the view that received the pointer event.
Definition embedder.h:1373
double y
The y coordinate of the pointer event in physical pixels.
Definition embedder.h:1347
double x
The x coordinate of the pointer event in physical pixels.
Definition embedder.h:1345
FlutterPointerPhase phase
Definition embedder.h:1339
const uint8_t * isolate_snapshot_data
Definition embedder.h:2561
const uint8_t * vm_snapshot_data
Definition embedder.h:2545
size_t isolate_snapshot_instructions_size
Definition embedder.h:2572
const uint8_t * vm_snapshot_instructions
Definition embedder.h:2553
size_t isolate_snapshot_data_size
Definition embedder.h:2564
size_t vm_snapshot_instructions_size
Definition embedder.h:2556
size_t vm_snapshot_data_size
Definition embedder.h:2548
const uint8_t * isolate_snapshot_instructions
Definition embedder.h:2569
A structure to represent a rectangle.
Definition embedder.h:648
A region represented by a collection of non-overlapping rectangles.
Definition embedder.h:2138
size_t struct_size
The size of this struct. Must be sizeof(FlutterRegion).
Definition embedder.h:2140
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:1195
FlutterViewId view_id
Definition embedder.h:1178
double height
Definition embedder.h:636
double width
Definition embedder.h:635
size_t height
The number of rows in the allocation.
Definition embedder.h:1964
FlutterViewFocusState state
The focus state of the view.
Definition embedder.h:1260
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1257
FlutterViewFocusDirection direction
The direction in which the focus transitioned across [FlutterView]s.
Definition embedder.h:1263
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1054
size_t height
Physical height of the window.
Definition embedder.h:1058
int64_t view_id
The view that this event is describing.
Definition embedder.h:1076
double pixel_ratio
Scale factor for the physical screen.
Definition embedder.h:1060
size_t width
Physical width of the window.
Definition embedder.h:1056
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)