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_thread = std::make_unique<fml::Thread>("test_ui_thread");
208 auto ui_task_runner = ui_thread->GetTaskRunner();
209 auto platform_thread = std::make_unique<fml::Thread>("test_platform_thread");
210 auto platform_task_runner = platform_thread->GetTaskRunner();
211 static std::mutex engine_mutex;
213
214 EmbedderTestTaskRunner test_ui_task_runner(
215 ui_task_runner, [&](FlutterTask task) {
216 std::scoped_lock lock(engine_mutex);
217 if (!engine.is_valid()) {
218 return;
219 }
220 FlutterEngineRunTask(engine.get(), &task);
221 });
222 EmbedderTestTaskRunner test_platform_task_runner(
223 platform_task_runner, [&](FlutterTask task) {
224 std::scoped_lock lock(engine_mutex);
225 if (!engine.is_valid()) {
226 return;
227 }
228 FlutterEngineRunTask(engine.get(), &task);
229 });
230
231 fml::AutoResetWaitableEvent signal_latch_ui;
232 fml::AutoResetWaitableEvent signal_latch_platform;
233
234 context.AddNativeCallback(
235 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
236 // Assert that the UI isolate is running on platform thread.
237 ASSERT_TRUE(ui_task_runner->RunsTasksOnCurrentThread());
238 signal_latch_ui.Signal();
239 }));
240
241 platform_task_runner->PostTask([&]() {
242 EmbedderConfigBuilder builder(context);
243 const auto ui_task_runner_description =
244 test_ui_task_runner.GetFlutterTaskRunnerDescription();
245 const auto platform_task_runner_description =
246 test_platform_task_runner.GetFlutterTaskRunnerDescription();
247 builder.SetSurface(DlISize(1, 1));
248 builder.SetUITaskRunner(&ui_task_runner_description);
249 builder.SetPlatformTaskRunner(&platform_task_runner_description);
250 builder.SetDartEntrypoint("canSpecifyCustomUITaskRunner");
252 [&](const FlutterPlatformMessage* message) {
253 ASSERT_TRUE(platform_task_runner->RunsTasksOnCurrentThread());
254 signal_latch_platform.Signal();
255 });
256 {
257 std::scoped_lock lock(engine_mutex);
258 engine = builder.InitializeEngine();
259 }
261 ASSERT_TRUE(engine.is_valid());
262 });
263 signal_latch_ui.Wait();
264 signal_latch_platform.Wait();
265
267 platform_task_runner->PostTask([&] {
268 engine.reset();
269 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
270 });
271 kill_latch.Wait();
272
273 // Shut down the threads before exiting the test. There may still be
274 // pending tasks queued to the task runners, and they must not run
275 // after the engine goes out of scope.
276 ui_thread.reset();
277 platform_thread.reset();
278}
279
280TEST_F(EmbedderTest, IgnoresStaleTasks) {
281 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
282 auto ui_task_runner = CreateNewThread("test_ui_thread");
283 auto platform_task_runner = CreateNewThread("test_platform_thread");
284 static std::mutex engine_mutex;
286 FlutterEngine engine_ptr;
287
288 EmbedderTestTaskRunner test_ui_task_runner(
289 ui_task_runner, [&](FlutterTask task) {
290 // The check for engine.is_valid() is intentionally absent here.
291 // FlutterEngineRunTask must be able to detect and ignore stale tasks
292 // without crashing even if the engine pointer is not null.
293 // Because the engine is destroyed on platform thread,
294 // relying solely on engine.is_valid() in UI thread is not safe.
295 FlutterEngineRunTask(engine_ptr, &task);
296 });
297 EmbedderTestTaskRunner test_platform_task_runner(
298 platform_task_runner, [&](FlutterTask task) {
299 std::scoped_lock lock(engine_mutex);
300 if (!engine.is_valid()) {
301 return;
302 }
303 FlutterEngineRunTask(engine.get(), &task);
304 });
305
307
308 platform_task_runner->PostTask([&]() {
309 EmbedderConfigBuilder builder(context);
310 const auto ui_task_runner_description =
311 test_ui_task_runner.GetFlutterTaskRunnerDescription();
312 const auto platform_task_runner_description =
313 test_platform_task_runner.GetFlutterTaskRunnerDescription();
314 builder.SetUITaskRunner(&ui_task_runner_description);
315 builder.SetPlatformTaskRunner(&platform_task_runner_description);
316 {
317 std::scoped_lock lock(engine_mutex);
318 engine = builder.InitializeEngine();
319 }
320 init_latch.Signal();
321 });
322
323 init_latch.Wait();
324 engine_ptr = engine.get();
325
326 auto flutter_engine = reinterpret_cast<EmbedderEngine*>(engine.get());
327
328 // Schedule task on UI thread that will likely run after the engine has shut
329 // down.
332
334 platform_task_runner->PostTask([&] {
335 engine.reset();
336 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
337 });
338 kill_latch.Wait();
339
340 // Ensure that the schedule task indeed runs.
341 kill_latch.Reset();
342 ui_task_runner->PostDelayedTask([&]() { kill_latch.Signal(); },
344 kill_latch.Wait();
345}
346
347TEST_F(EmbedderTest, MergedPlatformUIThread) {
348 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
349 auto task_runner = CreateNewThread("test_thread");
351
352 EmbedderTestTaskRunner test_task_runner(task_runner, [&](FlutterTask task) {
353 if (!engine.is_valid()) {
354 return;
355 }
356 FlutterEngineRunTask(engine.get(), &task);
357 });
358
359 fml::AutoResetWaitableEvent signal_latch_ui;
360 fml::AutoResetWaitableEvent signal_latch_platform;
361
362 context.AddNativeCallback(
363 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
364 // Assert that the UI isolate is running on platform thread.
365 ASSERT_TRUE(task_runner->RunsTasksOnCurrentThread());
366 signal_latch_ui.Signal();
367 }));
368
369 task_runner->PostTask([&]() {
370 EmbedderConfigBuilder builder(context);
371 const auto task_runner_description =
372 test_task_runner.GetFlutterTaskRunnerDescription();
373 builder.SetSurface(DlISize(1, 1));
374 builder.SetUITaskRunner(&task_runner_description);
375 builder.SetPlatformTaskRunner(&task_runner_description);
376 builder.SetDartEntrypoint("mergedPlatformUIThread");
378 [&](const FlutterPlatformMessage* message) {
379 ASSERT_TRUE(task_runner->RunsTasksOnCurrentThread());
380 signal_latch_platform.Signal();
381 });
382 engine = builder.LaunchEngine();
383 ASSERT_TRUE(engine.is_valid());
384 });
385 signal_latch_ui.Wait();
386 signal_latch_platform.Wait();
387
389 task_runner->PostTask([&] {
390 engine.reset();
391 task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
392 });
393 kill_latch.Wait();
394}
395
396TEST_F(EmbedderTest, UITaskRunnerFlushesMicrotasks) {
397 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
398 auto ui_task_runner = CreateNewThread("test_ui_thread");
400
401 EmbedderTestTaskRunner test_task_runner(
402 // Assert that the UI isolate is running on platform thread.
403 ui_task_runner, [&](FlutterTask task) {
404 if (!engine.is_valid()) {
405 return;
406 }
407 FlutterEngineRunTask(engine.get(), &task);
408 });
409
410 fml::AutoResetWaitableEvent signal_latch;
411
412 context.AddNativeCallback(
413 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
414 ASSERT_TRUE(ui_task_runner->RunsTasksOnCurrentThread());
415 signal_latch.Signal();
416 }));
417
418 ui_task_runner->PostTask([&]() {
419 EmbedderConfigBuilder builder(context);
420 const auto task_runner_description =
421 test_task_runner.GetFlutterTaskRunnerDescription();
422 builder.SetSurface(DlISize(1, 1));
423 builder.SetUITaskRunner(&task_runner_description);
424 builder.SetDartEntrypoint("uiTaskRunnerFlushesMicrotasks");
425 engine = builder.LaunchEngine();
426 ASSERT_TRUE(engine.is_valid());
427 });
428 signal_latch.Wait();
429
431 ui_task_runner->PostTask([&] {
432 engine.reset();
433 ui_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
434 });
435 kill_latch.Wait();
436}
437
438TEST_F(EmbedderTest, CanSpecifyCustomPlatformTaskRunner) {
439 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
441
442 // Run the test on its own thread with a message loop so that it can safely
443 // pump its event loop while we wait for all the conditions to be checked.
444 auto platform_task_runner = CreateNewThread("test_platform_thread");
445 static std::mutex engine_mutex;
446 static bool signaled_once = false;
447 static std::atomic<bool> destruction_callback_called = false;
449
450 EmbedderTestTaskRunner test_task_runner(
451 platform_task_runner, [&](FlutterTask task) {
452 std::scoped_lock lock(engine_mutex);
453 if (!engine.is_valid()) {
454 return;
455 }
456 // There may be multiple tasks posted but we only need to check
457 // assertions once.
458 if (signaled_once) {
459 FlutterEngineRunTask(engine.get(), &task);
460 return;
461 }
462
463 signaled_once = true;
464 ASSERT_TRUE(engine.is_valid());
465 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
466 latch.Signal();
467 });
468 test_task_runner.SetDestructionCallback(
469 [](void* user_data) { destruction_callback_called = true; });
470
471 platform_task_runner->PostTask([&]() {
472 EmbedderConfigBuilder builder(context);
473 const auto task_runner_description =
474 test_task_runner.GetFlutterTaskRunnerDescription();
475 builder.SetSurface(DlISize(1, 1));
476 builder.SetPlatformTaskRunner(&task_runner_description);
477 builder.SetDartEntrypoint("invokePlatformTaskRunner");
478 std::scoped_lock lock(engine_mutex);
479 engine = builder.LaunchEngine();
480 ASSERT_TRUE(engine.is_valid());
481 });
482
483 // Signaled when all the assertions are checked.
484 latch.Wait();
485 ASSERT_TRUE(engine.is_valid());
486
487 // Since the engine was started on its own thread, it must be killed there as
488 // well.
490 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
491 std::scoped_lock lock(engine_mutex);
492 engine.reset();
493
494 // There may still be pending tasks on the platform thread that were queued
495 // by the test_task_runner. Signal the latch after these tasks have been
496 // consumed.
497 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
498 }));
499 kill_latch.Wait();
500
501 ASSERT_TRUE(signaled_once);
502 signaled_once = false;
503
504 ASSERT_TRUE(destruction_callback_called);
505 destruction_callback_called = false;
506}
507
508TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
509 auto point1 = fml::TimePoint::FromEpochDelta(
511 auto point2 = fml::TimePoint::Now();
512
513 ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1));
514}
515
516TEST_F(EmbedderTest, CanReloadSystemFonts) {
517 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
518 EmbedderConfigBuilder builder(context);
519 builder.SetSurface(DlISize(1, 1));
520 auto engine = builder.LaunchEngine();
521 ASSERT_TRUE(engine.is_valid());
522
523 auto result = FlutterEngineReloadSystemFonts(engine.get());
524 ASSERT_EQ(result, kSuccess);
525}
526
527TEST_F(EmbedderTest, IsolateServiceIdSent) {
528 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
530
531 fml::Thread thread;
533 std::string isolate_message;
534
535 thread.GetTaskRunner()->PostTask([&]() {
536 EmbedderConfigBuilder builder(context);
537 builder.SetSurface(DlISize(1, 1));
538 builder.SetDartEntrypoint("main");
540 [&](const FlutterPlatformMessage* message) {
541 if (strcmp(message->channel, "flutter/isolate") == 0) {
542 isolate_message = {reinterpret_cast<const char*>(message->message),
543 message->message_size};
544 latch.Signal();
545 }
546 });
547 engine = builder.LaunchEngine();
548 ASSERT_TRUE(engine.is_valid());
549 });
550
551 // Wait for the isolate ID message and check its format.
552 latch.Wait();
553 ASSERT_EQ(isolate_message.find("isolates/"), 0ul);
554
555 // Since the engine was started on its own thread, it must be killed there as
556 // well.
558 thread.GetTaskRunner()->PostTask(
559 fml::MakeCopyable([&engine, &kill_latch]() mutable {
560 engine.reset();
561 kill_latch.Signal();
562 }));
563 kill_latch.Wait();
564}
565
566//------------------------------------------------------------------------------
567/// Creates a platform message response callbacks, does NOT send them, and
568/// immediately collects the same.
569///
570TEST_F(EmbedderTest, CanCreateAndCollectCallbacks) {
571 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
572 EmbedderConfigBuilder builder(context);
573 builder.SetSurface(DlISize(1, 1));
574 builder.SetDartEntrypoint("platform_messages_response");
575 context.AddNativeCallback(
576 "SignalNativeTest",
577 CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
578
579 auto engine = builder.LaunchEngine();
580 ASSERT_TRUE(engine.is_valid());
581
582 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
583 auto callback = [](const uint8_t* data, size_t size,
584 void* user_data) -> void {};
586 engine.get(), callback, nullptr, &response_handle);
587 ASSERT_EQ(result, kSuccess);
588 ASSERT_NE(response_handle, nullptr);
589
591 response_handle);
592 ASSERT_EQ(result, kSuccess);
593}
594
595//------------------------------------------------------------------------------
596/// Sends platform messages to Dart code than simply echoes the contents of the
597/// message back to the embedder. The embedder registers a native callback to
598/// intercept that message.
599///
600TEST_F(EmbedderTest, PlatformMessagesCanReceiveResponse) {
601 struct Captures {
603 std::thread::id thread_id;
604 };
605 Captures captures;
606
607 CreateNewThread()->PostTask([&]() {
608 captures.thread_id = std::this_thread::get_id();
609 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
610 EmbedderConfigBuilder builder(context);
611 builder.SetSurface(DlISize(1, 1));
612 builder.SetDartEntrypoint("platform_messages_response");
613
615 context.AddNativeCallback(
616 "SignalNativeTest",
618 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
619
620 auto engine = builder.LaunchEngine();
621 ASSERT_TRUE(engine.is_valid());
622
623 static std::string kMessageData = "Hello from embedder.";
624
625 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
626 auto callback = [](const uint8_t* data, size_t size,
627 void* user_data) -> void {
628 ASSERT_EQ(size, kMessageData.size());
629 ASSERT_EQ(strncmp(reinterpret_cast<const char*>(kMessageData.data()),
630 reinterpret_cast<const char*>(data), size),
631 0);
632 auto captures = reinterpret_cast<Captures*>(user_data);
633 ASSERT_EQ(captures->thread_id, std::this_thread::get_id());
634 captures->latch.Signal();
635 };
637 engine.get(), callback, &captures, &response_handle);
638 ASSERT_EQ(result, kSuccess);
639
642 message.channel = "test_channel";
643 message.message = reinterpret_cast<const uint8_t*>(kMessageData.data());
644 message.message_size = kMessageData.size();
645 message.response_handle = response_handle;
646
647 ready.Wait();
649 ASSERT_EQ(result, kSuccess);
650
652 response_handle);
653 ASSERT_EQ(result, kSuccess);
654 });
655
656 captures.latch.Wait();
657}
658
659//------------------------------------------------------------------------------
660/// Tests that a platform message can be sent with no response handle. Instead
661/// of the platform message integrity checked via a response handle, a native
662/// callback with the response is invoked to assert integrity.
663///
664TEST_F(EmbedderTest, PlatformMessagesCanBeSentWithoutResponseHandles) {
665 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
666 EmbedderConfigBuilder builder(context);
667 builder.SetSurface(DlISize(1, 1));
668 builder.SetDartEntrypoint("platform_messages_no_response");
669
670 const std::string message_data = "Hello but don't call me back.";
671
673 context.AddNativeCallback(
674 "SignalNativeTest",
676 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
677 context.AddNativeCallback(
678 "SignalNativeMessage",
680 ([&message, &message_data](Dart_NativeArguments args) {
681 auto received_message = tonic::DartConverter<std::string>::FromDart(
682 Dart_GetNativeArgument(args, 0));
683 ASSERT_EQ(received_message, message_data);
684 message.Signal();
685 })));
686
687 auto engine = builder.LaunchEngine();
688
689 ASSERT_TRUE(engine.is_valid());
690 ready.Wait();
691
692 FlutterPlatformMessage platform_message = {};
693 platform_message.struct_size = sizeof(FlutterPlatformMessage);
694 platform_message.channel = "test_channel";
695 platform_message.message =
696 reinterpret_cast<const uint8_t*>(message_data.data());
697 platform_message.message_size = message_data.size();
698 platform_message.response_handle = nullptr; // No response needed.
699
700 auto result =
701 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
702 ASSERT_EQ(result, kSuccess);
703 message.Wait();
704}
705
706//------------------------------------------------------------------------------
707/// Tests that a null platform message can be sent.
708///
709TEST_F(EmbedderTest, NullPlatformMessagesCanBeSent) {
710 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
711 EmbedderConfigBuilder builder(context);
712 builder.SetSurface(DlISize(1, 1));
713 builder.SetDartEntrypoint("null_platform_messages");
714
716 context.AddNativeCallback(
717 "SignalNativeTest",
719 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
720 context.AddNativeCallback(
721 "SignalNativeMessage",
722 CREATE_NATIVE_ENTRY(([&message](Dart_NativeArguments args) {
723 auto received_message = tonic::DartConverter<std::string>::FromDart(
724 Dart_GetNativeArgument(args, 0));
725 ASSERT_EQ("true", received_message);
726 message.Signal();
727 })));
728
729 auto engine = builder.LaunchEngine();
730
731 ASSERT_TRUE(engine.is_valid());
732 ready.Wait();
733
734 FlutterPlatformMessage platform_message = {};
735 platform_message.struct_size = sizeof(FlutterPlatformMessage);
736 platform_message.channel = "test_channel";
737 platform_message.message = nullptr;
738 platform_message.message_size = 0;
739 platform_message.response_handle = nullptr; // No response needed.
740
741 auto result =
742 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
743 ASSERT_EQ(result, kSuccess);
744 message.Wait();
745}
746
747//------------------------------------------------------------------------------
748/// Tests that a null platform message cannot be send if the message_size
749/// isn't equals to 0.
750///
751TEST_F(EmbedderTest, InvalidPlatformMessages) {
752 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
753 EmbedderConfigBuilder builder(context);
754 builder.SetSurface(DlISize(1, 1));
755 auto engine = builder.LaunchEngine();
756
757 ASSERT_TRUE(engine.is_valid());
758
759 FlutterPlatformMessage platform_message = {};
760 platform_message.struct_size = sizeof(FlutterPlatformMessage);
761 platform_message.channel = "test_channel";
762 platform_message.message = nullptr;
763 platform_message.message_size = 1;
764 platform_message.response_handle = nullptr; // No response needed.
765
766 auto result =
767 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
768 ASSERT_EQ(result, kInvalidArguments);
769}
770
771//------------------------------------------------------------------------------
772/// Tests that setting a custom log callback works as expected and defaults to
773/// using tag "flutter".
774TEST_F(EmbedderTest, CanSetCustomLogMessageCallback) {
775 fml::AutoResetWaitableEvent callback_latch;
776 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
777 EmbedderConfigBuilder builder(context);
778 builder.SetDartEntrypoint("custom_logger");
779 builder.SetSurface(DlISize(1, 1));
780 context.SetLogMessageCallback(
781 [&callback_latch](const char* tag, const char* message) {
782 EXPECT_EQ(std::string(tag), "flutter");
783 EXPECT_EQ(std::string(message), "hello world");
784 callback_latch.Signal();
785 });
786 auto engine = builder.LaunchEngine();
787 ASSERT_TRUE(engine.is_valid());
788 callback_latch.Wait();
789}
790
791//------------------------------------------------------------------------------
792/// Tests that setting a custom log tag works.
793TEST_F(EmbedderTest, CanSetCustomLogTag) {
794 fml::AutoResetWaitableEvent callback_latch;
795 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
796 EmbedderConfigBuilder builder(context);
797 builder.SetDartEntrypoint("custom_logger");
798 builder.SetSurface(DlISize(1, 1));
799 builder.SetLogTag("butterfly");
800 context.SetLogMessageCallback(
801 [&callback_latch](const char* tag, const char* message) {
802 EXPECT_EQ(std::string(tag), "butterfly");
803 EXPECT_EQ(std::string(message), "hello world");
804 callback_latch.Signal();
805 });
806 auto engine = builder.LaunchEngine();
807 ASSERT_TRUE(engine.is_valid());
808 callback_latch.Wait();
809}
810
811//------------------------------------------------------------------------------
812/// Asserts behavior of FlutterProjectArgs::shutdown_dart_vm_when_done (which is
813/// set to true by default in these unit-tests).
814///
815TEST_F(EmbedderTest, VMShutsDownWhenNoEnginesInProcess) {
816 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
817 EmbedderConfigBuilder builder(context);
818 builder.SetSurface(DlISize(1, 1));
819 const auto launch_count = DartVM::GetVMLaunchCount();
820
821 {
822 auto engine = builder.LaunchEngine();
823 ASSERT_EQ(launch_count + 1u, DartVM::GetVMLaunchCount());
824 }
825
826 {
827 auto engine = builder.LaunchEngine();
828 ASSERT_EQ(launch_count + 2u, DartVM::GetVMLaunchCount());
829 }
830}
831
832//------------------------------------------------------------------------------
833///
834TEST_F(EmbedderTest, DartEntrypointArgs) {
835 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
836 EmbedderConfigBuilder builder(context);
837 builder.SetSurface(DlISize(1, 1));
838 builder.AddDartEntrypointArgument("foo");
839 builder.AddDartEntrypointArgument("bar");
840 builder.SetDartEntrypoint("dart_entrypoint_args");
841 fml::AutoResetWaitableEvent callback_latch;
842 std::vector<std::string> callback_args;
843 auto nativeArgumentsCallback = [&callback_args,
844 &callback_latch](Dart_NativeArguments args) {
845 Dart_Handle exception = nullptr;
846 callback_args =
848 args, 0, exception);
849 callback_latch.Signal();
850 };
851 context.AddNativeCallback("NativeArgumentsCallback",
852 CREATE_NATIVE_ENTRY(nativeArgumentsCallback));
853 auto engine = builder.LaunchEngine();
854 callback_latch.Wait();
855 ASSERT_EQ(callback_args[0], "foo");
856 ASSERT_EQ(callback_args[1], "bar");
857}
858
859//------------------------------------------------------------------------------
860/// These snapshots may be materialized from symbols and the size field may not
861/// be relevant. Since this information is redundant, engine launch should not
862/// be gated on a non-zero buffer size.
863///
864TEST_F(EmbedderTest, VMAndIsolateSnapshotSizesAreRedundantInAOTMode) {
866 GTEST_SKIP();
867 return;
868 }
869 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
870 EmbedderConfigBuilder builder(context);
871 builder.SetSurface(DlISize(1, 1));
872
873 // The fixture sets this up correctly. Intentionally mess up the args.
878
879 auto engine = builder.LaunchEngine();
880 ASSERT_TRUE(engine.is_valid());
881}
882
883TEST_F(EmbedderTest, CanRenderImplicitView) {
884 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
885
886 EmbedderConfigBuilder builder(context);
887 builder.SetSurface(DlISize(800, 600));
888 builder.SetCompositor();
889 builder.SetDartEntrypoint("render_implicit_view");
890 builder.SetRenderTargetType(
891 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
892
894
895 context.GetCompositor().SetNextPresentCallback(
897 size_t layers_count) {
899 latch.Signal();
900 });
901
902 auto engine = builder.LaunchEngine();
903
904 FlutterWindowMetricsEvent event = {};
905 event.struct_size = sizeof(event);
906 event.width = 300;
907 event.height = 200;
908 event.pixel_ratio = 1.0;
909 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
910 kSuccess);
911 ASSERT_TRUE(engine.is_valid());
912 latch.Wait();
913}
914
915TEST_F(EmbedderTest, CanRenderImplicitViewUsingPresentLayersCallback) {
916 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
917
918 EmbedderConfigBuilder builder(context);
919 builder.SetSurface(DlISize(800, 600));
920 builder.SetCompositor(/* avoid_backing_store_cache = */ false,
921 /* use_present_layers_callback = */ true);
922 builder.SetDartEntrypoint("render_implicit_view");
923 builder.SetRenderTargetType(
924 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
925
927
928 context.GetCompositor().SetNextPresentCallback(
930 size_t layers_count) {
932 latch.Signal();
933 });
934
935 auto engine = builder.LaunchEngine();
936
937 FlutterWindowMetricsEvent event = {};
938 event.struct_size = sizeof(event);
939 event.width = 300;
940 event.height = 200;
941 event.pixel_ratio = 1.0;
942 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
943 kSuccess);
944 ASSERT_TRUE(engine.is_valid());
945 latch.Wait();
946}
947
948//------------------------------------------------------------------------------
949/// Test the layer structure and pixels rendered when using a custom software
950/// compositor.
951///
952// TODO(143940): Convert this test to use SkiaGold.
953#if FML_OS_MACOSX && FML_ARCH_CPU_ARM64
954TEST_F(EmbedderTest,
955 DISABLED_CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
956#else
958 CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
959#endif // FML_OS_MACOSX && FML_ARCH_CPU_ARM64
960
961 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
962
963 EmbedderConfigBuilder builder(context);
964 builder.SetSurface(DlISize(800, 600));
965 builder.SetCompositor();
966 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
967
968 builder.SetRenderTargetType(
969 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
970
971 fml::CountDownLatch latch(5);
972
973 auto scene_image = context.GetNextSceneImage();
974
975 context.GetCompositor().SetNextPresentCallback(
977 size_t layers_count) {
978 ASSERT_EQ(layers_count, 5u);
979
980 // Layer Root
981 {
982 FlutterBackingStore backing_store = *layers[0]->backing_store;
984 backing_store.did_update = true;
985 backing_store.software.height = 600;
986
987 FlutterRect paint_region_rects[] = {
988 FlutterRectMakeLTRB(0, 0, 800, 600),
989 };
990 FlutterRegion paint_region = {
991 .struct_size = sizeof(FlutterRegion),
992 .rects_count = 1,
993 .rects = paint_region_rects,
994 };
995 FlutterBackingStorePresentInfo present_info = {
997 .paint_region = &paint_region,
998 };
999
1000 FlutterLayer layer = {};
1001 layer.struct_size = sizeof(layer);
1003 layer.backing_store = &backing_store;
1004 layer.size = FlutterSizeMake(800.0, 600.0);
1005 layer.offset = FlutterPointMake(0.0, 0.0);
1006 layer.backing_store_present_info = &present_info;
1007
1008 ASSERT_EQ(*layers[0], layer);
1009 }
1010
1011 // Layer 1
1012 {
1015 platform_view.identifier = 1;
1016
1017 FlutterLayer layer = {};
1018 layer.struct_size = sizeof(layer);
1021 layer.size = FlutterSizeMake(50.0, 150.0);
1022 layer.offset = FlutterPointMake(20.0, 20.0);
1023
1024 ASSERT_EQ(*layers[1], layer);
1025 }
1026
1027 // Layer 2
1028 {
1029 FlutterBackingStore backing_store = *layers[2]->backing_store;
1030 backing_store.type = kFlutterBackingStoreTypeSoftware;
1031 backing_store.did_update = true;
1032 backing_store.software.height = 600;
1033
1034 FlutterRect paint_region_rects[] = {
1035 FlutterRectMakeLTRB(30, 30, 80, 180),
1036 };
1037 FlutterRegion paint_region = {
1038 .struct_size = sizeof(FlutterRegion),
1039 .rects_count = 1,
1040 .rects = paint_region_rects,
1041 };
1042 FlutterBackingStorePresentInfo present_info = {
1044 .paint_region = &paint_region,
1045 };
1046
1047 FlutterLayer layer = {};
1048 layer.struct_size = sizeof(layer);
1050 layer.backing_store = &backing_store;
1051 layer.size = FlutterSizeMake(800.0, 600.0);
1052 layer.offset = FlutterPointMake(0.0, 0.0);
1053 layer.backing_store_present_info = &present_info;
1054
1055 ASSERT_EQ(*layers[2], layer);
1056 }
1057
1058 // Layer 3
1059 {
1062 platform_view.identifier = 2;
1063
1064 FlutterLayer layer = {};
1065 layer.struct_size = sizeof(layer);
1068 layer.size = FlutterSizeMake(50.0, 150.0);
1069 layer.offset = FlutterPointMake(40.0, 40.0);
1070
1071 ASSERT_EQ(*layers[3], layer);
1072 }
1073
1074 // Layer 4
1075 {
1076 FlutterBackingStore backing_store = *layers[4]->backing_store;
1077 backing_store.type = kFlutterBackingStoreTypeSoftware;
1078 backing_store.did_update = true;
1079 backing_store.software.height = 600;
1080
1081 FlutterRect paint_region_rects[] = {
1082 FlutterRectMakeLTRB(50, 50, 100, 200),
1083 };
1084 FlutterRegion paint_region = {
1085 .struct_size = sizeof(FlutterRegion),
1086 .rects_count = 1,
1087 .rects = paint_region_rects,
1088 };
1089 FlutterBackingStorePresentInfo present_info = {
1091 .paint_region = &paint_region,
1092 };
1093
1094 FlutterLayer layer = {};
1095 layer.struct_size = sizeof(layer);
1097 layer.backing_store = &backing_store;
1098 layer.size = FlutterSizeMake(800.0, 600.0);
1099 layer.offset = FlutterPointMake(0.0, 0.0);
1100 layer.backing_store_present_info = &present_info;
1101
1102 ASSERT_EQ(*layers[4], layer);
1103 }
1104
1105 latch.CountDown();
1106 });
1107
1108 context.GetCompositor().SetPlatformViewRendererCallback(
1109 [&](const FlutterLayer& layer, GrDirectContext*
1110 /* don't use because software compositor */) -> sk_sp<SkImage> {
1112 layer, nullptr /* null because software compositor */);
1113 auto canvas = surface->getCanvas();
1114 FML_CHECK(canvas != nullptr);
1115
1116 switch (layer.platform_view->identifier) {
1117 case 1: {
1118 SkPaint paint;
1119 // See dart test for total order.
1120 paint.setColor(SK_ColorGREEN);
1121 paint.setAlpha(127);
1122 const auto& rect =
1123 SkRect::MakeWH(layer.size.width, layer.size.height);
1124 canvas->drawRect(rect, paint);
1125 latch.CountDown();
1126 } break;
1127 case 2: {
1128 SkPaint paint;
1129 // See dart test for total order.
1130 paint.setColor(SK_ColorMAGENTA);
1131 paint.setAlpha(127);
1132 const auto& rect =
1133 SkRect::MakeWH(layer.size.width, layer.size.height);
1134 canvas->drawRect(rect, paint);
1135 latch.CountDown();
1136 } break;
1137 default:
1138 // Asked to render an unknown platform view.
1139 FML_CHECK(false)
1140 << "Test was asked to composite an unknown platform view.";
1141 }
1142
1143 return surface->makeImageSnapshot();
1144 });
1145
1146 context.AddNativeCallback(
1147 "SignalNativeTest",
1149 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1150
1151 auto engine = builder.LaunchEngine();
1152
1153 // Send a window metrics events so frames may be scheduled.
1154 FlutterWindowMetricsEvent event = {};
1155 event.struct_size = sizeof(event);
1156 event.width = 800;
1157 event.height = 600;
1158 event.pixel_ratio = 1.0;
1159 event.physical_view_inset_top = 0.0;
1160 event.physical_view_inset_right = 0.0;
1161 event.physical_view_inset_bottom = 0.0;
1162 event.physical_view_inset_left = 0.0;
1163 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1164 kSuccess);
1165 ASSERT_TRUE(engine.is_valid());
1166
1167 latch.Wait();
1168
1169 ASSERT_TRUE(ImageMatchesFixture("compositor_software.png", scene_image));
1170
1171 // There should no present calls on the root surface.
1172 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1173}
1174
1175//------------------------------------------------------------------------------
1176/// Test the layer structure and pixels rendered when using a custom software
1177/// compositor, with a transparent overlay
1178///
1179TEST_F(EmbedderTest, NoLayerCreatedForTransparentOverlayOnTopOfPlatformLayer) {
1180 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1181
1182 EmbedderConfigBuilder builder(context);
1183 builder.SetSurface(DlISize(800, 600));
1184 builder.SetCompositor();
1185 builder.SetDartEntrypoint("can_composite_platform_views_transparent_overlay");
1186
1187 builder.SetRenderTargetType(
1188 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1189
1190 fml::CountDownLatch latch(4);
1191
1192 auto scene_image = context.GetNextSceneImage();
1193
1194 context.GetCompositor().SetNextPresentCallback(
1196 size_t layers_count) {
1197 ASSERT_EQ(layers_count, 2u);
1198
1199 // Layer Root
1200 {
1201 FlutterBackingStore backing_store = *layers[0]->backing_store;
1202 backing_store.type = kFlutterBackingStoreTypeSoftware;
1203 backing_store.did_update = true;
1204 backing_store.software.height = 600;
1205
1206 FlutterRect paint_region_rects[] = {
1207 FlutterRectMakeLTRB(0, 0, 800, 600),
1208 };
1209 FlutterRegion paint_region = {
1210 .struct_size = sizeof(FlutterRegion),
1211 .rects_count = 1,
1212 .rects = paint_region_rects,
1213 };
1214 FlutterBackingStorePresentInfo present_info = {
1216 .paint_region = &paint_region,
1217 };
1218
1219 FlutterLayer layer = {};
1220 layer.struct_size = sizeof(layer);
1222 layer.backing_store = &backing_store;
1223 layer.size = FlutterSizeMake(800.0, 600.0);
1224 layer.offset = FlutterPointMake(0.0, 0.0);
1225 layer.backing_store_present_info = &present_info;
1226
1227 ASSERT_EQ(*layers[0], layer);
1228 }
1229
1230 // Layer 1
1231 {
1234 platform_view.identifier = 1;
1235
1236 FlutterLayer layer = {};
1237 layer.struct_size = sizeof(layer);
1240 layer.size = FlutterSizeMake(50.0, 150.0);
1241 layer.offset = FlutterPointMake(20.0, 20.0);
1242
1243 ASSERT_EQ(*layers[1], layer);
1244 }
1245
1246 latch.CountDown();
1247 });
1248
1249 context.GetCompositor().SetPlatformViewRendererCallback(
1250 [&](const FlutterLayer& layer, GrDirectContext*
1251 /* don't use because software compositor */) -> sk_sp<SkImage> {
1253 layer, nullptr /* null because software compositor */);
1254 auto canvas = surface->getCanvas();
1255 FML_CHECK(canvas != nullptr);
1256
1257 switch (layer.platform_view->identifier) {
1258 case 1: {
1259 SkPaint paint;
1260 // See dart test for total order.
1261 paint.setColor(SK_ColorGREEN);
1262 paint.setAlpha(127);
1263 const auto& rect =
1264 SkRect::MakeWH(layer.size.width, layer.size.height);
1265 canvas->drawRect(rect, paint);
1266 latch.CountDown();
1267 } break;
1268 default:
1269 // Asked to render an unknown platform view.
1270 FML_CHECK(false)
1271 << "Test was asked to composite an unknown platform view.";
1272 }
1273
1274 return surface->makeImageSnapshot();
1275 });
1276
1277 context.AddNativeCallback(
1278 "SignalNativeTest",
1280 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1281
1282 auto engine = builder.LaunchEngine();
1283
1284 // Send a window metrics events so frames may be scheduled.
1285 FlutterWindowMetricsEvent event = {};
1286 event.struct_size = sizeof(event);
1287 event.width = 800;
1288 event.height = 600;
1289 event.pixel_ratio = 1.0;
1290 event.physical_view_inset_top = 0.0;
1291 event.physical_view_inset_right = 0.0;
1292 event.physical_view_inset_bottom = 0.0;
1293 event.physical_view_inset_left = 0.0;
1294 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1295 kSuccess);
1296 ASSERT_TRUE(engine.is_valid());
1297
1298 latch.Wait();
1299
1300 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1301 // platforms.
1302#if !defined(FML_OS_LINUX)
1303 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1304#endif // FML_OS_LINUX
1305 ASSERT_TRUE(ImageMatchesFixture(
1306 "compositor_platform_layer_with_no_overlay.png", scene_image));
1307
1308 // There should no present calls on the root surface.
1309 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1310}
1311
1312//------------------------------------------------------------------------------
1313/// Test the layer structure and pixels rendered when using a custom software
1314/// compositor, with a no overlay
1315///
1316TEST_F(EmbedderTest, NoLayerCreatedForNoOverlayOnTopOfPlatformLayer) {
1317 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1318
1319 EmbedderConfigBuilder builder(context);
1320 builder.SetSurface(DlISize(800, 600));
1321 builder.SetCompositor();
1322 builder.SetDartEntrypoint("can_composite_platform_views_no_overlay");
1323
1324 builder.SetRenderTargetType(
1325 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1326
1327 fml::CountDownLatch latch(4);
1328
1329 auto scene_image = context.GetNextSceneImage();
1330
1331 context.GetCompositor().SetNextPresentCallback(
1333 size_t layers_count) {
1334 ASSERT_EQ(layers_count, 2u);
1335
1336 // Layer Root
1337 {
1338 FlutterBackingStore backing_store = *layers[0]->backing_store;
1339 backing_store.type = kFlutterBackingStoreTypeSoftware;
1340 backing_store.did_update = true;
1341 backing_store.software.height = 600;
1342
1343 FlutterRect paint_region_rects[] = {
1344 FlutterRectMakeLTRB(0, 0, 800, 600),
1345 };
1346 FlutterRegion paint_region = {
1347 .struct_size = sizeof(FlutterRegion),
1348 .rects_count = 1,
1349 .rects = paint_region_rects,
1350 };
1351 FlutterBackingStorePresentInfo present_info = {
1353 .paint_region = &paint_region,
1354 };
1355
1356 FlutterLayer layer = {};
1357 layer.struct_size = sizeof(layer);
1359 layer.backing_store = &backing_store;
1360 layer.size = FlutterSizeMake(800.0, 600.0);
1361 layer.offset = FlutterPointMake(0.0, 0.0);
1362 layer.backing_store_present_info = &present_info;
1363
1364 ASSERT_EQ(*layers[0], layer);
1365 }
1366
1367 // Layer 1
1368 {
1371 platform_view.identifier = 1;
1372
1373 FlutterLayer layer = {};
1374 layer.struct_size = sizeof(layer);
1377 layer.size = FlutterSizeMake(50.0, 150.0);
1378 layer.offset = FlutterPointMake(20.0, 20.0);
1379
1380 ASSERT_EQ(*layers[1], layer);
1381 }
1382
1383 latch.CountDown();
1384 });
1385
1386 context.GetCompositor().SetPlatformViewRendererCallback(
1387 [&](const FlutterLayer& layer, GrDirectContext*
1388 /* don't use because software compositor */) -> sk_sp<SkImage> {
1390 layer, nullptr /* null because software compositor */);
1391 auto canvas = surface->getCanvas();
1392 FML_CHECK(canvas != nullptr);
1393
1394 switch (layer.platform_view->identifier) {
1395 case 1: {
1396 SkPaint paint;
1397 // See dart test for total order.
1398 paint.setColor(SK_ColorGREEN);
1399 paint.setAlpha(127);
1400 const auto& rect =
1401 SkRect::MakeWH(layer.size.width, layer.size.height);
1402 canvas->drawRect(rect, paint);
1403 latch.CountDown();
1404 } break;
1405 default:
1406 // Asked to render an unknown platform view.
1407 FML_CHECK(false)
1408 << "Test was asked to composite an unknown platform view.";
1409 }
1410
1411 return surface->makeImageSnapshot();
1412 });
1413
1414 context.AddNativeCallback(
1415 "SignalNativeTest",
1417 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1418
1419 auto engine = builder.LaunchEngine();
1420
1421 // Send a window metrics events so frames may be scheduled.
1422 FlutterWindowMetricsEvent event = {};
1423 event.struct_size = sizeof(event);
1424 event.width = 800;
1425 event.height = 600;
1426 event.pixel_ratio = 1.0;
1427 event.physical_view_inset_top = 0.0;
1428 event.physical_view_inset_right = 0.0;
1429 event.physical_view_inset_bottom = 0.0;
1430 event.physical_view_inset_left = 0.0;
1431 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1432 kSuccess);
1433 ASSERT_TRUE(engine.is_valid());
1434
1435 latch.Wait();
1436
1437 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1438 // platforms.
1439#if !defined(FML_OS_LINUX)
1440 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1441#endif // FML_OS_LINUX
1442 ASSERT_TRUE(ImageMatchesFixture(
1443 "compositor_platform_layer_with_no_overlay.png", scene_image));
1444
1445 // There should no present calls on the root surface.
1446 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1447}
1448
1449//------------------------------------------------------------------------------
1450/// Test that an engine can be initialized but not run.
1451///
1452TEST_F(EmbedderTest, CanCreateInitializedEngine) {
1453 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1454 EmbedderConfigBuilder builder(context);
1455 builder.SetSurface(DlISize(1, 1));
1456 auto engine = builder.InitializeEngine();
1457 ASSERT_TRUE(engine.is_valid());
1458 engine.reset();
1459}
1460
1461//------------------------------------------------------------------------------
1462/// Test that an initialized engine can be run exactly once.
1463///
1464TEST_F(EmbedderTest, CanRunInitializedEngine) {
1465 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1466 EmbedderConfigBuilder builder(context);
1467 builder.SetSurface(DlISize(1, 1));
1468 auto engine = builder.InitializeEngine();
1469 ASSERT_TRUE(engine.is_valid());
1470 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1471 // Cannot re-run an already running engine.
1473 engine.reset();
1474}
1475
1476//------------------------------------------------------------------------------
1477/// Test that an engine can be deinitialized.
1478///
1479TEST_F(EmbedderTest, CanDeinitializeAnEngine) {
1480 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1481 EmbedderConfigBuilder builder(context);
1482 builder.SetSurface(DlISize(1, 1));
1483 auto engine = builder.InitializeEngine();
1484 ASSERT_TRUE(engine.is_valid());
1485 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1486 // Cannot re-run an already running engine.
1488 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1489 // It is ok to deinitialize an engine multiple times.
1490 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1491
1492 // Sending events to a deinitialized engine fails.
1493 FlutterWindowMetricsEvent event = {};
1494 event.struct_size = sizeof(event);
1495 event.width = 800;
1496 event.height = 600;
1497 event.pixel_ratio = 1.0;
1498 event.physical_view_inset_top = 0.0;
1499 event.physical_view_inset_right = 0.0;
1500 event.physical_view_inset_bottom = 0.0;
1501 event.physical_view_inset_left = 0.0;
1502 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1504 engine.reset();
1505}
1506
1507//------------------------------------------------------------------------------
1508/// Test that a view can be added to a running engine.
1509///
1510TEST_F(EmbedderTest, CanAddView) {
1511 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1512 EmbedderConfigBuilder builder(context);
1513 builder.SetSurface(DlISize(1, 1));
1514 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1515
1516 fml::AutoResetWaitableEvent ready_latch, message_latch;
1517 context.AddNativeCallback(
1518 "SignalNativeTest",
1520 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1521
1522 std::string message;
1523 context.AddNativeCallback("SignalNativeMessage",
1524 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1525 message =
1527 Dart_GetNativeArgument(args, 0));
1528 message_latch.Signal();
1529 }));
1530
1531 auto engine = builder.LaunchEngine();
1532 ASSERT_TRUE(engine.is_valid());
1533
1534 ready_latch.Wait();
1535
1536 FlutterWindowMetricsEvent metrics = {};
1537 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1538 metrics.width = 800;
1539 metrics.height = 600;
1540 metrics.pixel_ratio = 1.0;
1541 metrics.view_id = 123;
1542
1543 FlutterAddViewInfo info = {};
1544 info.struct_size = sizeof(FlutterAddViewInfo);
1545 info.view_id = 123;
1546 info.view_metrics = &metrics;
1547 info.add_view_callback = [](const FlutterAddViewResult* result) {
1548 EXPECT_TRUE(result->added);
1549 };
1550 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1551 message_latch.Wait();
1552 ASSERT_EQ("View IDs: [0, 123]", message);
1553}
1554
1555//------------------------------------------------------------------------------
1556/// Test that adding a view schedules a frame.
1557///
1558TEST_F(EmbedderTest, AddViewSchedulesFrame) {
1559 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1560 EmbedderConfigBuilder builder(context);
1561 builder.SetSurface(DlISize(1, 1));
1562 builder.SetDartEntrypoint("add_view_schedules_frame");
1564 context.AddNativeCallback(
1565 "SignalNativeTest",
1567 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
1568
1569 fml::AutoResetWaitableEvent check_latch;
1570 context.AddNativeCallback(
1571 "SignalNativeCount",
1573 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
1574
1575 auto engine = builder.LaunchEngine();
1576 ASSERT_TRUE(engine.is_valid());
1577
1578 // Wait for the application to attach the listener.
1579 latch.Wait();
1580
1581 FlutterWindowMetricsEvent metrics = {};
1582 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1583 metrics.width = 800;
1584 metrics.height = 600;
1585 metrics.pixel_ratio = 1.0;
1586 metrics.view_id = 123;
1587
1588 FlutterAddViewInfo info = {};
1589 info.struct_size = sizeof(FlutterAddViewInfo);
1590 info.view_id = 123;
1591 info.view_metrics = &metrics;
1592 info.add_view_callback = [](const FlutterAddViewResult* result) {
1593 EXPECT_TRUE(result->added);
1594 };
1595 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1596
1597 check_latch.Wait();
1598}
1599
1600//------------------------------------------------------------------------------
1601/// Test that a view that was added can be removed.
1602///
1603TEST_F(EmbedderTest, CanRemoveView) {
1604 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1605 EmbedderConfigBuilder builder(context);
1606 builder.SetSurface(DlISize(1, 1));
1607 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1608
1609 fml::AutoResetWaitableEvent ready_latch, message_latch;
1610 context.AddNativeCallback(
1611 "SignalNativeTest",
1613 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1614
1615 std::string message;
1616 context.AddNativeCallback("SignalNativeMessage",
1617 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1618 message =
1620 Dart_GetNativeArgument(args, 0));
1621 message_latch.Signal();
1622 }));
1623
1624 auto engine = builder.LaunchEngine();
1625 ASSERT_TRUE(engine.is_valid());
1626
1627 ready_latch.Wait();
1628
1629 // Add view 123.
1630 FlutterWindowMetricsEvent metrics = {};
1631 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1632 metrics.width = 800;
1633 metrics.height = 600;
1634 metrics.pixel_ratio = 1.0;
1635 metrics.view_id = 123;
1636
1637 FlutterAddViewInfo add_info = {};
1638 add_info.struct_size = sizeof(FlutterAddViewInfo);
1639 add_info.view_id = 123;
1640 add_info.view_metrics = &metrics;
1641 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1642 ASSERT_TRUE(result->added);
1643 };
1644 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1645 message_latch.Wait();
1646 ASSERT_EQ(message, "View IDs: [0, 123]");
1647
1648 // Remove view 123.
1649 FlutterRemoveViewInfo remove_info = {};
1650 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1651 remove_info.view_id = 123;
1652 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1653 EXPECT_TRUE(result->removed);
1654 };
1655 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1656 message_latch.Wait();
1657 ASSERT_EQ(message, "View IDs: [0]");
1658}
1659
1660// Regression test for:
1661// https://github.com/flutter/flutter/issues/164564
1662TEST_F(EmbedderTest, RemoveViewCallbackIsInvokedAfterRasterThreadIsDone) {
1663 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1664 EmbedderConfigBuilder builder(context);
1665 std::mutex engine_mutex;
1667 auto render_thread = CreateNewThread("custom_render_thread");
1668 EmbedderTestTaskRunner render_task_runner(
1669 render_thread, [&](FlutterTask task) {
1670 std::scoped_lock engine_lock(engine_mutex);
1671 if (engine.is_valid()) {
1672 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
1673 }
1674 });
1675
1676 builder.SetSurface(DlISize(1, 1));
1677 builder.SetDartEntrypoint("remove_view_callback_too_early");
1678 builder.SetRenderTaskRunner(
1679 &render_task_runner.GetFlutterTaskRunnerDescription());
1680
1681 fml::AutoResetWaitableEvent ready_latch;
1682 context.AddNativeCallback(
1683 "SignalNativeTest",
1685 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1686
1687 {
1688 std::scoped_lock lock(engine_mutex);
1689 engine = builder.InitializeEngine();
1690 }
1691 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1692 ASSERT_TRUE(engine.is_valid());
1693
1694 ready_latch.Wait();
1695
1696 fml::AutoResetWaitableEvent add_view_latch;
1697 // Add view 123.
1698 FlutterWindowMetricsEvent metrics = {};
1699 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1700 metrics.width = 800;
1701 metrics.height = 600;
1702 metrics.pixel_ratio = 1.0;
1703 metrics.view_id = 123;
1704
1705 FlutterAddViewInfo add_info = {};
1706 add_info.struct_size = sizeof(FlutterAddViewInfo);
1707 add_info.view_id = 123;
1708 add_info.view_metrics = &metrics;
1709 add_info.user_data = &add_view_latch;
1710 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1711 ASSERT_TRUE(result->added);
1712 auto add_view_latch =
1713 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
1714 add_view_latch->Signal();
1715 };
1716 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1717 add_view_latch.Wait();
1718
1719 std::atomic_bool view_available = true;
1720
1721 // Simulate pending rasterization task scheduled before view removal request
1722 // that accesses view resources.
1723 fml::AutoResetWaitableEvent raster_thread_latch;
1724 render_thread->PostTask([&] {
1725 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1726 // View must be available.
1727 EXPECT_TRUE(view_available);
1728 raster_thread_latch.Signal();
1729 });
1730
1731 fml::AutoResetWaitableEvent remove_view_latch;
1732 FlutterRemoveViewInfo remove_view_info = {};
1733 remove_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
1734 remove_view_info.view_id = 123;
1735 remove_view_info.user_data = &remove_view_latch;
1736 remove_view_info.remove_view_callback =
1737 [](const FlutterRemoveViewResult* result) {
1738 ASSERT_TRUE(result->removed);
1739 auto remove_view_latch =
1740 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
1741 remove_view_latch->Signal();
1742 };
1743
1744 // Remove the view and wait until the callback is called.
1745 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_view_info), kSuccess);
1746 remove_view_latch.Wait();
1747
1748 // After FlutterEngineRemoveViewCallback is called it should be safe to
1749 // remove view - raster thread must not be accessing any view resources.
1750 view_available = false;
1751 raster_thread_latch.Wait();
1752
1754}
1755
1756//------------------------------------------------------------------------------
1757/// The implicit view is a special view that the engine and framework assume
1758/// can *always* be rendered to. Test that this view cannot be removed.
1759///
1760TEST_F(EmbedderTest, CannotRemoveImplicitView) {
1761 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1762 EmbedderConfigBuilder builder(context);
1763 builder.SetSurface(DlISize(1, 1));
1764
1765 auto engine = builder.LaunchEngine();
1766 ASSERT_TRUE(engine.is_valid());
1767
1768 FlutterRemoveViewInfo info = {};
1769 info.struct_size = sizeof(FlutterRemoveViewInfo);
1771 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1772 FAIL();
1773 };
1774 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &info), kInvalidArguments);
1775}
1776
1777//------------------------------------------------------------------------------
1778/// Test that a view cannot be added if its ID already exists.
1779///
1780TEST_F(EmbedderTest, CannotAddDuplicateViews) {
1781 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1782 EmbedderConfigBuilder builder(context);
1783 builder.SetSurface(DlISize(1, 1));
1784 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1785
1786 fml::AutoResetWaitableEvent ready_latch, message_latch;
1787 context.AddNativeCallback(
1788 "SignalNativeTest",
1790 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1791
1792 std::string message;
1793 context.AddNativeCallback("SignalNativeMessage",
1794 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1795 message =
1797 Dart_GetNativeArgument(args, 0));
1798 message_latch.Signal();
1799 }));
1800
1801 auto engine = builder.LaunchEngine();
1802 ASSERT_TRUE(engine.is_valid());
1803
1804 ready_latch.Wait();
1805
1806 // Add view 123.
1807 struct Captures {
1808 std::atomic<int> count = 0;
1809 fml::AutoResetWaitableEvent failure_latch;
1810 };
1811 Captures captures;
1812
1813 FlutterWindowMetricsEvent metrics = {};
1814 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1815 metrics.width = 800;
1816 metrics.height = 600;
1817 metrics.pixel_ratio = 1.0;
1818 metrics.view_id = 123;
1819
1820 FlutterAddViewInfo add_info = {};
1821 add_info.struct_size = sizeof(FlutterAddViewInfo);
1822 add_info.view_id = 123;
1823 add_info.view_metrics = &metrics;
1824 add_info.user_data = &captures;
1825 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1826 auto captures = reinterpret_cast<Captures*>(result->user_data);
1827
1828 int count = captures->count.fetch_add(1);
1829
1830 if (count == 0) {
1831 ASSERT_TRUE(result->added);
1832 } else {
1833 EXPECT_FALSE(result->added);
1834 captures->failure_latch.Signal();
1835 }
1836 };
1837 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1838 message_latch.Wait();
1839 ASSERT_EQ(message, "View IDs: [0, 123]");
1840 ASSERT_FALSE(captures.failure_latch.IsSignaledForTest());
1841
1842 // Add view 123 a second time.
1843 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1844 captures.failure_latch.Wait();
1845 ASSERT_EQ(captures.count, 2);
1846 ASSERT_FALSE(message_latch.IsSignaledForTest());
1847}
1848
1849//------------------------------------------------------------------------------
1850/// Test that a removed view's ID can be reused to add a new view.
1851///
1852TEST_F(EmbedderTest, CanReuseViewIds) {
1853 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1854 EmbedderConfigBuilder builder(context);
1855 builder.SetSurface(DlISize(1, 1));
1856 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1857
1858 fml::AutoResetWaitableEvent ready_latch, message_latch;
1859 context.AddNativeCallback(
1860 "SignalNativeTest",
1862 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1863
1864 std::string message;
1865 context.AddNativeCallback("SignalNativeMessage",
1866 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1867 message =
1869 Dart_GetNativeArgument(args, 0));
1870 message_latch.Signal();
1871 }));
1872
1873 auto engine = builder.LaunchEngine();
1874 ASSERT_TRUE(engine.is_valid());
1875
1876 ready_latch.Wait();
1877
1878 // Add view 123.
1879 FlutterWindowMetricsEvent metrics = {};
1880 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1881 metrics.width = 800;
1882 metrics.height = 600;
1883 metrics.pixel_ratio = 1.0;
1884 metrics.view_id = 123;
1885
1886 FlutterAddViewInfo add_info = {};
1887 add_info.struct_size = sizeof(FlutterAddViewInfo);
1888 add_info.view_id = 123;
1889 add_info.view_metrics = &metrics;
1890 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1891 ASSERT_TRUE(result->added);
1892 };
1893 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1894 message_latch.Wait();
1895 ASSERT_EQ(message, "View IDs: [0, 123]");
1896
1897 // Remove view 123.
1898 FlutterRemoveViewInfo remove_info = {};
1899 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1900 remove_info.view_id = 123;
1901 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1902 ASSERT_TRUE(result->removed);
1903 };
1904 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1905 message_latch.Wait();
1906 ASSERT_EQ(message, "View IDs: [0]");
1907
1908 // Re-add view 123.
1909 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1910 message_latch.Wait();
1911 ASSERT_EQ(message, "View IDs: [0, 123]");
1912}
1913
1914//------------------------------------------------------------------------------
1915/// Test that attempting to remove a view that does not exist fails as expected.
1916///
1917TEST_F(EmbedderTest, CannotRemoveUnknownView) {
1918 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1919 EmbedderConfigBuilder builder(context);
1920 builder.SetSurface(DlISize(1, 1));
1921
1922 auto engine = builder.LaunchEngine();
1923 ASSERT_TRUE(engine.is_valid());
1924
1926 FlutterRemoveViewInfo info = {};
1927 info.struct_size = sizeof(FlutterRemoveViewInfo);
1928 info.view_id = 123;
1929 info.user_data = &latch;
1930 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1931 EXPECT_FALSE(result->removed);
1932 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data)->Signal();
1933 };
1934 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &info), kSuccess);
1935 latch.Wait();
1936}
1937
1938//------------------------------------------------------------------------------
1939/// View operations - adding, removing, sending window metrics - must execute in
1940/// order even though they are asynchronous. This is necessary to ensure the
1941/// embedder's and engine's states remain synchronized.
1942///
1943TEST_F(EmbedderTest, ViewOperationsOrdered) {
1944 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
1945 EmbedderConfigBuilder builder(context);
1946 builder.SetSurface(DlISize(1, 1));
1947 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1948
1949 fml::AutoResetWaitableEvent ready_latch;
1950 context.AddNativeCallback(
1951 "SignalNativeTest",
1953 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1954
1955 std::atomic<int> message_count = 0;
1956 context.AddNativeCallback("SignalNativeMessage",
1957 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1958 message_count.fetch_add(1);
1959 }));
1960
1961 auto engine = builder.LaunchEngine();
1962 ASSERT_TRUE(engine.is_valid());
1963
1964 ready_latch.Wait();
1965
1966 // Enqueue multiple view operations at once:
1967 //
1968 // 1. Add view 123 - This must succeed.
1969 // 2. Add duplicate view 123 - This must fail asynchronously.
1970 // 3. Add second view 456 - This must succeed.
1971 // 4. Remove second view 456 - This must succeed.
1972 //
1973 // The engine must execute view operations asynchronously in serial order.
1974 // If step 2 succeeds instead of step 1, this indicates the engine did not
1975 // execute the view operations in the correct order. If step 4 fails,
1976 // this indicates the engine did not wait until the add second view completed.
1977 FlutterWindowMetricsEvent metrics123 = {};
1978 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
1979 metrics123.width = 800;
1980 metrics123.height = 600;
1981 metrics123.pixel_ratio = 1.0;
1982 metrics123.view_id = 123;
1983
1984 FlutterWindowMetricsEvent metrics456 = {};
1985 metrics456.struct_size = sizeof(FlutterWindowMetricsEvent);
1986 metrics456.width = 800;
1987 metrics456.height = 600;
1988 metrics456.pixel_ratio = 1.0;
1989 metrics456.view_id = 456;
1990
1991 struct Captures {
1992 fml::AutoResetWaitableEvent add_first_view;
1993 fml::AutoResetWaitableEvent add_duplicate_view;
1994 fml::AutoResetWaitableEvent add_second_view;
1995 fml::AutoResetWaitableEvent remove_second_view;
1996 };
1997 Captures captures;
1998
1999 // Add view 123.
2000 FlutterAddViewInfo add_view_info = {};
2001 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
2002 add_view_info.view_id = 123;
2003 add_view_info.view_metrics = &metrics123;
2004 add_view_info.user_data = &captures;
2005 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
2006 auto captures = reinterpret_cast<Captures*>(result->user_data);
2007
2008 ASSERT_TRUE(result->added);
2009 ASSERT_FALSE(captures->add_first_view.IsSignaledForTest());
2010 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
2011 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2012 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2013
2014 captures->add_first_view.Signal();
2015 };
2016
2017 // Add duplicate view 123.
2018 FlutterAddViewInfo add_duplicate_view_info = {};
2019 add_duplicate_view_info.struct_size = sizeof(FlutterAddViewInfo);
2020 add_duplicate_view_info.view_id = 123;
2021 add_duplicate_view_info.view_metrics = &metrics123;
2022 add_duplicate_view_info.user_data = &captures;
2023 add_duplicate_view_info.add_view_callback =
2024 [](const FlutterAddViewResult* result) {
2025 auto captures = reinterpret_cast<Captures*>(result->user_data);
2026
2027 ASSERT_FALSE(result->added);
2028 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2029 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
2030 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2031 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2032
2033 captures->add_duplicate_view.Signal();
2034 };
2035
2036 // Add view 456.
2037 FlutterAddViewInfo add_second_view_info = {};
2038 add_second_view_info.struct_size = sizeof(FlutterAddViewInfo);
2039 add_second_view_info.view_id = 456;
2040 add_second_view_info.view_metrics = &metrics456;
2041 add_second_view_info.user_data = &captures;
2042 add_second_view_info.add_view_callback =
2043 [](const FlutterAddViewResult* result) {
2044 auto captures = reinterpret_cast<Captures*>(result->user_data);
2045
2046 ASSERT_TRUE(result->added);
2047 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2048 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
2049 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
2050 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2051
2052 captures->add_second_view.Signal();
2053 };
2054
2055 // Remove view 456.
2056 FlutterRemoveViewInfo remove_second_view_info = {};
2057 remove_second_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
2058 remove_second_view_info.view_id = 456;
2059 remove_second_view_info.user_data = &captures;
2060 remove_second_view_info.remove_view_callback =
2061 [](const FlutterRemoveViewResult* result) {
2062 auto captures = reinterpret_cast<Captures*>(result->user_data);
2063
2064 ASSERT_TRUE(result->removed);
2065 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
2066 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
2067 ASSERT_TRUE(captures->add_second_view.IsSignaledForTest());
2068 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
2069
2070 captures->remove_second_view.Signal();
2071 };
2072
2073 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2074 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_duplicate_view_info),
2075 kSuccess);
2076 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_second_view_info),
2077 kSuccess);
2078 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_second_view_info),
2079 kSuccess);
2080 captures.remove_second_view.Wait();
2081 captures.add_second_view.Wait();
2082 captures.add_duplicate_view.Wait();
2083 captures.add_first_view.Wait();
2084 ASSERT_EQ(message_count, 3);
2085}
2086
2087//------------------------------------------------------------------------------
2088/// Test the engine can present to multiple views.
2089///
2090TEST_F(EmbedderTest, CanRenderMultipleViews) {
2091 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2092 EmbedderConfigBuilder builder(context);
2093 builder.SetSurface(DlISize(1, 1));
2094 builder.SetCompositor();
2095 builder.SetDartEntrypoint("render_all_views");
2096
2097 builder.SetRenderTargetType(
2098 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2099
2100 fml::AutoResetWaitableEvent latch0, latch123;
2101 context.GetCompositor().SetPresentCallback(
2103 size_t layers_count) {
2104 switch (view_id) {
2105 case 0:
2106 latch0.Signal();
2107 break;
2108 case 123:
2109 latch123.Signal();
2110 break;
2111 default:
2113 }
2114 },
2115 /* one_shot= */ false);
2116
2117 auto engine = builder.LaunchEngine();
2118 ASSERT_TRUE(engine.is_valid());
2119
2120 // Give the implicit view a non-zero size so that it renders something.
2121 FlutterWindowMetricsEvent metrics0 = {};
2122 metrics0.struct_size = sizeof(FlutterWindowMetricsEvent);
2123 metrics0.width = 800;
2124 metrics0.height = 600;
2125 metrics0.pixel_ratio = 1.0;
2126 metrics0.view_id = 0;
2127 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics0),
2128 kSuccess);
2129
2130 // Add view 123.
2131 FlutterWindowMetricsEvent metrics123 = {};
2132 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
2133 metrics123.width = 800;
2134 metrics123.height = 600;
2135 metrics123.pixel_ratio = 1.0;
2136 metrics123.view_id = 123;
2137
2138 FlutterAddViewInfo add_view_info = {};
2139 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
2140 add_view_info.view_id = 123;
2141 add_view_info.view_metrics = &metrics123;
2142 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
2143 ASSERT_TRUE(result->added);
2144 };
2145
2146 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2147
2148 latch0.Wait();
2149 latch123.Wait();
2150}
2151
2153 const FlutterViewFocusChangeRequest& rhs) {
2154 return lhs.view_id == rhs.view_id && lhs.state == rhs.state &&
2155 lhs.direction == rhs.direction;
2156}
2157
2158TEST_F(EmbedderTest, SendsViewFocusChangeRequest) {
2159 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2160 auto platform_task_runner = CreateNewThread("test_platform_thread");
2162 static std::mutex engine_mutex;
2163 EmbedderTestTaskRunner test_platform_task_runner(
2164 platform_task_runner, [&](FlutterTask task) {
2165 std::scoped_lock lock(engine_mutex);
2166 if (!engine.is_valid()) {
2167 return;
2168 }
2169 FlutterEngineRunTask(engine.get(), &task);
2170 });
2171 fml::CountDownLatch latch(3);
2172 std::vector<FlutterViewFocusChangeRequest> received_requests;
2173 platform_task_runner->PostTask([&]() {
2174 EmbedderConfigBuilder builder(context);
2175 builder.SetSurface(DlISize(1, 1));
2176 builder.SetDartEntrypoint("testSendViewFocusChangeRequest");
2177 const auto platform_task_runner_description =
2178 test_platform_task_runner.GetFlutterTaskRunnerDescription();
2179 builder.SetPlatformTaskRunner(&platform_task_runner_description);
2181 [&](const FlutterViewFocusChangeRequest* request) {
2182 EXPECT_TRUE(platform_task_runner->RunsTasksOnCurrentThread());
2183 received_requests.push_back(*request);
2184 latch.CountDown();
2185 });
2186 engine = builder.LaunchEngine();
2187 ASSERT_TRUE(engine.is_valid());
2188 });
2189 latch.Wait();
2190
2191 std::vector<FlutterViewFocusChangeRequest> expected_requests{
2192 {.view_id = 1, .state = kUnfocused, .direction = kUndefined},
2193 {.view_id = 2, .state = kFocused, .direction = kForward},
2194 {.view_id = 3, .state = kFocused, .direction = kBackward},
2195 };
2196
2197 ASSERT_EQ(received_requests.size(), expected_requests.size());
2198 for (size_t i = 0; i < received_requests.size(); ++i) {
2199 ASSERT_TRUE(received_requests[i] == expected_requests[i]);
2200 }
2201
2202 fml::AutoResetWaitableEvent kill_latch;
2203 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
2204 std::scoped_lock lock(engine_mutex);
2205 engine.reset();
2206
2207 // There may still be pending tasks on the platform thread that were queued
2208 // by the test_task_runner. Signal the latch after these tasks have been
2209 // consumed.
2210 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
2211 }));
2212 kill_latch.Wait();
2213}
2214
2215TEST_F(EmbedderTest, CanSendViewFocusEvent) {
2216 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2217 EmbedderConfigBuilder builder(context);
2218 builder.SetSurface(DlISize(1, 1));
2219 builder.SetDartEntrypoint("testSendViewFocusEvent");
2220
2222 std::string last_event;
2223
2224 context.AddNativeCallback(
2225 "SignalNativeTest",
2227 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
2228 context.AddNativeCallback("NotifyStringValue",
2229 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2230 const auto message_from_dart =
2232 Dart_GetNativeArgument(args, 0));
2233 last_event = message_from_dart;
2234 latch.Signal();
2235 }));
2236
2237 auto engine = builder.LaunchEngine();
2238 ASSERT_TRUE(engine.is_valid());
2239 // Wait until the focus change handler is attached.
2240 latch.Wait();
2241 latch.Reset();
2242
2243 FlutterViewFocusEvent event1{
2245 .view_id = 1,
2246 .state = kFocused,
2247 .direction = kUndefined,
2248 };
2249 FlutterEngineResult result =
2251 ASSERT_EQ(result, kSuccess);
2252 latch.Wait();
2253 ASSERT_EQ(last_event,
2254 "1 ViewFocusState.focused ViewFocusDirection.undefined");
2255
2256 FlutterViewFocusEvent event2{
2258 .view_id = 2,
2259 .state = kUnfocused,
2260 .direction = kBackward,
2261 };
2262 latch.Reset();
2263 result = FlutterEngineSendViewFocusEvent(engine.get(), &event2);
2264 ASSERT_EQ(result, kSuccess);
2265 latch.Wait();
2266 ASSERT_EQ(last_event,
2267 "2 ViewFocusState.unfocused ViewFocusDirection.backward");
2268}
2269
2270//------------------------------------------------------------------------------
2271/// Test that the backing store is created with the correct view ID, is used
2272/// for the correct view, and is cached according to their views.
2273///
2274/// The test involves two frames:
2275/// 1. The first frame renders the implicit view and the second view.
2276/// 2. The second frame renders the implicit view and the third view.
2277///
2278/// The test verifies that:
2279/// - Each backing store is created with a valid view ID.
2280/// - Each backing store is presented for the view that it was created for.
2281/// - Both frames render the expected sets of views.
2282/// - By the end of frame 1, only 2 backing stores were created.
2283/// - By the end of frame 2, only 3 backing stores were created. This ensures
2284/// that the backing store for the 2nd view is not reused for the 3rd view.
2285TEST_F(EmbedderTest, BackingStoresCorrespondToTheirViews) {
2286 constexpr FlutterViewId kSecondViewId = 123;
2287 constexpr FlutterViewId kThirdViewId = 456;
2288 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2289
2290 EmbedderConfigBuilder builder(context);
2291 builder.SetDartEntrypoint("render_all_views");
2292 builder.SetSurface(DlISize(800, 600));
2293 builder.SetCompositor();
2294
2296 context.GetCompositor().GetGrContext(),
2297 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2298
2299 // The variables needed by the callbacks of the compositor.
2300 struct CompositorUserData {
2302 // Each latch is signaled when its corresponding view is presented.
2303 fml::AutoResetWaitableEvent latch_implicit;
2304 fml::AutoResetWaitableEvent latch_second;
2305 fml::AutoResetWaitableEvent latch_third;
2306 // Whether the respective view should be rendered in the frame.
2307 bool second_expected;
2308 bool third_expected;
2309 // The total number of backing stores created to verify caching.
2310 int backing_stores_created;
2311 };
2312 CompositorUserData compositor_user_data{
2313 .producer = &producer,
2314 .backing_stores_created = 0,
2315 };
2316
2317 builder.GetCompositor() = FlutterCompositor{
2318 .struct_size = sizeof(FlutterCompositor),
2319 .user_data = reinterpret_cast<void*>(&compositor_user_data),
2320 .create_backing_store_callback =
2321 [](const FlutterBackingStoreConfig* config,
2322 FlutterBackingStore* backing_store_out, void* user_data) {
2323 // Verify that the backing store comes with the correct view ID.
2324 EXPECT_TRUE(config->view_id == 0 ||
2325 config->view_id == kSecondViewId ||
2326 config->view_id == kThirdViewId);
2327 auto compositor_user_data =
2328 reinterpret_cast<CompositorUserData*>(user_data);
2329 compositor_user_data->backing_stores_created += 1;
2330 bool result = compositor_user_data->producer->Create(
2331 config, backing_store_out);
2332 // The created backing store has a user_data that records the view
2333 // that the store is created for.
2334 backing_store_out->user_data =
2335 reinterpret_cast<void*>(config->view_id);
2336 return result;
2337 },
2338 .collect_backing_store_callback = [](const FlutterBackingStore* renderer,
2339 void* user_data) { return true; },
2340 .present_layers_callback = nullptr,
2341 .avoid_backing_store_cache = false,
2342 .present_view_callback =
2343 [](const FlutterPresentViewInfo* info) {
2344 EXPECT_EQ(info->layers_count, 1u);
2345 // Verify that the given layer's backing store has the same view ID
2346 // as the target view.
2347 int64_t store_view_id = reinterpret_cast<int64_t>(
2348 info->layers[0]->backing_store->user_data);
2349 EXPECT_EQ(store_view_id, info->view_id);
2350 auto compositor_user_data =
2351 reinterpret_cast<CompositorUserData*>(info->user_data);
2352 // Verify that the respective views are rendered.
2353 switch (info->view_id) {
2354 case 0:
2355 compositor_user_data->latch_implicit.Signal();
2356 break;
2357 case kSecondViewId:
2358 EXPECT_TRUE(compositor_user_data->second_expected);
2359 compositor_user_data->latch_second.Signal();
2360 break;
2361 case kThirdViewId:
2362 EXPECT_TRUE(compositor_user_data->third_expected);
2363 compositor_user_data->latch_third.Signal();
2364 break;
2365 default:
2367 }
2368 return true;
2369 },
2370 };
2371
2372 compositor_user_data.second_expected = true;
2373 compositor_user_data.third_expected = false;
2374
2375 /*=== First frame ===*/
2376
2377 auto engine = builder.LaunchEngine();
2378 ASSERT_TRUE(engine.is_valid());
2379
2380 // Give the implicit view a non-zero size so that it renders something.
2381 FlutterWindowMetricsEvent metrics_implicit = {
2383 .width = 800,
2384 .height = 600,
2385 .pixel_ratio = 1.0,
2386 .view_id = 0,
2387 };
2388 ASSERT_EQ(
2389 FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics_implicit),
2390 kSuccess);
2391
2392 // Add the second view.
2393 FlutterWindowMetricsEvent metrics_add = {
2395 .width = 800,
2396 .height = 600,
2397 .pixel_ratio = 1.0,
2398 .view_id = kSecondViewId,
2399 };
2400
2401 FlutterAddViewInfo add_view_info = {};
2402 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
2403 add_view_info.view_id = kSecondViewId;
2404 add_view_info.view_metrics = &metrics_add;
2405 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
2406 ASSERT_TRUE(result->added);
2407 };
2408
2409 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2410
2411 compositor_user_data.latch_implicit.Wait();
2412 compositor_user_data.latch_second.Wait();
2413
2414 /*=== Second frame ===*/
2415
2416 compositor_user_data.second_expected = false;
2417 compositor_user_data.third_expected = true;
2418 EXPECT_EQ(compositor_user_data.backing_stores_created, 2);
2419
2420 // Remove the second view
2421 FlutterRemoveViewInfo remove_view_info = {};
2422 remove_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
2423 remove_view_info.view_id = kSecondViewId;
2424 remove_view_info.remove_view_callback =
2425 [](const FlutterRemoveViewResult* result) {
2426 ASSERT_TRUE(result->removed);
2427 };
2428 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_view_info), kSuccess);
2429
2430 // Add the third view.
2431 add_view_info.view_id = kThirdViewId;
2432 metrics_add.view_id = kThirdViewId;
2433 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
2434 // Adding the view should have scheduled a frame.
2435
2436 compositor_user_data.latch_implicit.Wait();
2437 compositor_user_data.latch_third.Wait();
2438 EXPECT_EQ(compositor_user_data.backing_stores_created, 3);
2439}
2440
2441TEST_F(EmbedderTest, CanUpdateLocales) {
2442 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2443 EmbedderConfigBuilder builder(context);
2444 builder.SetSurface(DlISize(1, 1));
2445 builder.SetDartEntrypoint("can_receive_locale_updates");
2447 context.AddNativeCallback(
2448 "SignalNativeTest",
2450 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
2451
2452 fml::AutoResetWaitableEvent check_latch;
2453 context.AddNativeCallback(
2454 "SignalNativeCount",
2455 CREATE_NATIVE_ENTRY([&check_latch](Dart_NativeArguments args) {
2457 Dart_GetNativeArgument(args, 0)),
2458 2);
2459 check_latch.Signal();
2460 }));
2461
2462 auto engine = builder.LaunchEngine();
2463 ASSERT_TRUE(engine.is_valid());
2464
2465 // Wait for the application to attach the listener.
2466 latch.Wait();
2467
2468 FlutterLocale locale1 = {};
2469 locale1.struct_size = sizeof(locale1);
2470 locale1.language_code = ""; // invalid
2471 locale1.country_code = "US";
2472 locale1.script_code = "";
2473 locale1.variant_code = nullptr;
2474
2475 FlutterLocale locale2 = {};
2476 locale2.struct_size = sizeof(locale2);
2477 locale2.language_code = "zh";
2478 locale2.country_code = "CN";
2479 locale2.script_code = "Hans";
2480 locale2.variant_code = nullptr;
2481
2482 std::vector<const FlutterLocale*> locales;
2483 locales.push_back(&locale1);
2484 locales.push_back(&locale2);
2485
2486 ASSERT_EQ(
2487 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2489
2490 // Fix the invalid code.
2491 locale1.language_code = "en";
2492
2493 ASSERT_EQ(
2494 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2495 kSuccess);
2496
2497 check_latch.Wait();
2498}
2499
2500TEST_F(EmbedderTest, LocalizationCallbacksCalled) {
2501 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2503 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2504 EmbedderConfigBuilder builder(context);
2505 builder.SetSurface(DlISize(1, 1));
2506 auto engine = builder.LaunchEngine();
2507 ASSERT_TRUE(engine.is_valid());
2508 // Wait for the root isolate to launch.
2509 latch.Wait();
2510
2512 std::vector<std::string> supported_locales;
2513 supported_locales.push_back("es");
2514 supported_locales.push_back("MX");
2515 supported_locales.push_back("");
2516 auto result = shell.GetPlatformView()->ComputePlatformResolvedLocales(
2517 supported_locales);
2518
2519 ASSERT_EQ((*result).size(), supported_locales.size()); // 3
2520 ASSERT_EQ((*result)[0], supported_locales[0]);
2521 ASSERT_EQ((*result)[1], supported_locales[1]);
2522 ASSERT_EQ((*result)[2], supported_locales[2]);
2523
2524 engine.reset();
2525}
2526
2531
2532TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) {
2533 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2534
2535 EmbedderConfigBuilder builder(context);
2536 builder.SetSurface(DlISize(1024, 600));
2537 builder.SetCompositor();
2538 builder.SetDartEntrypoint("verify_b143464703");
2539
2540 builder.SetRenderTargetType(
2541 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2542
2543 // setup the screenshot promise.
2544 auto rendered_scene = context.GetNextSceneImage();
2545
2546 fml::CountDownLatch latch(1);
2547 context.GetCompositor().SetNextPresentCallback(
2549 size_t layers_count) {
2550 ASSERT_EQ(layers_count, 2u);
2551
2552 // Layer 0 (Root)
2553 {
2554 FlutterBackingStore backing_store = *layers[0]->backing_store;
2555 backing_store.type = kFlutterBackingStoreTypeSoftware;
2556 backing_store.did_update = true;
2557
2558 FlutterRect paint_region_rects[] = {
2559 FlutterRectMakeLTRB(0, 0, 1024, 600),
2560 };
2561 FlutterRegion paint_region = {
2562 .struct_size = sizeof(FlutterRegion),
2563 .rects_count = 1,
2564 .rects = paint_region_rects,
2565 };
2566 FlutterBackingStorePresentInfo present_info = {
2568 .paint_region = &paint_region,
2569 };
2570
2571 FlutterLayer layer = {};
2572 layer.struct_size = sizeof(layer);
2574 layer.backing_store = &backing_store;
2575 layer.size = FlutterSizeMake(1024.0, 600.0);
2576 layer.offset = FlutterPointMake(0.0, 0.0);
2577 layer.backing_store_present_info = &present_info;
2578
2579 ASSERT_EQ(*layers[0], layer);
2580 }
2581
2582 // Layer 1
2583 {
2586 platform_view.identifier = 42;
2587
2588 FlutterLayer layer = {};
2589 layer.struct_size = sizeof(layer);
2592 layer.size = FlutterSizeMake(1024.0, 540.0);
2593 layer.offset = FlutterPointMake(135.0, 60.0);
2594
2595 ASSERT_EQ(*layers[1], layer);
2596 }
2597
2598 latch.CountDown();
2599 });
2600
2601 context.GetCompositor().SetPlatformViewRendererCallback(
2602 [](const FlutterLayer& layer,
2603 GrDirectContext* context) -> sk_sp<SkImage> {
2605 layer, nullptr /* null because software compositor */);
2606 auto canvas = surface->getCanvas();
2607 FML_CHECK(canvas != nullptr);
2608
2609 switch (layer.platform_view->identifier) {
2610 case 42: {
2611 SkPaint paint;
2612 // See dart test for total order.
2613 paint.setColor(SK_ColorGREEN);
2614 paint.setAlpha(127);
2615 const auto& rect =
2616 SkRect::MakeWH(layer.size.width, layer.size.height);
2617 canvas->drawRect(rect, paint);
2618 } break;
2619 default:
2620 // Asked to render an unknown platform view.
2621 FML_CHECK(false)
2622 << "Test was asked to composite an unknown platform view.";
2623 }
2624
2625 return surface->makeImageSnapshot();
2626 });
2627
2628 auto engine = builder.LaunchEngine();
2629
2630 // Send a window metrics events so frames may be scheduled.
2631 FlutterWindowMetricsEvent event = {};
2632 event.struct_size = sizeof(event);
2633 event.width = 1024;
2634 event.height = 600;
2635 event.pixel_ratio = 1.0;
2636 event.physical_view_inset_top = 0.0;
2637 event.physical_view_inset_right = 0.0;
2638 event.physical_view_inset_bottom = 0.0;
2639 event.physical_view_inset_left = 0.0;
2640 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2641 kSuccess);
2642 ASSERT_TRUE(engine.is_valid());
2643
2644 // wait for scene to be rendered.
2645 latch.Wait();
2646
2647 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
2648 // platforms.
2649#if !defined(FML_OS_LINUX)
2650 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
2651#endif // FML_OS_LINUX
2652 ASSERT_TRUE(
2653 ImageMatchesFixture("verifyb143464703_soft_noxform.png", rendered_scene));
2654}
2655
2656TEST_F(EmbedderTest, CanSendLowMemoryNotification) {
2657 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2658
2659 EmbedderConfigBuilder builder(context);
2660 builder.SetSurface(DlISize(1, 1));
2661
2662 auto engine = builder.LaunchEngine();
2663
2664 ASSERT_TRUE(engine.is_valid());
2665
2666 // TODO(chinmaygarde): The shell ought to have a mechanism for notification
2667 // dispatch that engine subsystems can register handlers to. This would allow
2668 // the raster cache and the secondary context caches to respond to
2669 // notifications. Once that is in place, this test can be updated to actually
2670 // ensure that the dispatched message is visible to engine subsystems.
2672}
2673
2674TEST_F(EmbedderTest, CanPostTaskToAllNativeThreads) {
2676 size_t worker_count = 0;
2677 fml::AutoResetWaitableEvent sync_latch;
2678
2679 // One of the threads that the callback will be posted to is the platform
2680 // thread. So we cannot wait for assertions to complete on the platform
2681 // thread. Create a new thread to manage the engine instance and wait for
2682 // assertions on the test thread.
2683 auto platform_task_runner = CreateNewThread("platform_thread");
2684
2685 platform_task_runner->PostTask([&]() {
2686 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2687 EmbedderConfigBuilder builder(context);
2688 builder.SetSurface(DlISize(1, 1));
2689
2690 engine = builder.LaunchEngine();
2691
2692 ASSERT_TRUE(engine.is_valid());
2693
2694 worker_count = ToEmbedderEngine(engine.get())
2695 ->GetShell()
2696 .GetDartVM()
2698 ->GetWorkerCount();
2699
2700 sync_latch.Signal();
2701 });
2702
2703 sync_latch.Wait();
2704
2705 const auto engine_threads_count = worker_count + 4u;
2706
2707 struct Captures {
2708 // Waits the adequate number of callbacks to fire.
2709 fml::CountDownLatch latch;
2710
2711 // This class will be accessed from multiple threads concurrently to track
2712 // thread specific information that is later checked. All updates to fields
2713 // in this struct must be made with this mutex acquired.
2714
2715 std::mutex captures_mutex;
2716 // Ensures that the expect number of distinct threads were serviced.
2717 std::set<std::thread::id> thread_ids;
2718
2719 size_t platform_threads_count = 0;
2720 size_t render_threads_count = 0;
2721 size_t ui_threads_count = 0;
2722 size_t worker_threads_count = 0;
2723
2724 explicit Captures(size_t count) : latch(count) {}
2725 };
2726
2727 Captures captures(engine_threads_count);
2728
2729 platform_task_runner->PostTask([&]() {
2731 engine.get(),
2732 [](FlutterNativeThreadType type, void* baton) {
2733 auto captures = reinterpret_cast<Captures*>(baton);
2734 {
2735 std::scoped_lock lock(captures->captures_mutex);
2736 switch (type) {
2737 case kFlutterNativeThreadTypeRender:
2738 captures->render_threads_count++;
2739 break;
2740 case kFlutterNativeThreadTypeWorker:
2741 captures->worker_threads_count++;
2742 break;
2743 case kFlutterNativeThreadTypeUI:
2744 captures->ui_threads_count++;
2745 break;
2746 case kFlutterNativeThreadTypePlatform:
2747 captures->platform_threads_count++;
2748 break;
2749 }
2750 captures->thread_ids.insert(std::this_thread::get_id());
2751 }
2752 captures->latch.CountDown();
2753 },
2754 &captures),
2755 kSuccess);
2756 });
2757
2758 captures.latch.Wait();
2759 ASSERT_EQ(captures.thread_ids.size(), engine_threads_count);
2760 ASSERT_EQ(captures.platform_threads_count, 1u);
2761 ASSERT_EQ(captures.render_threads_count, 1u);
2762 ASSERT_EQ(captures.ui_threads_count, 1u);
2763 ASSERT_EQ(captures.worker_threads_count, worker_count + 1u /* for IO */);
2764 EXPECT_GE(captures.worker_threads_count - 1, 2u);
2765 EXPECT_LE(captures.worker_threads_count - 1, 4u);
2766
2767 platform_task_runner->PostTask([&]() {
2768 engine.reset();
2769 sync_latch.Signal();
2770 });
2771 sync_latch.Wait();
2772
2773 // The engine should have already been destroyed on the platform task runner.
2774 ASSERT_FALSE(engine.is_valid());
2775}
2776
2777TEST_F(EmbedderTest, InvalidAOTDataSourcesMustReturnError) {
2779 GTEST_SKIP();
2780 return;
2781 }
2782 FlutterEngineAOTDataSource data_in = {};
2783 FlutterEngineAOTData data_out = nullptr;
2784
2785 // Null source specified.
2786 ASSERT_EQ(FlutterEngineCreateAOTData(nullptr, &data_out), kInvalidArguments);
2787 ASSERT_EQ(data_out, nullptr);
2788
2789 // Null data_out specified.
2790 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, nullptr), kInvalidArguments);
2791
2792 // Invalid FlutterEngineAOTDataSourceType type specified.
2793 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2794 data_in.type = static_cast<FlutterEngineAOTDataSourceType>(-1);
2795 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2796 ASSERT_EQ(data_out, nullptr);
2797
2798 // Invalid ELF path specified.
2800 data_in.elf_path = nullptr;
2801 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2802 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2803 ASSERT_EQ(data_in.elf_path, nullptr);
2804 ASSERT_EQ(data_out, nullptr);
2805
2806 // Invalid ELF path specified.
2807 data_in.elf_path = "";
2808 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2809 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2810 ASSERT_EQ(data_in.elf_path, "");
2811 ASSERT_EQ(data_out, nullptr);
2812
2813 // Could not find VM snapshot data.
2814 data_in.elf_path = "/bin/true";
2815 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2816 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2817 ASSERT_EQ(data_in.elf_path, "/bin/true");
2818 ASSERT_EQ(data_out, nullptr);
2819}
2820
2821TEST_F(EmbedderTest, MustNotRunWithMultipleAOTSources) {
2823 GTEST_SKIP();
2824 return;
2825 }
2826 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2827
2828 EmbedderConfigBuilder builder(
2829 context,
2830 EmbedderConfigBuilder::InitializationPreference::kMultiAOTInitialize);
2831
2832 builder.SetSurface(DlISize(1, 1));
2833
2834 auto engine = builder.LaunchEngine();
2835 ASSERT_FALSE(engine.is_valid());
2836}
2837
2838TEST_F(EmbedderTest, CanCreateAndCollectAValidElfSource) {
2840 GTEST_SKIP();
2841 return;
2842 }
2843 FlutterEngineAOTDataSource data_in = {};
2844 FlutterEngineAOTData data_out = nullptr;
2845
2846 // Collecting a null object should be allowed
2847 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2848
2849 const auto elf_path =
2851
2853 data_in.elf_path = elf_path.c_str();
2854
2855 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kSuccess);
2856 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2857 ASSERT_EQ(data_in.elf_path, elf_path.c_str());
2858 ASSERT_NE(data_out, nullptr);
2859
2860 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2861}
2862
2863TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) {
2865 GTEST_SKIP();
2866 return;
2867 }
2868 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2869
2871 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2872
2873 EmbedderConfigBuilder builder(
2874 context,
2875 EmbedderConfigBuilder::InitializationPreference::kAOTDataInitialize);
2876
2877 builder.SetSurface(DlISize(1, 1));
2878
2879 auto engine = builder.LaunchEngine();
2880 ASSERT_TRUE(engine.is_valid());
2881
2882 // Wait for the root isolate to launch.
2883 latch.Wait();
2884 engine.reset();
2885}
2886
2887#if defined(__clang_analyzer__)
2888#define TEST_VM_SNAPSHOT_DATA "vm_data"
2889#define TEST_VM_SNAPSHOT_INSTRUCTIONS "vm_instructions"
2890#define TEST_ISOLATE_SNAPSHOT_DATA "isolate_data"
2891#define TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS "isolate_instructions"
2892#endif
2893
2894//------------------------------------------------------------------------------
2895/// PopulateJITSnapshotMappingCallbacks should successfully change the callbacks
2896/// of the snapshots in the engine's settings when JIT snapshots are explicitly
2897/// defined.
2898///
2899TEST_F(EmbedderTest, CanSuccessfullyPopulateSpecificJITSnapshotCallbacks) {
2900// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2901#if defined(OS_FUCHSIA)
2902 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2903#else
2904
2905 // This test is only relevant in JIT mode.
2907 GTEST_SKIP();
2908 return;
2909 }
2910
2911 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2912 EmbedderConfigBuilder builder(context);
2913 builder.SetSurface(DlISize(1, 1));
2914
2915 // Construct the location of valid JIT snapshots.
2916 const std::string src_path = GetSourcePath();
2917 const std::string vm_snapshot_data =
2918 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2919 const std::string vm_snapshot_instructions =
2920 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2921 const std::string isolate_snapshot_data =
2922 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2923 const std::string isolate_snapshot_instructions =
2924 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2925
2926 // Explicitly define the locations of the JIT snapshots
2928 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2930 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2932 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2934 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2935
2936 auto engine = builder.LaunchEngine();
2937
2939 const Settings settings = shell.GetSettings();
2940
2941 ASSERT_NE(settings.vm_snapshot_data(), nullptr);
2942 ASSERT_NE(settings.vm_snapshot_instr(), nullptr);
2943 ASSERT_NE(settings.isolate_snapshot_data(), nullptr);
2944 ASSERT_NE(settings.isolate_snapshot_instr(), nullptr);
2945 ASSERT_NE(settings.dart_library_sources_kernel(), nullptr);
2946#endif // OS_FUCHSIA
2947}
2948
2949//------------------------------------------------------------------------------
2950/// PopulateJITSnapshotMappingCallbacks should still be able to successfully
2951/// change the callbacks of the snapshots in the engine's settings when JIT
2952/// snapshots are explicitly defined. However, if those snapshot locations are
2953/// invalid, the callbacks should return a nullptr.
2954///
2955TEST_F(EmbedderTest, JITSnapshotCallbacksFailWithInvalidLocation) {
2956// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2957#if defined(OS_FUCHSIA)
2958 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2959#else
2960
2961 // This test is only relevant in JIT mode.
2963 GTEST_SKIP();
2964 return;
2965 }
2966
2967 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
2968 EmbedderConfigBuilder builder(context);
2969 builder.SetSurface(DlISize(1, 1));
2970
2971 // Explicitly define the locations of the invalid JIT snapshots
2973 reinterpret_cast<const uint8_t*>("invalid_vm_data");
2975 reinterpret_cast<const uint8_t*>("invalid_vm_instructions");
2977 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2979 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2980
2981 auto engine = builder.LaunchEngine();
2982
2984 const Settings settings = shell.GetSettings();
2985
2986 ASSERT_EQ(settings.vm_snapshot_data(), nullptr);
2987 ASSERT_EQ(settings.vm_snapshot_instr(), nullptr);
2988 ASSERT_EQ(settings.isolate_snapshot_data(), nullptr);
2989 ASSERT_EQ(settings.isolate_snapshot_instr(), nullptr);
2990#endif // OS_FUCHSIA
2991}
2992
2993//------------------------------------------------------------------------------
2994/// The embedder must be able to run explicitly specified snapshots in JIT mode
2995/// (i.e. when those are present in known locations).
2996///
2997TEST_F(EmbedderTest, CanLaunchEngineWithSpecifiedJITSnapshots) {
2998 // This test is only relevant in JIT mode.
3000 GTEST_SKIP();
3001 return;
3002 }
3003
3004 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3005 EmbedderConfigBuilder builder(context);
3006 builder.SetSurface(DlISize(1, 1));
3007
3008 // Construct the location of valid JIT snapshots.
3009 const std::string src_path = GetSourcePath();
3010 const std::string vm_snapshot_data =
3011 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
3012 const std::string vm_snapshot_instructions =
3013 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
3014 const std::string isolate_snapshot_data =
3015 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
3016 const std::string isolate_snapshot_instructions =
3017 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
3018
3019 // Explicitly define the locations of the JIT snapshots
3021 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
3023 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
3025 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
3027 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
3028
3029 auto engine = builder.LaunchEngine();
3030 ASSERT_TRUE(engine.is_valid());
3031}
3032
3033//------------------------------------------------------------------------------
3034/// The embedder must be able to run in JIT mode when only some snapshots are
3035/// specified.
3036///
3037TEST_F(EmbedderTest, CanLaunchEngineWithSomeSpecifiedJITSnapshots) {
3038 // This test is only relevant in JIT mode.
3040 GTEST_SKIP();
3041 return;
3042 }
3043
3044 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3045 EmbedderConfigBuilder builder(context);
3046 builder.SetSurface(DlISize(1, 1));
3047
3048 // Construct the location of valid JIT snapshots.
3049 const std::string src_path = GetSourcePath();
3050 const std::string vm_snapshot_data =
3051 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
3052 const std::string vm_snapshot_instructions =
3053 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
3054
3055 // Explicitly define the locations of the JIT snapshots
3057 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
3059 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
3060
3061 auto engine = builder.LaunchEngine();
3062 ASSERT_TRUE(engine.is_valid());
3063}
3064
3065//------------------------------------------------------------------------------
3066/// The embedder must be able to run in JIT mode even when the specfied
3067/// snapshots are invalid. It should be able to resolve them as it would when
3068/// the snapshots are not specified.
3069///
3070TEST_F(EmbedderTest, CanLaunchEngineWithInvalidJITSnapshots) {
3071 // This test is only relevant in JIT mode.
3073 GTEST_SKIP();
3074 return;
3075 }
3076
3077 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3078 EmbedderConfigBuilder builder(context);
3079 builder.SetSurface(DlISize(1, 1));
3080
3081 // Explicitly define the locations of the JIT snapshots
3083 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
3085 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
3086
3087 auto engine = builder.LaunchEngine();
3088 ASSERT_TRUE(engine.is_valid());
3090}
3091
3092//------------------------------------------------------------------------------
3093/// The embedder must be able to launch even when the snapshots are not
3094/// explicitly defined in JIT mode. It must be able to resolve those snapshots.
3095///
3096TEST_F(EmbedderTest, CanLaunchEngineWithUnspecifiedJITSnapshots) {
3097 // This test is only relevant in JIT mode.
3099 GTEST_SKIP();
3100 return;
3101 }
3102
3103 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3104 EmbedderConfigBuilder builder(context);
3105 builder.SetSurface(DlISize(1, 1));
3106
3107 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_data, nullptr);
3108 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_instructions, nullptr);
3109 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_data, nullptr);
3110 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_instructions, nullptr);
3111
3112 auto engine = builder.LaunchEngine();
3113 ASSERT_TRUE(engine.is_valid());
3114}
3115
3116TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) {
3117 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3118 EmbedderConfigBuilder builder(context);
3119 builder.SetSurface(DlISize(1, 1));
3120 auto engine = builder.LaunchEngine();
3121
3122 ASSERT_TRUE(engine.is_valid());
3123
3124 FlutterWindowMetricsEvent event = {};
3125 event.struct_size = sizeof(event);
3126 event.width = 800;
3127 event.height = 600;
3128 event.pixel_ratio = 0.0;
3129 event.physical_view_inset_top = 0.0;
3130 event.physical_view_inset_right = 0.0;
3131 event.physical_view_inset_bottom = 0.0;
3132 event.physical_view_inset_left = 0.0;
3133
3134 // Pixel ratio must be positive.
3135 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3137
3138 event.pixel_ratio = 1.0;
3139 event.physical_view_inset_top = -1.0;
3140 event.physical_view_inset_right = -1.0;
3141 event.physical_view_inset_bottom = -1.0;
3142 event.physical_view_inset_left = -1.0;
3143
3144 // Physical view insets must be non-negative.
3145 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3147
3148 event.physical_view_inset_top = 700;
3149 event.physical_view_inset_right = 900;
3150 event.physical_view_inset_bottom = 700;
3151 event.physical_view_inset_left = 900;
3152
3153 // Top/bottom insets cannot be greater than height.
3154 // Left/right insets cannot be greater than width.
3155 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3157}
3158
3159TEST_F(EmbedderTest, WindowMetricsEventWithConstraints) {
3160 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3161 EmbedderConfigBuilder builder(context);
3162 builder.SetSurface(DlISize(1, 1));
3163 auto engine = builder.LaunchEngine();
3164
3165 ASSERT_TRUE(engine.is_valid());
3166
3167 // Test with has_constraints = true and valid constraints
3168 FlutterWindowMetricsEvent event = {};
3169 event.struct_size = sizeof(event);
3170 event.width = 800;
3171 event.height = 600;
3172 event.pixel_ratio = 1.0;
3173 event.has_constraints = true;
3174 event.min_width_constraint = 400;
3175 event.max_width_constraint = 1200;
3176 event.min_height_constraint = 300;
3177 event.max_height_constraint = 900;
3178
3179 // Should succeed with valid constraints
3180 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3181 kSuccess);
3182
3183 // Test with has_constraints = false
3184 // Constraints should be ignored and set to current width/height
3185 FlutterWindowMetricsEvent event_no_constraints = {};
3186 event_no_constraints.struct_size = sizeof(event_no_constraints);
3187 event_no_constraints.width = 1024;
3188 event_no_constraints.height = 768;
3189 event_no_constraints.pixel_ratio = 1.0;
3190 event_no_constraints.has_constraints = false;
3191 // These constraint values should be ignored
3192 event_no_constraints.min_width_constraint = 0;
3193 event_no_constraints.max_width_constraint = 0;
3194 event_no_constraints.min_height_constraint = 0;
3195 event_no_constraints.max_height_constraint = 0;
3196
3197 // Should succeed even with invalid constraint values because has_constraints
3198 // is false
3199 ASSERT_EQ(
3200 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_no_constraints),
3201 kSuccess);
3202
3203 // Test with has_constraints = true but width violates min constraint
3204 FlutterWindowMetricsEvent event_invalid_min = {};
3205 event_invalid_min.struct_size = sizeof(event_invalid_min);
3206 event_invalid_min.width = 300; // Less than min_width_constraint
3207 event_invalid_min.height = 600;
3208 event_invalid_min.pixel_ratio = 1.0;
3209 event_invalid_min.has_constraints = true;
3210 event_invalid_min.min_width_constraint = 400;
3211 event_invalid_min.max_width_constraint = 1200;
3212 event_invalid_min.min_height_constraint = 300;
3213 event_invalid_min.max_height_constraint = 900;
3214
3215 // Should fail because width < min_width_constraint
3216 ASSERT_EQ(
3217 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_min),
3219
3220 // Test with has_constraints = true but width violates max constraint
3221 FlutterWindowMetricsEvent event_invalid_max = {};
3222 event_invalid_max.struct_size = sizeof(event_invalid_max);
3223 event_invalid_max.width = 1300; // Greater than max_width_constraint
3224 event_invalid_max.height = 600;
3225 event_invalid_max.pixel_ratio = 1.0;
3226 event_invalid_max.has_constraints = true;
3227 event_invalid_max.min_width_constraint = 400;
3228 event_invalid_max.max_width_constraint = 1200;
3229 event_invalid_max.min_height_constraint = 300;
3230 event_invalid_max.max_height_constraint = 900;
3231
3232 // Should fail because width > max_width_constraint
3233 ASSERT_EQ(
3234 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_max),
3236
3237 // Test with has_constraints = true but height violates constraints
3238 FlutterWindowMetricsEvent event_invalid_height = {};
3239 event_invalid_height.struct_size = sizeof(event_invalid_height);
3240 event_invalid_height.width = 800;
3241 event_invalid_height.height = 200; // Less than min_height_constraint
3242 event_invalid_height.pixel_ratio = 1.0;
3243 event_invalid_height.has_constraints = true;
3244 event_invalid_height.min_width_constraint = 400;
3245 event_invalid_height.max_width_constraint = 1200;
3246 event_invalid_height.min_height_constraint = 300;
3247 event_invalid_height.max_height_constraint = 900;
3248
3249 // Should fail because height < min_height_constraint
3250 ASSERT_EQ(
3251 FlutterEngineSendWindowMetricsEvent(engine.get(), &event_invalid_height),
3253}
3254
3256 EmbedderTest& test,
3257 std::string entrypoint,
3259 const std::vector<uint8_t>& bytes) {
3260 auto& context = test.GetEmbedderContext<EmbedderTestContextSoftware>();
3261
3262 EmbedderConfigBuilder builder(context);
3264 bool matches = false;
3265
3266 builder.SetSurface(DlISize(1, 1));
3267 builder.SetCompositor();
3268 builder.SetDartEntrypoint(std::move(entrypoint));
3269 builder.SetRenderTargetType(
3270 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer2,
3271 pixfmt);
3272
3273 auto engine = builder.LaunchEngine();
3274 ASSERT_TRUE(engine.is_valid());
3275
3276 context.GetCompositor().SetNextPresentCallback(
3277 [&context, &matches, &bytes, &latch](FlutterViewId view_id,
3278 const FlutterLayer** layers,
3279 size_t layers_count) {
3281 ASSERT_EQ(layers[0]->backing_store->type,
3283 sk_sp<SkSurface> surface =
3284 context.GetCompositor().GetSurface(layers[0]->backing_store);
3285 matches = SurfacePixelDataMatchesBytes(surface.get(), bytes);
3286 latch.Signal();
3287 });
3288
3289 // Send a window metrics events so frames may be scheduled.
3290 FlutterWindowMetricsEvent event = {};
3291 event.struct_size = sizeof(event);
3292 event.width = 1;
3293 event.height = 1;
3294 event.pixel_ratio = 1.0;
3295 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3296 kSuccess);
3297
3298 latch.Wait();
3299 ASSERT_TRUE(matches);
3300
3301 engine.reset();
3302}
3303
3304template <typename T>
3306 EmbedderTest& test,
3307 std::string entrypoint,
3309 T pixelvalue) {
3310 uint8_t* bytes = reinterpret_cast<uint8_t*>(&pixelvalue);
3312 test, std::move(entrypoint), pixfmt,
3313 std::vector<uint8_t>(bytes, bytes + sizeof(T)));
3314}
3315
3316#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher) \
3317 TEST_F(EmbedderTest, SoftwareRenderingPixelFormats##test_name) { \
3318 expectSoftwareRenderingOutputMatches(*this, #dart_entrypoint, pixfmt, \
3319 matcher); \
3320 }
3321
3322// Don't test the pixel formats that contain padding (so an X) and the
3323// kFlutterSoftwarePixelFormatNative32 pixel format here, so we don't add any
3324// flakiness.
3325SW_PIXFMT_TEST_F(RedRGBA565xF800,
3326 draw_solid_red,
3328 (uint16_t)0xF800);
3329SW_PIXFMT_TEST_F(RedRGBA4444xF00F,
3330 draw_solid_red,
3332 (uint16_t)0xF00F);
3333SW_PIXFMT_TEST_F(RedRGBA8888xFFx00x00xFF,
3334 draw_solid_red,
3336 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3337SW_PIXFMT_TEST_F(RedBGRA8888x00x00xFFxFF,
3338 draw_solid_red,
3340 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3342 draw_solid_red,
3344 (uint8_t)0x36);
3345
3346SW_PIXFMT_TEST_F(GreenRGB565x07E0,
3347 draw_solid_green,
3349 (uint16_t)0x07E0);
3350SW_PIXFMT_TEST_F(GreenRGBA4444x0F0F,
3351 draw_solid_green,
3353 (uint16_t)0x0F0F);
3354SW_PIXFMT_TEST_F(GreenRGBA8888x00xFFx00xFF,
3355 draw_solid_green,
3357 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3358SW_PIXFMT_TEST_F(GreenBGRA8888x00xFFx00xFF,
3359 draw_solid_green,
3361 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
3362SW_PIXFMT_TEST_F(GreenGray8xB6,
3363 draw_solid_green,
3365 (uint8_t)0xB6);
3366
3367SW_PIXFMT_TEST_F(BlueRGB565x001F,
3368 draw_solid_blue,
3370 (uint16_t)0x001F);
3371SW_PIXFMT_TEST_F(BlueRGBA4444x00FF,
3372 draw_solid_blue,
3374 (uint16_t)0x00FF);
3375SW_PIXFMT_TEST_F(BlueRGBA8888x00x00xFFxFF,
3376 draw_solid_blue,
3378 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
3379SW_PIXFMT_TEST_F(BlueBGRA8888xFFx00x00xFF,
3380 draw_solid_blue,
3382 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
3383SW_PIXFMT_TEST_F(BlueGray8x12,
3384 draw_solid_blue,
3386 (uint8_t)0x12);
3387
3388//------------------------------------------------------------------------------
3389// Key Data
3390//------------------------------------------------------------------------------
3391
3392typedef struct {
3393 std::shared_ptr<fml::AutoResetWaitableEvent> latch;
3396
3397// Convert `kind` in integer form to its enum form.
3398//
3399// It performs a revesed mapping from `_serializeKeyEventType`
3400// in shell/platform/embedder/fixtures/main.dart.
3402 switch (kind) {
3403 case 1:
3405 case 2:
3407 case 3:
3409 default:
3412 }
3413}
3414
3415// Convert `source` in integer form to its enum form.
3416//
3417// It performs a revesed mapping from `_serializeKeyEventDeviceType`
3418// in shell/platform/embedder/fixtures/main.dart.
3420 switch (source) {
3421 case 1:
3423 case 2:
3425 case 3:
3427 case 4:
3429 case 5:
3431 default:
3434 }
3435}
3436
3437// Checks the equality of two `FlutterKeyEvent` by each of their members except
3438// for `character`. The `character` must be checked separately.
3440 const FlutterKeyEvent& baseline) {
3441 EXPECT_EQ(subject.timestamp, baseline.timestamp);
3442 EXPECT_EQ(subject.type, baseline.type);
3443 EXPECT_EQ(subject.physical, baseline.physical);
3444 EXPECT_EQ(subject.logical, baseline.logical);
3445 EXPECT_EQ(subject.synthesized, baseline.synthesized);
3446 EXPECT_EQ(subject.device_type, baseline.device_type);
3447}
3448
3449TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) {
3450 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3451 uint64_t echoed_char;
3452 FlutterKeyEvent echoed_event;
3453 echoed_event.struct_size = sizeof(FlutterKeyEvent);
3454
3455 auto native_echo_event = [&](Dart_NativeArguments args) {
3456 echoed_event.type =
3458 Dart_GetNativeArgument(args, 0)));
3459 echoed_event.timestamp =
3460 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3461 Dart_GetNativeArgument(args, 1)));
3463 Dart_GetNativeArgument(args, 2));
3465 Dart_GetNativeArgument(args, 3));
3467 Dart_GetNativeArgument(args, 4));
3468 echoed_event.synthesized =
3469 tonic::DartConverter<bool>::FromDart(Dart_GetNativeArgument(args, 5));
3470 echoed_event.device_type =
3472 Dart_GetNativeArgument(args, 6)));
3473
3474 message_latch->Signal();
3475 };
3476
3477 auto platform_task_runner = CreateNewThread("platform_thread");
3478
3481 platform_task_runner->PostTask([&]() {
3482 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3483 EmbedderConfigBuilder builder(context);
3484 builder.SetSurface(DlISize(1, 1));
3485 builder.SetDartEntrypoint("key_data_echo");
3487 [&](const FlutterPlatformMessage* message) {
3489 engine.get(), message->response_handle, nullptr, 0);
3490 });
3491 context.AddNativeCallback(
3492 "SignalNativeTest",
3494 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3495
3496 context.AddNativeCallback("EchoKeyEvent",
3497 CREATE_NATIVE_ENTRY(native_echo_event));
3498
3499 engine = builder.LaunchEngine();
3500 ASSERT_TRUE(engine.is_valid());
3501 });
3502
3503 ready.Wait();
3504
3505 // A normal down event
3506 const FlutterKeyEvent down_event_upper_a{
3507 .struct_size = sizeof(FlutterKeyEvent),
3508 .timestamp = 1,
3510 .physical = 0x00070004,
3511 .logical = 0x00000000061,
3512 .character = "A",
3513 .synthesized = false,
3515 };
3516 platform_task_runner->PostTask([&]() {
3517 FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, nullptr,
3518 nullptr);
3519 });
3520 message_latch->Wait();
3521
3522 ExpectKeyEventEq(echoed_event, down_event_upper_a);
3523 EXPECT_EQ(echoed_char, 0x41llu);
3524
3525 // A repeat event with multi-byte character
3526 const FlutterKeyEvent repeat_event_wide_char{
3527 .struct_size = sizeof(FlutterKeyEvent),
3528 .timestamp = 1000,
3530 .physical = 0x00070005,
3531 .logical = 0x00000000062,
3532 .character = "∆",
3533 .synthesized = false,
3535 };
3536 platform_task_runner->PostTask([&]() {
3537 FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, nullptr,
3538 nullptr);
3539 });
3540 message_latch->Wait();
3541
3542 ExpectKeyEventEq(echoed_event, repeat_event_wide_char);
3543 EXPECT_EQ(echoed_char, 0x2206llu);
3544
3545 // An up event with no character, synthesized
3546 const FlutterKeyEvent up_event{
3547 .struct_size = sizeof(FlutterKeyEvent),
3548 .timestamp = 1000000,
3550 .physical = 0x00070006,
3551 .logical = 0x00000000063,
3552 .character = nullptr,
3553 .synthesized = true,
3555 };
3556 platform_task_runner->PostTask([&]() {
3557 FlutterEngineSendKeyEvent(engine.get(), &up_event, nullptr, nullptr);
3558 });
3559 message_latch->Wait();
3560
3561 ExpectKeyEventEq(echoed_event, up_event);
3562 EXPECT_EQ(echoed_char, 0llu);
3563
3564 fml::AutoResetWaitableEvent shutdown_latch;
3565 platform_task_runner->PostTask([&]() {
3566 engine.reset();
3567 shutdown_latch.Signal();
3568 });
3569 shutdown_latch.Wait();
3570}
3571
3572TEST_F(EmbedderTest, KeyDataAreBuffered) {
3573 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3574 std::vector<FlutterKeyEvent> echoed_events;
3575
3576 auto native_echo_event = [&](Dart_NativeArguments args) {
3577 echoed_events.push_back(FlutterKeyEvent{
3578 .timestamp =
3579 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3580 Dart_GetNativeArgument(args, 1))),
3581 .type =
3583 Dart_GetNativeArgument(args, 0))),
3585 Dart_GetNativeArgument(args, 2)),
3587 Dart_GetNativeArgument(args, 3)),
3589 Dart_GetNativeArgument(args, 5)),
3590 .device_type = UnserializeKeyEventDeviceType(
3592 Dart_GetNativeArgument(args, 6))),
3593 });
3594
3595 message_latch->Signal();
3596 };
3597
3598 auto platform_task_runner = CreateNewThread("platform_thread");
3599
3602 platform_task_runner->PostTask([&]() {
3603 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3604 EmbedderConfigBuilder builder(context);
3605 builder.SetSurface(DlISize(1, 1));
3606 builder.SetDartEntrypoint("key_data_late_echo");
3608 [&](const FlutterPlatformMessage* message) {
3610 engine.get(), message->response_handle, nullptr, 0);
3611 });
3612 context.AddNativeCallback(
3613 "SignalNativeTest",
3615 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3616
3617 context.AddNativeCallback("EchoKeyEvent",
3618 CREATE_NATIVE_ENTRY(native_echo_event));
3619
3620 engine = builder.LaunchEngine();
3621 ASSERT_TRUE(engine.is_valid());
3622 });
3623 ready.Wait();
3624
3625 FlutterKeyEvent sample_event{
3626 .struct_size = sizeof(FlutterKeyEvent),
3628 .physical = 0x00070004,
3629 .logical = 0x00000000061,
3630 .character = "A",
3631 .synthesized = false,
3633 };
3634
3635 // Send an event.
3636 sample_event.timestamp = 1.0l;
3637 platform_task_runner->PostTask([&]() {
3638 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3639 message_latch->Signal();
3640 });
3641 message_latch->Wait();
3642
3643 // Should not receive echos because the callback is not set yet.
3644 EXPECT_EQ(echoed_events.size(), 0u);
3645
3646 // Send an empty message to 'test/starts_echo' to start echoing.
3647 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
3649 engine.get(), [](const uint8_t* data, size_t size, void* user_data) {},
3650 nullptr, &response_handle);
3651
3654 .channel = "test/starts_echo",
3655 .message = nullptr,
3656 .message_size = 0,
3657 .response_handle = response_handle,
3658 };
3659
3660 platform_task_runner->PostTask([&]() {
3661 FlutterEngineResult result =
3663 ASSERT_EQ(result, kSuccess);
3664
3666 });
3667
3668 // message_latch->Wait();
3669 message_latch->Wait();
3670 // All previous events should be received now.
3671 EXPECT_EQ(echoed_events.size(), 1u);
3672
3673 // Send a second event.
3674 sample_event.timestamp = 10.0l;
3675 platform_task_runner->PostTask([&]() {
3676 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3677 });
3678 message_latch->Wait();
3679
3680 // The event should be echoed, too.
3681 EXPECT_EQ(echoed_events.size(), 2u);
3682
3683 fml::AutoResetWaitableEvent shutdown_latch;
3684 platform_task_runner->PostTask([&]() {
3685 engine.reset();
3686 shutdown_latch.Signal();
3687 });
3688 shutdown_latch.Wait();
3689}
3690
3691TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) {
3693 fml::AutoResetWaitableEvent sync_latch;
3695
3696 // One of the threads that the key data callback will be posted to is the
3697 // platform thread. So we cannot wait for assertions to complete on the
3698 // platform thread. Create a new thread to manage the engine instance and wait
3699 // for assertions on the test thread.
3700 auto platform_task_runner = CreateNewThread("platform_thread");
3701
3702 platform_task_runner->PostTask([&]() {
3703 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3704 EmbedderConfigBuilder builder(context);
3705 builder.SetSurface(DlISize(1, 1));
3706 builder.SetDartEntrypoint("key_data_echo");
3707 context.AddNativeCallback(
3708 "SignalNativeTest",
3710 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3711 context.AddNativeCallback(
3712 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3713
3714 engine = builder.LaunchEngine();
3715 ASSERT_TRUE(engine.is_valid());
3716
3717 sync_latch.Signal();
3718 });
3719 sync_latch.Wait();
3720 ready.Wait();
3721
3722 // Dispatch a single event
3723 FlutterKeyEvent event{
3724 .struct_size = sizeof(FlutterKeyEvent),
3725 .timestamp = 1000,
3727 .physical = 0x00070005,
3728 .logical = 0x00000000062,
3729 .character = nullptr,
3731 };
3732
3733 KeyEventUserData user_data1{
3734 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3735 };
3736 // Entrypoint `key_data_echo` returns `event.synthesized` as `handled`.
3737 event.synthesized = true;
3738 platform_task_runner->PostTask([&]() {
3739 // Test when the response callback is empty.
3740 // It should not cause a crash.
3741 FlutterEngineSendKeyEvent(engine.get(), &event, nullptr, nullptr);
3742
3743 // Test when the response callback is non-empty.
3744 // It should be invoked (so that the latch can be unlocked.)
3746 engine.get(), &event,
3747 [](bool handled, void* untyped_user_data) {
3748 KeyEventUserData* user_data =
3749 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3750 EXPECT_EQ(handled, true);
3751 user_data->latch->Signal();
3752 },
3753 &user_data1);
3754 });
3755 user_data1.latch->Wait();
3756 fml::AutoResetWaitableEvent shutdown_latch;
3757 platform_task_runner->PostTask([&]() {
3758 engine.reset();
3759 shutdown_latch.Signal();
3760 });
3761 shutdown_latch.Wait();
3762}
3763
3764TEST_F(EmbedderTest, BackToBackKeyEventResponsesCorrectlyInvoked) {
3766 fml::AutoResetWaitableEvent sync_latch;
3768
3769 // One of the threads that the callback will be posted to is the platform
3770 // thread. So we cannot wait for assertions to complete on the platform
3771 // thread. Create a new thread to manage the engine instance and wait for
3772 // assertions on the test thread.
3773 auto platform_task_runner = CreateNewThread("platform_thread");
3774
3775 platform_task_runner->PostTask([&]() {
3776 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3777 EmbedderConfigBuilder builder(context);
3778 builder.SetSurface(DlISize(1, 1));
3779 builder.SetDartEntrypoint("key_data_echo");
3780 context.AddNativeCallback(
3781 "SignalNativeTest",
3783 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3784
3785 context.AddNativeCallback(
3786 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3787
3788 engine = builder.LaunchEngine();
3789 ASSERT_TRUE(engine.is_valid());
3790
3791 sync_latch.Signal();
3792 });
3793 sync_latch.Wait();
3794 ready.Wait();
3795
3796 // Dispatch a single event
3797 FlutterKeyEvent event{
3798 .struct_size = sizeof(FlutterKeyEvent),
3799 .timestamp = 1000,
3801 .physical = 0x00070005,
3802 .logical = 0x00000000062,
3803 .character = nullptr,
3804 .synthesized = false,
3806 };
3807
3808 // Dispatch two events back to back, using the same callback on different
3809 // user_data
3810 KeyEventUserData user_data2{
3811 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3812 .returned = false,
3813 };
3814 KeyEventUserData user_data3{
3815 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3816 .returned = false,
3817 };
3818 auto callback23 = [](bool handled, void* untyped_user_data) {
3820 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3821 EXPECT_EQ(handled, false);
3822 user_data->returned = true;
3823 user_data->latch->Signal();
3824 };
3825 platform_task_runner->PostTask([&]() {
3826 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2);
3827 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3);
3828 });
3829 user_data2.latch->Wait();
3830 user_data3.latch->Wait();
3831
3832 EXPECT_TRUE(user_data2.returned);
3833 EXPECT_TRUE(user_data3.returned);
3834
3835 fml::AutoResetWaitableEvent shutdown_latch;
3836 platform_task_runner->PostTask([&]() {
3837 engine.reset();
3838 shutdown_latch.Signal();
3839 });
3840 shutdown_latch.Wait();
3841}
3842
3843//------------------------------------------------------------------------------
3844// Vsync waiter
3845//------------------------------------------------------------------------------
3846
3847// This test schedules a frame for the future and asserts that vsync waiter
3848// posts the event at the right frame start time (which is in the future).
3849TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) {
3851 fml::AutoResetWaitableEvent present_latch;
3852 fml::AutoResetWaitableEvent vsync_latch;
3853
3854 // One of the threads that the callback (FlutterEngineOnVsync) will be posted
3855 // to is the platform thread. So we cannot wait for assertions to complete on
3856 // the platform thread. Create a new thread to manage the engine instance and
3857 // wait for assertions on the test thread.
3858 auto platform_task_runner = CreateNewThread("platform_thread");
3859
3860 platform_task_runner->PostTask([&]() {
3861 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3862 context.SetVsyncCallback([&](intptr_t baton) {
3863 platform_task_runner->PostTask([baton = baton, &engine, &vsync_latch]() {
3864 FlutterEngineOnVsync(engine.get(), baton, NanosFromEpoch(16),
3865 NanosFromEpoch(32));
3866 vsync_latch.Signal();
3867 });
3868 });
3869 context.AddNativeCallback(
3870 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3871 present_latch.Signal();
3872 }));
3873
3874 EmbedderConfigBuilder builder(context);
3875 builder.SetSurface(DlISize(1, 1));
3876 builder.SetupVsyncCallback();
3877 builder.SetDartEntrypoint("empty_scene");
3878 engine = builder.LaunchEngine();
3879 ASSERT_TRUE(engine.is_valid());
3880
3881 // Send a window metrics events so frames may be scheduled.
3882 FlutterWindowMetricsEvent event = {};
3883 event.struct_size = sizeof(event);
3884 event.width = 800;
3885 event.height = 600;
3886 event.pixel_ratio = 1.0;
3887
3888 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3889 kSuccess);
3890 });
3891
3892 vsync_latch.Wait();
3893 present_latch.Wait();
3894
3895 fml::AutoResetWaitableEvent shutdown_latch;
3896 platform_task_runner->PostTask([&]() {
3897 engine.reset();
3898 shutdown_latch.Signal();
3899 });
3900 shutdown_latch.Wait();
3901}
3902
3903TEST_F(EmbedderTest, CanScheduleFrame) {
3904 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3905 EmbedderConfigBuilder builder(context);
3906 builder.SetSurface(DlISize(1, 1));
3907 builder.SetDartEntrypoint("can_schedule_frame");
3909 context.AddNativeCallback(
3910 "SignalNativeTest",
3912 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
3913
3914 fml::AutoResetWaitableEvent check_latch;
3915 context.AddNativeCallback(
3916 "SignalNativeCount",
3918 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
3919
3920 auto engine = builder.LaunchEngine();
3921 ASSERT_TRUE(engine.is_valid());
3922
3923 // Wait for the application to attach the listener.
3924 latch.Wait();
3925
3926 ASSERT_EQ(FlutterEngineScheduleFrame(engine.get()), kSuccess);
3927
3928 check_latch.Wait();
3929}
3930
3931TEST_F(EmbedderTest, CanSetNextFrameCallback) {
3932 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
3933 EmbedderConfigBuilder builder(context);
3934 builder.SetSurface(DlISize(1, 1));
3935 builder.SetDartEntrypoint("draw_solid_red");
3936
3937 auto engine = builder.LaunchEngine();
3938 ASSERT_TRUE(engine.is_valid());
3939
3940 // Register the callback that is executed once the next frame is drawn.
3941 fml::AutoResetWaitableEvent callback_latch;
3942 VoidCallback callback = [](void* user_data) {
3943 fml::AutoResetWaitableEvent* callback_latch =
3945
3946 callback_latch->Signal();
3947 };
3948
3950 &callback_latch);
3951 ASSERT_EQ(result, kSuccess);
3952
3953 // Send a window metrics events so frames may be scheduled.
3954 FlutterWindowMetricsEvent event = {};
3955 event.struct_size = sizeof(event);
3956 event.width = 800;
3957 event.height = 600;
3958 event.pixel_ratio = 1.0;
3959 event.physical_view_inset_top = 0.0;
3960 event.physical_view_inset_right = 0.0;
3961 event.physical_view_inset_bottom = 0.0;
3962 event.physical_view_inset_left = 0.0;
3963 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3964 kSuccess);
3965
3966 callback_latch.Wait();
3967}
3968
3969#if defined(FML_OS_MACOSX)
3970
3971static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
3972 pthread_t tid = pthread_self();
3973 struct sched_param param;
3974 int policy = SCHED_OTHER;
3975 switch (config.priority) {
3977 param.sched_priority = 10;
3978 break;
3979 default:
3980 param.sched_priority = 1;
3981 }
3982 pthread_setschedparam(tid, policy, &param);
3983}
3984
3985TEST_F(EmbedderTest, EmbedderThreadHostUseCustomThreadConfig) {
3986 auto thread_host =
3988 nullptr, MockThreadConfigSetter);
3989
3991 int ui_policy;
3992 struct sched_param ui_param;
3993
3994 thread_host->GetTaskRunners().GetUITaskRunner()->PostTask([&] {
3995 pthread_t current_thread = pthread_self();
3996 pthread_getschedparam(current_thread, &ui_policy, &ui_param);
3997 ASSERT_EQ(ui_param.sched_priority, 10);
3998 ui_latch.Signal();
3999 });
4000
4002 int io_policy;
4003 struct sched_param io_param;
4004 thread_host->GetTaskRunners().GetIOTaskRunner()->PostTask([&] {
4005 pthread_t current_thread = pthread_self();
4006 pthread_getschedparam(current_thread, &io_policy, &io_param);
4007 ASSERT_EQ(io_param.sched_priority, 1);
4008 io_latch.Signal();
4009 });
4010
4011 ui_latch.Wait();
4012 io_latch.Wait();
4013}
4014#endif
4015
4016/// Send a pointer event to Dart and wait until the Dart code signals
4017/// it received the event.
4018TEST_F(EmbedderTest, CanSendPointer) {
4019 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4020 EmbedderConfigBuilder builder(context);
4021 builder.SetSurface(DlISize(1, 1));
4022 builder.SetDartEntrypoint("pointer_data_packet");
4023
4024 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
4025 context.AddNativeCallback(
4026 "SignalNativeTest",
4028 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4029 context.AddNativeCallback(
4030 "SignalNativeCount",
4031 CREATE_NATIVE_ENTRY([&count_latch](Dart_NativeArguments args) {
4033 Dart_GetNativeArgument(args, 0));
4034 ASSERT_EQ(count, 1);
4035 count_latch.Signal();
4036 }));
4037 context.AddNativeCallback(
4038 "SignalNativeMessage",
4039 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4041 Dart_GetNativeArgument(args, 0));
4042 ASSERT_EQ("PointerData(viewId: 0, x: 123.0, y: 456.0)", message);
4043 message_latch.Signal();
4044 }));
4045
4046 auto engine = builder.LaunchEngine();
4047 ASSERT_TRUE(engine.is_valid());
4048
4049 ready_latch.Wait();
4050
4051 FlutterPointerEvent pointer_event = {};
4052 pointer_event.struct_size = sizeof(FlutterPointerEvent);
4053 pointer_event.phase = FlutterPointerPhase::kAdd;
4054 pointer_event.x = 123;
4055 pointer_event.y = 456;
4056 pointer_event.timestamp = static_cast<size_t>(1234567890);
4057 pointer_event.view_id = 0;
4058
4059 FlutterEngineResult result =
4060 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
4061 ASSERT_EQ(result, kSuccess);
4062
4063 count_latch.Wait();
4064 message_latch.Wait();
4065}
4066
4067/// Send a pointer event to Dart and wait until the Dart code echos with the
4068/// view ID.
4069TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
4070 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4071 EmbedderConfigBuilder builder(context);
4072 builder.SetSurface(DlISize(1, 1));
4073 builder.SetDartEntrypoint("pointer_data_packet_view_id");
4074
4075 fml::AutoResetWaitableEvent ready_latch, add_view_latch, message_latch;
4076 context.AddNativeCallback(
4077 "SignalNativeTest",
4079 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4080 context.AddNativeCallback(
4081 "SignalNativeMessage",
4082 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4084 Dart_GetNativeArgument(args, 0));
4085 ASSERT_EQ("ViewID: 2", message);
4086 message_latch.Signal();
4087 }));
4088
4089 auto engine = builder.LaunchEngine();
4090 ASSERT_TRUE(engine.is_valid());
4091 ready_latch.Wait();
4092
4093 // Add view 2
4094 FlutterWindowMetricsEvent metrics = {};
4095 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
4096 metrics.width = 800;
4097 metrics.height = 600;
4098 metrics.pixel_ratio = 1.0;
4099 metrics.view_id = 2;
4100
4101 FlutterAddViewInfo info = {};
4102 info.struct_size = sizeof(FlutterAddViewInfo);
4103 info.view_id = 2;
4104 info.view_metrics = &metrics;
4105 info.add_view_callback = [](const FlutterAddViewResult* result) {
4106 EXPECT_TRUE(result->added);
4107 fml::AutoResetWaitableEvent* add_view_latch =
4108 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
4109 add_view_latch->Signal();
4110 };
4111 info.user_data = &add_view_latch;
4112 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
4113 add_view_latch.Wait();
4114
4115 // Send a pointer event for view 2
4116 FlutterPointerEvent pointer_event = {};
4117 pointer_event.struct_size = sizeof(FlutterPointerEvent);
4118 pointer_event.phase = FlutterPointerPhase::kAdd;
4119 pointer_event.x = 123;
4120 pointer_event.y = 456;
4121 pointer_event.timestamp = static_cast<size_t>(1234567890);
4122 pointer_event.view_id = 2;
4123
4124 FlutterEngineResult result =
4125 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
4126 ASSERT_EQ(result, kSuccess);
4127
4128 message_latch.Wait();
4129}
4130
4131TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
4132 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4133 EmbedderConfigBuilder builder(context);
4134 builder.SetSurface(DlISize(1, 1));
4135 builder.SetDartEntrypoint("window_metrics_event_view_id");
4136
4137 fml::AutoResetWaitableEvent ready_latch, message_latch;
4138 context.AddNativeCallback(
4139 "SignalNativeTest",
4141 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4142 context.AddNativeCallback(
4143 "SignalNativeMessage",
4144 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4146 Dart_GetNativeArgument(args, 0));
4147 ASSERT_EQ("Changed: [0]", message);
4148 message_latch.Signal();
4149 }));
4150
4151 auto engine = builder.LaunchEngine();
4152 ASSERT_TRUE(engine.is_valid());
4153
4154 ready_latch.Wait();
4155
4156 FlutterWindowMetricsEvent event = {};
4157 // Simulate an event that comes from an old version of embedder.h that doesn't
4158 // have the view_id field.
4160 event.width = 200;
4161 event.height = 300;
4162 event.pixel_ratio = 1.5;
4163 // Skip assigning event.view_id here to test the default behavior.
4164
4165 FlutterEngineResult result =
4167 ASSERT_EQ(result, kSuccess);
4168
4169 message_latch.Wait();
4170}
4171
4172TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
4173 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4174 EmbedderConfigBuilder builder(context);
4175 builder.SetSurface(DlISize(1, 1));
4176 builder.SetDartEntrypoint("window_metrics_event_view_id");
4177
4178 fml::AutoResetWaitableEvent ready_latch, message_latch;
4179 context.AddNativeCallback(
4180 "SignalNativeTest",
4182 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
4183
4184 context.AddNativeCallback(
4185 "SignalNativeMessage",
4186 CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
4188 Dart_GetNativeArgument(args, 0));
4189 // Message latch should only be signaled once as the bad
4190 // view metric should be dropped by the engine.
4191 ASSERT_FALSE(message_latch.IsSignaledForTest());
4192 ASSERT_EQ("Changed: [0]", message);
4193 message_latch.Signal();
4194 }));
4195
4196 auto engine = builder.LaunchEngine();
4197 ASSERT_TRUE(engine.is_valid());
4198
4199 ready_latch.Wait();
4200
4201 // Send a window metric for a nonexistent view, which should be dropped by the
4202 // engine.
4203 FlutterWindowMetricsEvent bad_event = {};
4204 bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
4205 bad_event.width = 200;
4206 bad_event.height = 300;
4207 bad_event.pixel_ratio = 1.5;
4208 bad_event.view_id = 100;
4209
4210 FlutterEngineResult result =
4212 ASSERT_EQ(result, kSuccess);
4213
4214 // Send a window metric for a valid view. The engine notifies the Dart app.
4215 FlutterWindowMetricsEvent event = {};
4216 event.struct_size = sizeof(FlutterWindowMetricsEvent);
4217 event.width = 200;
4218 event.height = 300;
4219 event.pixel_ratio = 1.5;
4220 event.view_id = 0;
4221
4222 result = FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
4223 ASSERT_EQ(result, kSuccess);
4224
4225 message_latch.Wait();
4226}
4227
4228TEST_F(EmbedderTest, RegisterChannelListener) {
4229 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4230
4233 bool listening = false;
4234 context.AddNativeCallback(
4235 "SignalNativeTest",
4236 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { latch.Signal(); }));
4237 context.SetChannelUpdateCallback([&](const FlutterChannelUpdate* update) {
4238 EXPECT_STREQ(update->channel, "test/listen");
4239 EXPECT_TRUE(update->listening);
4240 listening = true;
4241 latch2.Signal();
4242 });
4243
4244 EmbedderConfigBuilder builder(context);
4245 builder.SetSurface(DlISize(1, 1));
4246 builder.SetDartEntrypoint("channel_listener_response");
4247
4248 auto engine = builder.LaunchEngine();
4249 ASSERT_TRUE(engine.is_valid());
4250
4251 latch.Wait();
4252 // Drain tasks posted to platform thread task runner.
4254 latch2.Wait();
4255
4256 ASSERT_TRUE(listening);
4257}
4258
4259TEST_F(EmbedderTest, PlatformThreadIsolatesWithCustomPlatformTaskRunner) {
4260 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
4261 static fml::AutoResetWaitableEvent latch;
4262
4263 static std::thread::id ffi_call_thread_id;
4264 static void (*ffi_signal_native_test)() = []() -> void {
4265 ffi_call_thread_id = std::this_thread::get_id();
4266 latch.Signal();
4267 };
4268
4269 Dart_FfiNativeResolver ffi_resolver = [](const char* name,
4270 uintptr_t args_n) -> void* {
4271 if (std::string_view(name) == "FFISignalNativeTest") {
4272 return reinterpret_cast<void*>(ffi_signal_native_test);
4273 }
4274 return nullptr;
4275 };
4276
4277 // The test's Dart code will call this native function which overrides the
4278 // FFI resolver. After that, the Dart code will invoke the FFI function
4279 // using runOnPlatformThread.
4280 context.AddNativeCallback(
4281 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4282 Dart_SetFfiNativeResolver(Dart_RootLibrary(), ffi_resolver);
4283 }));
4284
4285 auto platform_task_runner = CreateNewThread("test_platform_thread");
4286
4288
4289 EmbedderTestTaskRunner test_task_runner(
4290 platform_task_runner, [&](FlutterTask task) {
4291 if (!engine.is_valid()) {
4292 return;
4293 }
4294 FlutterEngineRunTask(engine.get(), &task);
4295 });
4296
4297 std::thread::id platform_thread_id;
4298 platform_task_runner->PostTask([&]() {
4299 platform_thread_id = std::this_thread::get_id();
4300
4301 EmbedderConfigBuilder builder(context);
4302 const auto task_runner_description =
4303 test_task_runner.GetFlutterTaskRunnerDescription();
4304 builder.SetSurface(DlISize(1, 1));
4305 builder.SetPlatformTaskRunner(&task_runner_description);
4306 builder.SetDartEntrypoint("invokePlatformThreadIsolate");
4307 builder.AddCommandLineArgument("--enable-platform-isolates");
4308 engine = builder.LaunchEngine();
4309 ASSERT_TRUE(engine.is_valid());
4310 });
4311
4312 latch.Wait();
4313
4314 fml::AutoResetWaitableEvent kill_latch;
4315 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
4316 engine.reset();
4317
4318 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
4319 }));
4320 kill_latch.Wait();
4321
4322 // Check that the FFI call was executed on the platform thread.
4323 ASSERT_EQ(platform_thread_id, ffi_call_thread_id);
4324}
4325
4326} // namespace testing
4327} // namespace flutter
4328
4329// 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:3338
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:3254
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:3390
FlutterEngineResult FlutterEngineSendViewFocusEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterViewFocusEvent *event)
Notifies the engine that platform view focus state has changed.
Definition embedder.cc:2628
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Schedule a new frame to redraw the content.
Definition embedder.cc:3679
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2685
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:3334
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:3691
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:2661
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:3599
FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterAddViewInfo *info)
Adds a view.
Definition embedder.cc:2525
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:3053
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data)
Collects the AOT data.
Definition embedder.cc:1760
FlutterEngineResult FlutterEngineSendPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *flutter_message)
Definition embedder.cc:3002
bool FlutterEngineRunsAOTCompiledDartCode(void)
Returns if the Flutter engine instance will run AOT compiled Dart code. This call has no threading re...
Definition embedder.cc:3449
FlutterEngineResult FlutterEngineReloadSystemFonts(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Reloads the system fonts in engine.
Definition embedder.cc:3281
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2794
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition embedder.cc:2486
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:3577
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:2946
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:3103
FLUTTER_EXPORT FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterRemoveViewInfo *info)
Removes a view.
Definition embedder.cc:2586
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterPlatformMessageResponseHandle *response)
Collects the handle created using FlutterPlatformMessageCreateResponseHandle.
Definition embedder.cc:3088
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:1706
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)