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