Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
embedder_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#define FML_USED_ON_EMBEDDER
6
7#include <string>
8#include <utility>
9#include <vector>
10
11#include "embedder.h"
12#include "embedder_engine.h"
13#include "flutter/common/constants.h"
14#include "flutter/flow/raster_cache.h"
15#include "flutter/fml/file.h"
16#include "flutter/fml/make_copyable.h"
17#include "flutter/fml/mapping.h"
18#include "flutter/fml/message_loop.h"
19#include "flutter/fml/paths.h"
20#include "flutter/fml/synchronization/count_down_latch.h"
21#include "flutter/fml/synchronization/waitable_event.h"
22#include "flutter/fml/task_runner.h"
23#include "flutter/fml/thread.h"
24#include "flutter/fml/time/time_delta.h"
25#include "flutter/fml/time/time_point.h"
26#include "flutter/runtime/dart_vm.h"
27#include "flutter/shell/platform/embedder/tests/embedder_assertions.h"
28#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
29#include "flutter/shell/platform/embedder/tests/embedder_test.h"
30#include "flutter/shell/platform/embedder/tests/embedder_unittests_util.h"
31#include "flutter/testing/assertions_skia.h"
32#include "flutter/testing/testing.h"
35
36#if defined(FML_OS_MACOSX)
37#include <pthread.h>
38#endif
39
40// CREATE_NATIVE_ENTRY is leaky by design
41// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
42
43namespace {
44
45static uint64_t NanosFromEpoch(int millis_from_now) {
46 const auto now = fml::TimePoint::Now();
47 const auto delta = fml::TimeDelta::FromMilliseconds(millis_from_now);
48 return (now + delta).ToEpochDelta().ToNanoseconds();
49}
50
51} // namespace
52
53namespace flutter {
54namespace testing {
55
56using EmbedderTest = testing::EmbedderTest;
57
58TEST(EmbedderTestNoFixture, MustNotRunWithInvalidArgs) {
62 auto engine = builder.LaunchEngine();
63 ASSERT_FALSE(engine.is_valid());
64}
65
66TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
67 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
69 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
70 EmbedderConfigBuilder builder(context);
71 builder.SetSoftwareRendererConfig();
72 auto engine = builder.LaunchEngine();
73 ASSERT_TRUE(engine.is_valid());
74 // Wait for the root isolate to launch.
75 latch.Wait();
76 engine.reset();
77}
78
79// TODO(41999): Disabled because flaky.
80TEST_F(EmbedderTest, DISABLED_CanLaunchAndShutdownMultipleTimes) {
83 builder.SetSoftwareRendererConfig();
84 for (size_t i = 0; i < 3; ++i) {
85 auto engine = builder.LaunchEngine();
86 ASSERT_TRUE(engine.is_valid());
87 FML_LOG(INFO) << "Engine launch count: " << i + 1;
88 }
89}
90
91TEST_F(EmbedderTest, CanInvokeCustomEntrypoint) {
92 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
93 static fml::AutoResetWaitableEvent latch;
95 latch.Signal();
96 };
97 context.AddNativeCallback("SayHiFromCustomEntrypoint", entrypoint);
98 EmbedderConfigBuilder builder(context);
99 builder.SetSoftwareRendererConfig();
100 builder.SetDartEntrypoint("customEntrypoint");
101 auto engine = builder.LaunchEngine();
102 latch.Wait();
103 ASSERT_TRUE(engine.is_valid());
104}
105
106TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
107 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
108
112
113 // Can be defined separately.
114 auto entry1 = [&latch1](Dart_NativeArguments args) {
115 FML_LOG(INFO) << "In Callback 1";
116 latch1.Signal();
117 };
118 auto native_entry1 = CREATE_NATIVE_ENTRY(entry1);
119 context.AddNativeCallback("SayHiFromCustomEntrypoint1", native_entry1);
120
121 // Can be wrapped in the args.
122 auto entry2 = [&latch2](Dart_NativeArguments args) {
123 FML_LOG(INFO) << "In Callback 2";
124 latch2.Signal();
125 };
126 context.AddNativeCallback("SayHiFromCustomEntrypoint2",
127 CREATE_NATIVE_ENTRY(entry2));
128
129 // Everything can be inline.
130 context.AddNativeCallback(
131 "SayHiFromCustomEntrypoint3",
133 FML_LOG(INFO) << "In Callback 3";
134 latch3.Signal();
135 }));
136
137 EmbedderConfigBuilder builder(context);
138 builder.SetSoftwareRendererConfig();
139 builder.SetDartEntrypoint("customEntrypoint1");
140 auto engine = builder.LaunchEngine();
141 latch1.Wait();
142 latch2.Wait();
143 latch3.Wait();
144 ASSERT_TRUE(engine.is_valid());
145}
146
147TEST_F(EmbedderTest, CanTerminateCleanly) {
148 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
149 EmbedderConfigBuilder builder(context);
150 builder.SetSoftwareRendererConfig();
151 builder.SetDartEntrypoint("terminateExitCodeHandler");
152 auto engine = builder.LaunchEngine();
153 ASSERT_TRUE(engine.is_valid());
154}
155
156TEST_F(EmbedderTest, ExecutableNameNotNull) {
157 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
158
159 // Supply a callback to Dart for the test fixture to pass Platform.executable
160 // back to us.
162 context.AddNativeCallback(
163 "NotifyStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
164 const auto dart_string = tonic::DartConverter<std::string>::FromDart(
166 EXPECT_EQ("/path/to/binary", dart_string);
167 latch.Signal();
168 }));
169
170 EmbedderConfigBuilder builder(context);
171 builder.SetSoftwareRendererConfig();
172 builder.SetDartEntrypoint("executableNameNotNull");
173 builder.SetExecutableName("/path/to/binary");
174 auto engine = builder.LaunchEngine();
175 latch.Wait();
176}
177
178TEST_F(EmbedderTest, ImplicitViewNotNull) {
179 // TODO(loicsharma): Update this test when embedders can opt-out
180 // of the implicit view.
181 // See: https://github.com/flutter/flutter/issues/120306
182 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
183
184 bool implicitViewNotNull = false;
186 context.AddNativeCallback(
187 "NotifyBoolValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
188 implicitViewNotNull = tonic::DartConverter<bool>::FromDart(
190 latch.Signal();
191 }));
192
193 EmbedderConfigBuilder builder(context);
194 builder.SetSoftwareRendererConfig();
195 builder.SetDartEntrypoint("implicitViewNotNull");
196 auto engine = builder.LaunchEngine();
197 latch.Wait();
198
199 EXPECT_TRUE(implicitViewNotNull);
200}
201
202std::atomic_size_t EmbedderTestTaskRunner::sEmbedderTaskRunnerIdentifiers = {};
203
204TEST_F(EmbedderTest, CanSpecifyCustomPlatformTaskRunner) {
205 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
207
208 // Run the test on its own thread with a message loop so that it can safely
209 // pump its event loop while we wait for all the conditions to be checked.
210 auto platform_task_runner = CreateNewThread("test_platform_thread");
211 static std::mutex engine_mutex;
212 static bool signaled_once = false;
214
215 EmbedderTestTaskRunner test_task_runner(
216 platform_task_runner, [&](FlutterTask task) {
217 std::scoped_lock lock(engine_mutex);
218 if (!engine.is_valid()) {
219 return;
220 }
221 // There may be multiple tasks posted but we only need to check
222 // assertions once.
223 if (signaled_once) {
224 FlutterEngineRunTask(engine.get(), &task);
225 return;
226 }
227
228 signaled_once = true;
229 ASSERT_TRUE(engine.is_valid());
230 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
231 latch.Signal();
232 });
233
234 platform_task_runner->PostTask([&]() {
235 EmbedderConfigBuilder builder(context);
236 const auto task_runner_description =
237 test_task_runner.GetFlutterTaskRunnerDescription();
238 builder.SetSoftwareRendererConfig();
239 builder.SetPlatformTaskRunner(&task_runner_description);
240 builder.SetDartEntrypoint("invokePlatformTaskRunner");
241 std::scoped_lock lock(engine_mutex);
242 engine = builder.LaunchEngine();
243 ASSERT_TRUE(engine.is_valid());
244 });
245
246 // Signaled when all the assertions are checked.
247 latch.Wait();
248 ASSERT_TRUE(engine.is_valid());
249
250 // Since the engine was started on its own thread, it must be killed there as
251 // well.
253 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
254 std::scoped_lock lock(engine_mutex);
255 engine.reset();
256
257 // There may still be pending tasks on the platform thread that were queued
258 // by the test_task_runner. Signal the latch after these tasks have been
259 // consumed.
260 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
261 }));
262 kill_latch.Wait();
263
264 ASSERT_TRUE(signaled_once);
265 signaled_once = false;
266}
267
268TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
269 auto point1 = fml::TimePoint::FromEpochDelta(
271 auto point2 = fml::TimePoint::Now();
272
273 ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1));
274}
275
276TEST_F(EmbedderTest, CanReloadSystemFonts) {
277 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
278 EmbedderConfigBuilder builder(context);
279 builder.SetSoftwareRendererConfig();
280 auto engine = builder.LaunchEngine();
281 ASSERT_TRUE(engine.is_valid());
282
284 ASSERT_EQ(result, kSuccess);
285}
286
287TEST_F(EmbedderTest, IsolateServiceIdSent) {
288 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
290
291 fml::Thread thread;
293 std::string isolate_message;
294
295 thread.GetTaskRunner()->PostTask([&]() {
296 EmbedderConfigBuilder builder(context);
297 builder.SetSoftwareRendererConfig();
298 builder.SetDartEntrypoint("main");
299 builder.SetPlatformMessageCallback(
300 [&](const FlutterPlatformMessage* message) {
301 if (strcmp(message->channel, "flutter/isolate") == 0) {
302 isolate_message = {reinterpret_cast<const char*>(message->message),
303 message->message_size};
304 latch.Signal();
305 }
306 });
307 engine = builder.LaunchEngine();
308 ASSERT_TRUE(engine.is_valid());
309 });
310
311 // Wait for the isolate ID message and check its format.
312 latch.Wait();
313 ASSERT_EQ(isolate_message.find("isolates/"), 0ul);
314
315 // Since the engine was started on its own thread, it must be killed there as
316 // well.
318 thread.GetTaskRunner()->PostTask(
319 fml::MakeCopyable([&engine, &kill_latch]() mutable {
320 engine.reset();
321 kill_latch.Signal();
322 }));
323 kill_latch.Wait();
324}
325
326//------------------------------------------------------------------------------
327/// Creates a platform message response callbacks, does NOT send them, and
328/// immediately collects the same.
329///
330TEST_F(EmbedderTest, CanCreateAndCollectCallbacks) {
331 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
332 EmbedderConfigBuilder builder(context);
333 builder.SetSoftwareRendererConfig();
334 builder.SetDartEntrypoint("platform_messages_response");
335 context.AddNativeCallback(
336 "SignalNativeTest",
338
339 auto engine = builder.LaunchEngine();
340 ASSERT_TRUE(engine.is_valid());
341
342 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
343 auto callback = [](const uint8_t* data, size_t size,
344 void* user_data) -> void {};
346 engine.get(), callback, nullptr, &response_handle);
347 ASSERT_EQ(result, kSuccess);
348 ASSERT_NE(response_handle, nullptr);
349
351 response_handle);
352 ASSERT_EQ(result, kSuccess);
353}
354
355//------------------------------------------------------------------------------
356/// Sends platform messages to Dart code than simply echoes the contents of the
357/// message back to the embedder. The embedder registers a native callback to
358/// intercept that message.
359///
360TEST_F(EmbedderTest, PlatformMessagesCanReceiveResponse) {
361 struct Captures {
363 std::thread::id thread_id;
364 };
365 Captures captures;
366
367 CreateNewThread()->PostTask([&]() {
368 captures.thread_id = std::this_thread::get_id();
369 auto& context =
370 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
371 EmbedderConfigBuilder builder(context);
372 builder.SetSoftwareRendererConfig();
373 builder.SetDartEntrypoint("platform_messages_response");
374
376 context.AddNativeCallback(
377 "SignalNativeTest",
379 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
380
381 auto engine = builder.LaunchEngine();
382 ASSERT_TRUE(engine.is_valid());
383
384 static std::string kMessageData = "Hello from embedder.";
385
386 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
387 auto callback = [](const uint8_t* data, size_t size,
388 void* user_data) -> void {
389 ASSERT_EQ(size, kMessageData.size());
390 ASSERT_EQ(strncmp(reinterpret_cast<const char*>(kMessageData.data()),
391 reinterpret_cast<const char*>(data), size),
392 0);
393 auto captures = reinterpret_cast<Captures*>(user_data);
394 ASSERT_EQ(captures->thread_id, std::this_thread::get_id());
395 captures->latch.Signal();
396 };
398 engine.get(), callback, &captures, &response_handle);
399 ASSERT_EQ(result, kSuccess);
400
403 message.channel = "test_channel";
404 message.message = reinterpret_cast<const uint8_t*>(kMessageData.data());
405 message.message_size = kMessageData.size();
406 message.response_handle = response_handle;
407
408 ready.Wait();
410 ASSERT_EQ(result, kSuccess);
411
413 response_handle);
414 ASSERT_EQ(result, kSuccess);
415 });
416
417 captures.latch.Wait();
418}
419
420//------------------------------------------------------------------------------
421/// Tests that a platform message can be sent with no response handle. Instead
422/// of the platform message integrity checked via a response handle, a native
423/// callback with the response is invoked to assert integrity.
424///
425TEST_F(EmbedderTest, PlatformMessagesCanBeSentWithoutResponseHandles) {
426 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
427 EmbedderConfigBuilder builder(context);
428 builder.SetSoftwareRendererConfig();
429 builder.SetDartEntrypoint("platform_messages_no_response");
430
431 const std::string message_data = "Hello but don't call me back.";
432
434 context.AddNativeCallback(
435 "SignalNativeTest",
437 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
438 context.AddNativeCallback(
439 "SignalNativeMessage",
441 ([&message, &message_data](Dart_NativeArguments args) {
442 auto received_message = tonic::DartConverter<std::string>::FromDart(
444 ASSERT_EQ(received_message, message_data);
445 message.Signal();
446 })));
447
448 auto engine = builder.LaunchEngine();
449
450 ASSERT_TRUE(engine.is_valid());
451 ready.Wait();
452
453 FlutterPlatformMessage platform_message = {};
454 platform_message.struct_size = sizeof(FlutterPlatformMessage);
455 platform_message.channel = "test_channel";
456 platform_message.message =
457 reinterpret_cast<const uint8_t*>(message_data.data());
458 platform_message.message_size = message_data.size();
459 platform_message.response_handle = nullptr; // No response needed.
460
461 auto result =
462 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
463 ASSERT_EQ(result, kSuccess);
464 message.Wait();
465}
466
467//------------------------------------------------------------------------------
468/// Tests that a null platform message can be sent.
469///
470TEST_F(EmbedderTest, NullPlatformMessagesCanBeSent) {
471 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
472 EmbedderConfigBuilder builder(context);
473 builder.SetSoftwareRendererConfig();
474 builder.SetDartEntrypoint("null_platform_messages");
475
477 context.AddNativeCallback(
478 "SignalNativeTest",
480 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
481 context.AddNativeCallback(
482 "SignalNativeMessage",
484 auto received_message = tonic::DartConverter<std::string>::FromDart(
486 ASSERT_EQ("true", received_message);
487 message.Signal();
488 })));
489
490 auto engine = builder.LaunchEngine();
491
492 ASSERT_TRUE(engine.is_valid());
493 ready.Wait();
494
495 FlutterPlatformMessage platform_message = {};
496 platform_message.struct_size = sizeof(FlutterPlatformMessage);
497 platform_message.channel = "test_channel";
498 platform_message.message = nullptr;
499 platform_message.message_size = 0;
500 platform_message.response_handle = nullptr; // No response needed.
501
502 auto result =
503 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
504 ASSERT_EQ(result, kSuccess);
505 message.Wait();
506}
507
508//------------------------------------------------------------------------------
509/// Tests that a null platform message cannot be send if the message_size
510/// isn't equals to 0.
511///
512TEST_F(EmbedderTest, InvalidPlatformMessages) {
513 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
514 EmbedderConfigBuilder builder(context);
515 builder.SetSoftwareRendererConfig();
516 auto engine = builder.LaunchEngine();
517
518 ASSERT_TRUE(engine.is_valid());
519
520 FlutterPlatformMessage platform_message = {};
521 platform_message.struct_size = sizeof(FlutterPlatformMessage);
522 platform_message.channel = "test_channel";
523 platform_message.message = nullptr;
524 platform_message.message_size = 1;
525 platform_message.response_handle = nullptr; // No response needed.
526
527 auto result =
528 FlutterEngineSendPlatformMessage(engine.get(), &platform_message);
529 ASSERT_EQ(result, kInvalidArguments);
530}
531
532//------------------------------------------------------------------------------
533/// Tests that setting a custom log callback works as expected and defaults to
534/// using tag "flutter".
535TEST_F(EmbedderTest, CanSetCustomLogMessageCallback) {
536 fml::AutoResetWaitableEvent callback_latch;
537 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
538 EmbedderConfigBuilder builder(context);
539 builder.SetDartEntrypoint("custom_logger");
540 builder.SetSoftwareRendererConfig();
541 context.SetLogMessageCallback(
542 [&callback_latch](const char* tag, const char* message) {
543 EXPECT_EQ(std::string(tag), "flutter");
544 EXPECT_EQ(std::string(message), "hello world");
545 callback_latch.Signal();
546 });
547 auto engine = builder.LaunchEngine();
548 ASSERT_TRUE(engine.is_valid());
549 callback_latch.Wait();
550}
551
552//------------------------------------------------------------------------------
553/// Tests that setting a custom log tag works.
554TEST_F(EmbedderTest, CanSetCustomLogTag) {
555 fml::AutoResetWaitableEvent callback_latch;
556 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
557 EmbedderConfigBuilder builder(context);
558 builder.SetDartEntrypoint("custom_logger");
559 builder.SetSoftwareRendererConfig();
560 builder.SetLogTag("butterfly");
561 context.SetLogMessageCallback(
562 [&callback_latch](const char* tag, const char* message) {
563 EXPECT_EQ(std::string(tag), "butterfly");
564 EXPECT_EQ(std::string(message), "hello world");
565 callback_latch.Signal();
566 });
567 auto engine = builder.LaunchEngine();
568 ASSERT_TRUE(engine.is_valid());
569 callback_latch.Wait();
570}
571
572//------------------------------------------------------------------------------
573/// Asserts behavior of FlutterProjectArgs::shutdown_dart_vm_when_done (which is
574/// set to true by default in these unit-tests).
575///
576TEST_F(EmbedderTest, VMShutsDownWhenNoEnginesInProcess) {
577 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
578 EmbedderConfigBuilder builder(context);
579 builder.SetSoftwareRendererConfig();
580 const auto launch_count = DartVM::GetVMLaunchCount();
581
582 {
583 auto engine = builder.LaunchEngine();
584 ASSERT_EQ(launch_count + 1u, DartVM::GetVMLaunchCount());
585 }
586
587 {
588 auto engine = builder.LaunchEngine();
589 ASSERT_EQ(launch_count + 2u, DartVM::GetVMLaunchCount());
590 }
591}
592
593//------------------------------------------------------------------------------
594///
595TEST_F(EmbedderTest, DartEntrypointArgs) {
596 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
597 EmbedderConfigBuilder builder(context);
598 builder.SetSoftwareRendererConfig();
599 builder.AddDartEntrypointArgument("foo");
600 builder.AddDartEntrypointArgument("bar");
601 builder.SetDartEntrypoint("dart_entrypoint_args");
602 fml::AutoResetWaitableEvent callback_latch;
603 std::vector<std::string> callback_args;
604 auto nativeArgumentsCallback = [&callback_args,
605 &callback_latch](Dart_NativeArguments args) {
606 Dart_Handle exception = nullptr;
607 callback_args =
609 args, 0, exception);
610 callback_latch.Signal();
611 };
612 context.AddNativeCallback("NativeArgumentsCallback",
613 CREATE_NATIVE_ENTRY(nativeArgumentsCallback));
614 auto engine = builder.LaunchEngine();
615 callback_latch.Wait();
616 ASSERT_EQ(callback_args[0], "foo");
617 ASSERT_EQ(callback_args[1], "bar");
618}
619
620//------------------------------------------------------------------------------
621/// These snapshots may be materialized from symbols and the size field may not
622/// be relevant. Since this information is redundant, engine launch should not
623/// be gated on a non-zero buffer size.
624///
625TEST_F(EmbedderTest, VMAndIsolateSnapshotSizesAreRedundantInAOTMode) {
627 GTEST_SKIP();
628 return;
629 }
630 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
631 EmbedderConfigBuilder builder(context);
632 builder.SetSoftwareRendererConfig();
633
634 // The fixture sets this up correctly. Intentionally mess up the args.
635 builder.GetProjectArgs().vm_snapshot_data_size = 0;
636 builder.GetProjectArgs().vm_snapshot_instructions_size = 0;
637 builder.GetProjectArgs().isolate_snapshot_data_size = 0;
638 builder.GetProjectArgs().isolate_snapshot_instructions_size = 0;
639
640 auto engine = builder.LaunchEngine();
641 ASSERT_TRUE(engine.is_valid());
642}
643
644TEST_F(EmbedderTest, CanRenderImplicitView) {
645 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
646
647 EmbedderConfigBuilder builder(context);
648 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
649 builder.SetCompositor();
650 builder.SetDartEntrypoint("render_implicit_view");
651 builder.SetRenderTargetType(
652 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
653
655
656 context.GetCompositor().SetNextPresentCallback(
657 [&](FlutterViewId view_id, const FlutterLayer** layers,
658 size_t layers_count) {
659 ASSERT_EQ(view_id, kFlutterImplicitViewId);
660 latch.Signal();
661 });
662
663 auto engine = builder.LaunchEngine();
664
665 FlutterWindowMetricsEvent event = {};
666 event.struct_size = sizeof(event);
667 event.width = 300;
668 event.height = 200;
669 event.pixel_ratio = 1.0;
671 kSuccess);
672 ASSERT_TRUE(engine.is_valid());
673 latch.Wait();
674}
675
676TEST_F(EmbedderTest, CanRenderImplicitViewUsingPresentLayersCallback) {
677 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
678
679 EmbedderConfigBuilder builder(context);
680 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
681 builder.SetCompositor(/* avoid_backing_store_cache = */ false,
682 /* use_present_layers_callback = */ true);
683 builder.SetDartEntrypoint("render_implicit_view");
684 builder.SetRenderTargetType(
685 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
686
688
689 context.GetCompositor().SetNextPresentCallback(
690 [&](FlutterViewId view_id, const FlutterLayer** layers,
691 size_t layers_count) {
692 ASSERT_EQ(view_id, kFlutterImplicitViewId);
693 latch.Signal();
694 });
695
696 auto engine = builder.LaunchEngine();
697
698 FlutterWindowMetricsEvent event = {};
699 event.struct_size = sizeof(event);
700 event.width = 300;
701 event.height = 200;
702 event.pixel_ratio = 1.0;
704 kSuccess);
705 ASSERT_TRUE(engine.is_valid());
706 latch.Wait();
707}
708
709//------------------------------------------------------------------------------
710/// Test the layer structure and pixels rendered when using a custom software
711/// compositor.
712///
713// TODO(143940): Convert this test to use SkiaGold.
714#if FML_OS_MACOSX && FML_ARCH_CPU_ARM64
715TEST_F(EmbedderTest,
716 DISABLED_CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
717#else
719 CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
720#endif // FML_OS_MACOSX && FML_ARCH_CPU_ARM64
721
722 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
723
724 EmbedderConfigBuilder builder(context);
725 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
726 builder.SetCompositor();
727 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
728
729 builder.SetRenderTargetType(
730 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
731
732 fml::CountDownLatch latch(5);
733
734 auto scene_image = context.GetNextSceneImage();
735
736 context.GetCompositor().SetNextPresentCallback(
737 [&](FlutterViewId view_id, const FlutterLayer** layers,
738 size_t layers_count) {
739 ASSERT_EQ(layers_count, 5u);
740
741 // Layer Root
742 {
743 FlutterBackingStore backing_store = *layers[0]->backing_store;
745 backing_store.did_update = true;
746 backing_store.software.height = 600;
747
748 FlutterRect paint_region_rects[] = {
749 FlutterRectMakeLTRB(0, 0, 800, 600),
750 };
751 FlutterRegion paint_region = {
752 .struct_size = sizeof(FlutterRegion),
753 .rects_count = 1,
754 .rects = paint_region_rects,
755 };
756 FlutterBackingStorePresentInfo present_info = {
758 .paint_region = &paint_region,
759 };
760
761 FlutterLayer layer = {};
762 layer.struct_size = sizeof(layer);
764 layer.backing_store = &backing_store;
765 layer.size = FlutterSizeMake(800.0, 600.0);
766 layer.offset = FlutterPointMake(0.0, 0.0);
767 layer.backing_store_present_info = &present_info;
768
769 ASSERT_EQ(*layers[0], layer);
770 }
771
772 // Layer 1
773 {
776 platform_view.identifier = 1;
777
778 FlutterLayer layer = {};
779 layer.struct_size = sizeof(layer);
782 layer.size = FlutterSizeMake(50.0, 150.0);
783 layer.offset = FlutterPointMake(20.0, 20.0);
784
785 ASSERT_EQ(*layers[1], layer);
786 }
787
788 // Layer 2
789 {
790 FlutterBackingStore backing_store = *layers[2]->backing_store;
792 backing_store.did_update = true;
793 backing_store.software.height = 600;
794
795 FlutterRect paint_region_rects[] = {
796 FlutterRectMakeLTRB(30, 30, 80, 180),
797 };
798 FlutterRegion paint_region = {
799 .struct_size = sizeof(FlutterRegion),
800 .rects_count = 1,
801 .rects = paint_region_rects,
802 };
803 FlutterBackingStorePresentInfo present_info = {
805 .paint_region = &paint_region,
806 };
807
808 FlutterLayer layer = {};
809 layer.struct_size = sizeof(layer);
811 layer.backing_store = &backing_store;
812 layer.size = FlutterSizeMake(800.0, 600.0);
813 layer.offset = FlutterPointMake(0.0, 0.0);
814 layer.backing_store_present_info = &present_info;
815
816 ASSERT_EQ(*layers[2], layer);
817 }
818
819 // Layer 3
820 {
823 platform_view.identifier = 2;
824
825 FlutterLayer layer = {};
826 layer.struct_size = sizeof(layer);
829 layer.size = FlutterSizeMake(50.0, 150.0);
830 layer.offset = FlutterPointMake(40.0, 40.0);
831
832 ASSERT_EQ(*layers[3], layer);
833 }
834
835 // Layer 4
836 {
837 FlutterBackingStore backing_store = *layers[4]->backing_store;
839 backing_store.did_update = true;
840 backing_store.software.height = 600;
841
842 FlutterRect paint_region_rects[] = {
843 FlutterRectMakeLTRB(50, 50, 100, 200),
844 };
845 FlutterRegion paint_region = {
846 .struct_size = sizeof(FlutterRegion),
847 .rects_count = 1,
848 .rects = paint_region_rects,
849 };
850 FlutterBackingStorePresentInfo present_info = {
852 .paint_region = &paint_region,
853 };
854
855 FlutterLayer layer = {};
856 layer.struct_size = sizeof(layer);
858 layer.backing_store = &backing_store;
859 layer.size = FlutterSizeMake(800.0, 600.0);
860 layer.offset = FlutterPointMake(0.0, 0.0);
861 layer.backing_store_present_info = &present_info;
862
863 ASSERT_EQ(*layers[4], layer);
864 }
865
866 latch.CountDown();
867 });
868
869 context.GetCompositor().SetPlatformViewRendererCallback(
870 [&](const FlutterLayer& layer, GrDirectContext*
871 /* don't use because software compositor */) -> sk_sp<SkImage> {
873 layer, nullptr /* null because software compositor */);
874 auto canvas = surface->getCanvas();
875 FML_CHECK(canvas != nullptr);
876
877 switch (layer.platform_view->identifier) {
878 case 1: {
880 // See dart test for total order.
881 paint.setColor(SK_ColorGREEN);
882 paint.setAlpha(127);
883 const auto& rect =
884 SkRect::MakeWH(layer.size.width, layer.size.height);
885 canvas->drawRect(rect, paint);
886 latch.CountDown();
887 } break;
888 case 2: {
890 // See dart test for total order.
891 paint.setColor(SK_ColorMAGENTA);
892 paint.setAlpha(127);
893 const auto& rect =
894 SkRect::MakeWH(layer.size.width, layer.size.height);
895 canvas->drawRect(rect, paint);
896 latch.CountDown();
897 } break;
898 default:
899 // Asked to render an unknown platform view.
900 FML_CHECK(false)
901 << "Test was asked to composite an unknown platform view.";
902 }
903
904 return surface->makeImageSnapshot();
905 });
906
907 context.AddNativeCallback(
908 "SignalNativeTest",
910 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
911
912 auto engine = builder.LaunchEngine();
913
914 // Send a window metrics events so frames may be scheduled.
915 FlutterWindowMetricsEvent event = {};
916 event.struct_size = sizeof(event);
917 event.width = 800;
918 event.height = 600;
919 event.pixel_ratio = 1.0;
920 event.physical_view_inset_top = 0.0;
921 event.physical_view_inset_right = 0.0;
922 event.physical_view_inset_bottom = 0.0;
923 event.physical_view_inset_left = 0.0;
925 kSuccess);
926 ASSERT_TRUE(engine.is_valid());
927
928 latch.Wait();
929
930 ASSERT_TRUE(ImageMatchesFixture("compositor_software.png", scene_image));
931
932 // There should no present calls on the root surface.
933 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
934}
935
936//------------------------------------------------------------------------------
937/// Test the layer structure and pixels rendered when using a custom software
938/// compositor, with a transparent overlay
939///
940TEST_F(EmbedderTest, NoLayerCreatedForTransparentOverlayOnTopOfPlatformLayer) {
941 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
942
943 EmbedderConfigBuilder builder(context);
944 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
945 builder.SetCompositor();
946 builder.SetDartEntrypoint("can_composite_platform_views_transparent_overlay");
947
948 builder.SetRenderTargetType(
949 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
950
951 fml::CountDownLatch latch(4);
952
953 auto scene_image = context.GetNextSceneImage();
954
955 context.GetCompositor().SetNextPresentCallback(
956 [&](FlutterViewId view_id, const FlutterLayer** layers,
957 size_t layers_count) {
958 ASSERT_EQ(layers_count, 2u);
959
960 // Layer Root
961 {
962 FlutterBackingStore backing_store = *layers[0]->backing_store;
964 backing_store.did_update = true;
965 backing_store.software.height = 600;
966
967 FlutterRect paint_region_rects[] = {
968 FlutterRectMakeLTRB(0, 0, 800, 600),
969 };
970 FlutterRegion paint_region = {
971 .struct_size = sizeof(FlutterRegion),
972 .rects_count = 1,
973 .rects = paint_region_rects,
974 };
975 FlutterBackingStorePresentInfo present_info = {
977 .paint_region = &paint_region,
978 };
979
980 FlutterLayer layer = {};
981 layer.struct_size = sizeof(layer);
983 layer.backing_store = &backing_store;
984 layer.size = FlutterSizeMake(800.0, 600.0);
985 layer.offset = FlutterPointMake(0.0, 0.0);
986 layer.backing_store_present_info = &present_info;
987
988 ASSERT_EQ(*layers[0], layer);
989 }
990
991 // Layer 1
992 {
995 platform_view.identifier = 1;
996
997 FlutterLayer layer = {};
998 layer.struct_size = sizeof(layer);
1001 layer.size = FlutterSizeMake(50.0, 150.0);
1002 layer.offset = FlutterPointMake(20.0, 20.0);
1003
1004 ASSERT_EQ(*layers[1], layer);
1005 }
1006
1007 latch.CountDown();
1008 });
1009
1010 context.GetCompositor().SetPlatformViewRendererCallback(
1011 [&](const FlutterLayer& layer, GrDirectContext*
1012 /* don't use because software compositor */) -> sk_sp<SkImage> {
1014 layer, nullptr /* null because software compositor */);
1015 auto canvas = surface->getCanvas();
1016 FML_CHECK(canvas != nullptr);
1017
1018 switch (layer.platform_view->identifier) {
1019 case 1: {
1020 SkPaint paint;
1021 // See dart test for total order.
1022 paint.setColor(SK_ColorGREEN);
1023 paint.setAlpha(127);
1024 const auto& rect =
1025 SkRect::MakeWH(layer.size.width, layer.size.height);
1026 canvas->drawRect(rect, paint);
1027 latch.CountDown();
1028 } break;
1029 default:
1030 // Asked to render an unknown platform view.
1031 FML_CHECK(false)
1032 << "Test was asked to composite an unknown platform view.";
1033 }
1034
1035 return surface->makeImageSnapshot();
1036 });
1037
1038 context.AddNativeCallback(
1039 "SignalNativeTest",
1041 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1042
1043 auto engine = builder.LaunchEngine();
1044
1045 // Send a window metrics events so frames may be scheduled.
1046 FlutterWindowMetricsEvent event = {};
1047 event.struct_size = sizeof(event);
1048 event.width = 800;
1049 event.height = 600;
1050 event.pixel_ratio = 1.0;
1051 event.physical_view_inset_top = 0.0;
1052 event.physical_view_inset_right = 0.0;
1053 event.physical_view_inset_bottom = 0.0;
1054 event.physical_view_inset_left = 0.0;
1056 kSuccess);
1057 ASSERT_TRUE(engine.is_valid());
1058
1059 latch.Wait();
1060
1061 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1062 // platforms.
1063#if !defined(FML_OS_LINUX)
1064 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1065#endif // FML_OS_LINUX
1066 ASSERT_TRUE(ImageMatchesFixture(
1067 "compositor_platform_layer_with_no_overlay.png", scene_image));
1068
1069 // There should no present calls on the root surface.
1070 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1071}
1072
1073//------------------------------------------------------------------------------
1074/// Test the layer structure and pixels rendered when using a custom software
1075/// compositor, with a no overlay
1076///
1077TEST_F(EmbedderTest, NoLayerCreatedForNoOverlayOnTopOfPlatformLayer) {
1078 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1079
1080 EmbedderConfigBuilder builder(context);
1081 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
1082 builder.SetCompositor();
1083 builder.SetDartEntrypoint("can_composite_platform_views_no_overlay");
1084
1085 builder.SetRenderTargetType(
1086 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1087
1088 fml::CountDownLatch latch(4);
1089
1090 auto scene_image = context.GetNextSceneImage();
1091
1092 context.GetCompositor().SetNextPresentCallback(
1093 [&](FlutterViewId view_id, const FlutterLayer** layers,
1094 size_t layers_count) {
1095 ASSERT_EQ(layers_count, 2u);
1096
1097 // Layer Root
1098 {
1099 FlutterBackingStore backing_store = *layers[0]->backing_store;
1100 backing_store.type = kFlutterBackingStoreTypeSoftware;
1101 backing_store.did_update = true;
1102 backing_store.software.height = 600;
1103
1104 FlutterRect paint_region_rects[] = {
1105 FlutterRectMakeLTRB(0, 0, 800, 600),
1106 };
1107 FlutterRegion paint_region = {
1108 .struct_size = sizeof(FlutterRegion),
1109 .rects_count = 1,
1110 .rects = paint_region_rects,
1111 };
1112 FlutterBackingStorePresentInfo present_info = {
1114 .paint_region = &paint_region,
1115 };
1116
1117 FlutterLayer layer = {};
1118 layer.struct_size = sizeof(layer);
1120 layer.backing_store = &backing_store;
1121 layer.size = FlutterSizeMake(800.0, 600.0);
1122 layer.offset = FlutterPointMake(0.0, 0.0);
1123 layer.backing_store_present_info = &present_info;
1124
1125 ASSERT_EQ(*layers[0], layer);
1126 }
1127
1128 // Layer 1
1129 {
1132 platform_view.identifier = 1;
1133
1134 FlutterLayer layer = {};
1135 layer.struct_size = sizeof(layer);
1138 layer.size = FlutterSizeMake(50.0, 150.0);
1139 layer.offset = FlutterPointMake(20.0, 20.0);
1140
1141 ASSERT_EQ(*layers[1], layer);
1142 }
1143
1144 latch.CountDown();
1145 });
1146
1147 context.GetCompositor().SetPlatformViewRendererCallback(
1148 [&](const FlutterLayer& layer, GrDirectContext*
1149 /* don't use because software compositor */) -> sk_sp<SkImage> {
1151 layer, nullptr /* null because software compositor */);
1152 auto canvas = surface->getCanvas();
1153 FML_CHECK(canvas != nullptr);
1154
1155 switch (layer.platform_view->identifier) {
1156 case 1: {
1157 SkPaint paint;
1158 // See dart test for total order.
1159 paint.setColor(SK_ColorGREEN);
1160 paint.setAlpha(127);
1161 const auto& rect =
1162 SkRect::MakeWH(layer.size.width, layer.size.height);
1163 canvas->drawRect(rect, paint);
1164 latch.CountDown();
1165 } break;
1166 default:
1167 // Asked to render an unknown platform view.
1168 FML_CHECK(false)
1169 << "Test was asked to composite an unknown platform view.";
1170 }
1171
1172 return surface->makeImageSnapshot();
1173 });
1174
1175 context.AddNativeCallback(
1176 "SignalNativeTest",
1178 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1179
1180 auto engine = builder.LaunchEngine();
1181
1182 // Send a window metrics events so frames may be scheduled.
1183 FlutterWindowMetricsEvent event = {};
1184 event.struct_size = sizeof(event);
1185 event.width = 800;
1186 event.height = 600;
1187 event.pixel_ratio = 1.0;
1188 event.physical_view_inset_top = 0.0;
1189 event.physical_view_inset_right = 0.0;
1190 event.physical_view_inset_bottom = 0.0;
1191 event.physical_view_inset_left = 0.0;
1193 kSuccess);
1194 ASSERT_TRUE(engine.is_valid());
1195
1196 latch.Wait();
1197
1198 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
1199 // platforms.
1200#if !defined(FML_OS_LINUX)
1201 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
1202#endif // FML_OS_LINUX
1203 ASSERT_TRUE(ImageMatchesFixture(
1204 "compositor_platform_layer_with_no_overlay.png", scene_image));
1205
1206 // There should no present calls on the root surface.
1207 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
1208}
1209
1210//------------------------------------------------------------------------------
1211/// Test that an engine can be initialized but not run.
1212///
1213TEST_F(EmbedderTest, CanCreateInitializedEngine) {
1214 EmbedderConfigBuilder builder(
1215 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext));
1216 builder.SetSoftwareRendererConfig();
1217 auto engine = builder.InitializeEngine();
1218 ASSERT_TRUE(engine.is_valid());
1219 engine.reset();
1220}
1221
1222//------------------------------------------------------------------------------
1223/// Test that an initialized engine can be run exactly once.
1224///
1225TEST_F(EmbedderTest, CanRunInitializedEngine) {
1226 EmbedderConfigBuilder builder(
1227 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext));
1228 builder.SetSoftwareRendererConfig();
1229 auto engine = builder.InitializeEngine();
1230 ASSERT_TRUE(engine.is_valid());
1231 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1232 // Cannot re-run an already running engine.
1234 engine.reset();
1235}
1236
1237//------------------------------------------------------------------------------
1238/// Test that an engine can be deinitialized.
1239///
1240TEST_F(EmbedderTest, CanDeinitializeAnEngine) {
1241 EmbedderConfigBuilder builder(
1242 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext));
1243 builder.SetSoftwareRendererConfig();
1244 auto engine = builder.InitializeEngine();
1245 ASSERT_TRUE(engine.is_valid());
1246 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
1247 // Cannot re-run an already running engine.
1249 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1250 // It is ok to deinitialize an engine multiple times.
1251 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
1252
1253 // Sending events to a deinitialized engine fails.
1254 FlutterWindowMetricsEvent event = {};
1255 event.struct_size = sizeof(event);
1256 event.width = 800;
1257 event.height = 600;
1258 event.pixel_ratio = 1.0;
1259 event.physical_view_inset_top = 0.0;
1260 event.physical_view_inset_right = 0.0;
1261 event.physical_view_inset_bottom = 0.0;
1262 event.physical_view_inset_left = 0.0;
1265 engine.reset();
1266}
1267
1268//------------------------------------------------------------------------------
1269/// Test that a view can be added to a running engine.
1270///
1271TEST_F(EmbedderTest, CanAddView) {
1272 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1273 EmbedderConfigBuilder builder(context);
1274 builder.SetSoftwareRendererConfig();
1275 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1276
1277 fml::AutoResetWaitableEvent ready_latch, message_latch;
1278 context.AddNativeCallback(
1279 "SignalNativeTest",
1281 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1282
1283 std::string message;
1284 context.AddNativeCallback("SignalNativeMessage",
1286 message =
1289 message_latch.Signal();
1290 }));
1291
1292 auto engine = builder.LaunchEngine();
1293 ASSERT_TRUE(engine.is_valid());
1294
1295 ready_latch.Wait();
1296
1297 FlutterWindowMetricsEvent metrics = {};
1298 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1299 metrics.width = 800;
1300 metrics.height = 600;
1301 metrics.pixel_ratio = 1.0;
1302 metrics.view_id = 123;
1303
1306 info.view_id = 123;
1307 info.view_metrics = &metrics;
1308 info.add_view_callback = [](const FlutterAddViewResult* result) {
1309 EXPECT_TRUE(result->added);
1310 };
1311 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1312 message_latch.Wait();
1313 ASSERT_EQ("View IDs: [0, 123]", message);
1314}
1315
1316//------------------------------------------------------------------------------
1317/// Test that adding a view schedules a frame.
1318///
1319TEST_F(EmbedderTest, AddViewSchedulesFrame) {
1320 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1321 EmbedderConfigBuilder builder(context);
1322 builder.SetSoftwareRendererConfig();
1323 builder.SetDartEntrypoint("add_view_schedules_frame");
1325 context.AddNativeCallback(
1326 "SignalNativeTest",
1328 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
1329
1330 fml::AutoResetWaitableEvent check_latch;
1331 context.AddNativeCallback(
1332 "SignalNativeCount",
1334 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
1335
1336 auto engine = builder.LaunchEngine();
1337 ASSERT_TRUE(engine.is_valid());
1338
1339 // Wait for the application to attach the listener.
1340 latch.Wait();
1341
1342 FlutterWindowMetricsEvent metrics = {};
1343 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1344 metrics.width = 800;
1345 metrics.height = 600;
1346 metrics.pixel_ratio = 1.0;
1347 metrics.view_id = 123;
1348
1351 info.view_id = 123;
1352 info.view_metrics = &metrics;
1353 info.add_view_callback = [](const FlutterAddViewResult* result) {
1354 EXPECT_TRUE(result->added);
1355 };
1356 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
1357
1358 check_latch.Wait();
1359}
1360
1361//------------------------------------------------------------------------------
1362/// Test that a view that was added can be removed.
1363///
1364TEST_F(EmbedderTest, CanRemoveView) {
1365 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1366 EmbedderConfigBuilder builder(context);
1367 builder.SetSoftwareRendererConfig();
1368 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1369
1370 fml::AutoResetWaitableEvent ready_latch, message_latch;
1371 context.AddNativeCallback(
1372 "SignalNativeTest",
1374 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1375
1376 std::string message;
1377 context.AddNativeCallback("SignalNativeMessage",
1379 message =
1382 message_latch.Signal();
1383 }));
1384
1385 auto engine = builder.LaunchEngine();
1386 ASSERT_TRUE(engine.is_valid());
1387
1388 ready_latch.Wait();
1389
1390 // Add view 123.
1391 FlutterWindowMetricsEvent metrics = {};
1392 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1393 metrics.width = 800;
1394 metrics.height = 600;
1395 metrics.pixel_ratio = 1.0;
1396 metrics.view_id = 123;
1397
1398 FlutterAddViewInfo add_info = {};
1399 add_info.struct_size = sizeof(FlutterAddViewInfo);
1400 add_info.view_id = 123;
1401 add_info.view_metrics = &metrics;
1402 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1403 ASSERT_TRUE(result->added);
1404 };
1405 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1406 message_latch.Wait();
1407 ASSERT_EQ(message, "View IDs: [0, 123]");
1408
1409 // Remove view 123.
1410 FlutterRemoveViewInfo remove_info = {};
1411 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1412 remove_info.view_id = 123;
1413 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1414 EXPECT_TRUE(result->removed);
1415 };
1416 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1417 message_latch.Wait();
1418 ASSERT_EQ(message, "View IDs: [0]");
1419}
1420
1421//------------------------------------------------------------------------------
1422/// The implicit view is a special view that the engine and framework assume
1423/// can *always* be rendered to. Test that this view cannot be removed.
1424///
1425TEST_F(EmbedderTest, CannotRemoveImplicitView) {
1426 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1427 EmbedderConfigBuilder builder(context);
1428 builder.SetSoftwareRendererConfig();
1429
1430 auto engine = builder.LaunchEngine();
1431 ASSERT_TRUE(engine.is_valid());
1432
1435 info.view_id = kFlutterImplicitViewId;
1436 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1437 FAIL();
1438 };
1440}
1441
1442//------------------------------------------------------------------------------
1443/// Test that a view cannot be added if its ID already exists.
1444///
1445TEST_F(EmbedderTest, CannotAddDuplicateViews) {
1446 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1447 EmbedderConfigBuilder builder(context);
1448 builder.SetSoftwareRendererConfig();
1449 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1450
1451 fml::AutoResetWaitableEvent ready_latch, message_latch;
1452 context.AddNativeCallback(
1453 "SignalNativeTest",
1455 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1456
1457 std::string message;
1458 context.AddNativeCallback("SignalNativeMessage",
1460 message =
1463 message_latch.Signal();
1464 }));
1465
1466 auto engine = builder.LaunchEngine();
1467 ASSERT_TRUE(engine.is_valid());
1468
1469 ready_latch.Wait();
1470
1471 // Add view 123.
1472 struct Captures {
1473 std::atomic<int> count = 0;
1474 fml::AutoResetWaitableEvent failure_latch;
1475 };
1476 Captures captures;
1477
1478 FlutterWindowMetricsEvent metrics = {};
1479 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1480 metrics.width = 800;
1481 metrics.height = 600;
1482 metrics.pixel_ratio = 1.0;
1483 metrics.view_id = 123;
1484
1485 FlutterAddViewInfo add_info = {};
1486 add_info.struct_size = sizeof(FlutterAddViewInfo);
1487 add_info.view_id = 123;
1488 add_info.view_metrics = &metrics;
1489 add_info.user_data = &captures;
1490 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1491 auto captures = reinterpret_cast<Captures*>(result->user_data);
1492
1493 int count = captures->count.fetch_add(1);
1494
1495 if (count == 0) {
1496 ASSERT_TRUE(result->added);
1497 } else {
1498 EXPECT_FALSE(result->added);
1499 captures->failure_latch.Signal();
1500 }
1501 };
1502 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1503 message_latch.Wait();
1504 ASSERT_EQ(message, "View IDs: [0, 123]");
1505 ASSERT_FALSE(captures.failure_latch.IsSignaledForTest());
1506
1507 // Add view 123 a second time.
1508 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1509 captures.failure_latch.Wait();
1510 ASSERT_EQ(captures.count, 2);
1511 ASSERT_FALSE(message_latch.IsSignaledForTest());
1512}
1513
1514//------------------------------------------------------------------------------
1515/// Test that a removed view's ID can be reused to add a new view.
1516///
1517TEST_F(EmbedderTest, CanReuseViewIds) {
1518 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1519 EmbedderConfigBuilder builder(context);
1520 builder.SetSoftwareRendererConfig();
1521 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1522
1523 fml::AutoResetWaitableEvent ready_latch, message_latch;
1524 context.AddNativeCallback(
1525 "SignalNativeTest",
1527 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1528
1529 std::string message;
1530 context.AddNativeCallback("SignalNativeMessage",
1532 message =
1535 message_latch.Signal();
1536 }));
1537
1538 auto engine = builder.LaunchEngine();
1539 ASSERT_TRUE(engine.is_valid());
1540
1541 ready_latch.Wait();
1542
1543 // Add view 123.
1544 FlutterWindowMetricsEvent metrics = {};
1545 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
1546 metrics.width = 800;
1547 metrics.height = 600;
1548 metrics.pixel_ratio = 1.0;
1549 metrics.view_id = 123;
1550
1551 FlutterAddViewInfo add_info = {};
1552 add_info.struct_size = sizeof(FlutterAddViewInfo);
1553 add_info.view_id = 123;
1554 add_info.view_metrics = &metrics;
1555 add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1556 ASSERT_TRUE(result->added);
1557 };
1558 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1559 message_latch.Wait();
1560 ASSERT_EQ(message, "View IDs: [0, 123]");
1561
1562 // Remove view 123.
1563 FlutterRemoveViewInfo remove_info = {};
1564 remove_info.struct_size = sizeof(FlutterAddViewInfo);
1565 remove_info.view_id = 123;
1566 remove_info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1567 ASSERT_TRUE(result->removed);
1568 };
1569 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_info), kSuccess);
1570 message_latch.Wait();
1571 ASSERT_EQ(message, "View IDs: [0]");
1572
1573 // Re-add view 123.
1574 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_info), kSuccess);
1575 message_latch.Wait();
1576 ASSERT_EQ(message, "View IDs: [0, 123]");
1577}
1578
1579//------------------------------------------------------------------------------
1580/// Test that attempting to remove a view that does not exist fails as expected.
1581///
1582TEST_F(EmbedderTest, CannotRemoveUnknownView) {
1583 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1584 EmbedderConfigBuilder builder(context);
1585 builder.SetSoftwareRendererConfig();
1586
1587 auto engine = builder.LaunchEngine();
1588 ASSERT_TRUE(engine.is_valid());
1589
1593 info.view_id = 123;
1594 info.user_data = &latch;
1595 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
1596 EXPECT_FALSE(result->removed);
1597 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data)->Signal();
1598 };
1599 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &info), kSuccess);
1600 latch.Wait();
1601}
1602
1603//------------------------------------------------------------------------------
1604/// View operations - adding, removing, sending window metrics - must execute in
1605/// order even though they are asynchronous. This is necessary to ensure the
1606/// embedder's and engine's states remain synchronized.
1607///
1608TEST_F(EmbedderTest, ViewOperationsOrdered) {
1609 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1610 EmbedderConfigBuilder builder(context);
1611 builder.SetSoftwareRendererConfig();
1612 builder.SetDartEntrypoint("window_metrics_event_all_view_ids");
1613
1614 fml::AutoResetWaitableEvent ready_latch;
1615 context.AddNativeCallback(
1616 "SignalNativeTest",
1618 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
1619
1620 std::atomic<int> message_count = 0;
1621 context.AddNativeCallback("SignalNativeMessage",
1623 message_count.fetch_add(1);
1624 }));
1625
1626 auto engine = builder.LaunchEngine();
1627 ASSERT_TRUE(engine.is_valid());
1628
1629 ready_latch.Wait();
1630
1631 // Enqueue multiple view operations at once:
1632 //
1633 // 1. Add view 123 - This must succeed.
1634 // 2. Add duplicate view 123 - This must fail asynchronously.
1635 // 3. Add second view 456 - This must succeed.
1636 // 4. Remove second view 456 - This must succeed.
1637 //
1638 // The engine must execute view operations asynchronously in serial order.
1639 // If step 2 succeeds instead of step 1, this indicates the engine did not
1640 // execute the view operations in the correct order. If step 4 fails,
1641 // this indicates the engine did not wait until the add second view completed.
1642 FlutterWindowMetricsEvent metrics123 = {};
1643 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
1644 metrics123.width = 800;
1645 metrics123.height = 600;
1646 metrics123.pixel_ratio = 1.0;
1647 metrics123.view_id = 123;
1648
1649 FlutterWindowMetricsEvent metrics456 = {};
1650 metrics456.struct_size = sizeof(FlutterWindowMetricsEvent);
1651 metrics456.width = 800;
1652 metrics456.height = 600;
1653 metrics456.pixel_ratio = 1.0;
1654 metrics456.view_id = 456;
1655
1656 struct Captures {
1657 fml::AutoResetWaitableEvent add_first_view;
1658 fml::AutoResetWaitableEvent add_duplicate_view;
1659 fml::AutoResetWaitableEvent add_second_view;
1660 fml::AutoResetWaitableEvent remove_second_view;
1661 };
1662 Captures captures;
1663
1664 // Add view 123.
1665 FlutterAddViewInfo add_view_info = {};
1666 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
1667 add_view_info.view_id = 123;
1668 add_view_info.view_metrics = &metrics123;
1669 add_view_info.user_data = &captures;
1670 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
1671 auto captures = reinterpret_cast<Captures*>(result->user_data);
1672
1673 ASSERT_TRUE(result->added);
1674 ASSERT_FALSE(captures->add_first_view.IsSignaledForTest());
1675 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
1676 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
1677 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
1678
1679 captures->add_first_view.Signal();
1680 };
1681
1682 // Add duplicate view 123.
1683 FlutterAddViewInfo add_duplicate_view_info = {};
1684 add_duplicate_view_info.struct_size = sizeof(FlutterAddViewInfo);
1685 add_duplicate_view_info.view_id = 123;
1686 add_duplicate_view_info.view_metrics = &metrics123;
1687 add_duplicate_view_info.user_data = &captures;
1688 add_duplicate_view_info.add_view_callback =
1689 [](const FlutterAddViewResult* result) {
1690 auto captures = reinterpret_cast<Captures*>(result->user_data);
1691
1692 ASSERT_FALSE(result->added);
1693 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
1694 ASSERT_FALSE(captures->add_duplicate_view.IsSignaledForTest());
1695 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
1696 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
1697
1698 captures->add_duplicate_view.Signal();
1699 };
1700
1701 // Add view 456.
1702 FlutterAddViewInfo add_second_view_info = {};
1703 add_second_view_info.struct_size = sizeof(FlutterAddViewInfo);
1704 add_second_view_info.view_id = 456;
1705 add_second_view_info.view_metrics = &metrics456;
1706 add_second_view_info.user_data = &captures;
1707 add_second_view_info.add_view_callback =
1708 [](const FlutterAddViewResult* result) {
1709 auto captures = reinterpret_cast<Captures*>(result->user_data);
1710
1711 ASSERT_TRUE(result->added);
1712 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
1713 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
1714 ASSERT_FALSE(captures->add_second_view.IsSignaledForTest());
1715 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
1716
1717 captures->add_second_view.Signal();
1718 };
1719
1720 // Remove view 456.
1721 FlutterRemoveViewInfo remove_second_view_info = {};
1722 remove_second_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
1723 remove_second_view_info.view_id = 456;
1724 remove_second_view_info.user_data = &captures;
1725 remove_second_view_info.remove_view_callback =
1726 [](const FlutterRemoveViewResult* result) {
1727 auto captures = reinterpret_cast<Captures*>(result->user_data);
1728
1729 ASSERT_TRUE(result->removed);
1730 ASSERT_TRUE(captures->add_first_view.IsSignaledForTest());
1731 ASSERT_TRUE(captures->add_duplicate_view.IsSignaledForTest());
1732 ASSERT_TRUE(captures->add_second_view.IsSignaledForTest());
1733 ASSERT_FALSE(captures->remove_second_view.IsSignaledForTest());
1734
1735 captures->remove_second_view.Signal();
1736 };
1737
1738 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
1739 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_duplicate_view_info),
1740 kSuccess);
1741 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_second_view_info),
1742 kSuccess);
1743 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_second_view_info),
1744 kSuccess);
1745 captures.remove_second_view.Wait();
1746 captures.add_second_view.Wait();
1747 captures.add_duplicate_view.Wait();
1748 captures.add_first_view.Wait();
1749 ASSERT_EQ(message_count, 3);
1750}
1751
1752//------------------------------------------------------------------------------
1753/// Test the engine can present to multiple views.
1754///
1755TEST_F(EmbedderTest, CanRenderMultipleViews) {
1756 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1757 EmbedderConfigBuilder builder(context);
1758 builder.SetSoftwareRendererConfig();
1759 builder.SetCompositor();
1760 builder.SetDartEntrypoint("render_all_views");
1761
1762 builder.SetRenderTargetType(
1763 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1764
1765 fml::AutoResetWaitableEvent latch0, latch123;
1766 context.GetCompositor().SetPresentCallback(
1767 [&](FlutterViewId view_id, const FlutterLayer** layers,
1768 size_t layers_count) {
1769 switch (view_id) {
1770 case 0:
1771 latch0.Signal();
1772 break;
1773 case 123:
1774 latch123.Signal();
1775 break;
1776 default:
1778 }
1779 },
1780 /* one_shot= */ false);
1781
1782 auto engine = builder.LaunchEngine();
1783 ASSERT_TRUE(engine.is_valid());
1784
1785 // Give the implicit view a non-zero size so that it renders something.
1786 FlutterWindowMetricsEvent metrics0 = {};
1787 metrics0.struct_size = sizeof(FlutterWindowMetricsEvent);
1788 metrics0.width = 800;
1789 metrics0.height = 600;
1790 metrics0.pixel_ratio = 1.0;
1791 metrics0.view_id = 0;
1792 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics0),
1793 kSuccess);
1794
1795 // Add view 123.
1796 FlutterWindowMetricsEvent metrics123 = {};
1797 metrics123.struct_size = sizeof(FlutterWindowMetricsEvent);
1798 metrics123.width = 800;
1799 metrics123.height = 600;
1800 metrics123.pixel_ratio = 1.0;
1801 metrics123.view_id = 123;
1802
1803 FlutterAddViewInfo add_view_info = {};
1804 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
1805 add_view_info.view_id = 123;
1806 add_view_info.view_metrics = &metrics123;
1807 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
1808 ASSERT_TRUE(result->added);
1809 };
1810
1811 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
1812
1813 latch0.Wait();
1814 latch123.Wait();
1815}
1816
1817//------------------------------------------------------------------------------
1818/// Test that the backing store is created with the correct view ID, is used
1819/// for the correct view, and is cached according to their views.
1820///
1821/// The test involves two frames:
1822/// 1. The first frame renders the implicit view and the second view.
1823/// 2. The second frame renders the implicit view and the third view.
1824///
1825/// The test verifies that:
1826/// - Each backing store is created with a valid view ID.
1827/// - Each backing store is presented for the view that it was created for.
1828/// - Both frames render the expected sets of views.
1829/// - By the end of frame 1, only 2 backing stores were created.
1830/// - By the end of frame 2, only 3 backing stores were created. This ensures
1831/// that the backing store for the 2nd view is not reused for the 3rd view.
1832TEST_F(EmbedderTest, BackingStoresCorrespondToTheirViews) {
1833 constexpr FlutterViewId kSecondViewId = 123;
1834 constexpr FlutterViewId kThirdViewId = 456;
1835 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1836
1837 EmbedderConfigBuilder builder(context);
1838 builder.SetDartEntrypoint("render_all_views");
1839 builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
1840 builder.SetCompositor();
1841
1843 context.GetCompositor().GetGrContext(),
1844 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
1845
1846 // The variables needed by the callbacks of the compositor.
1847 struct CompositorUserData {
1849 // Each latch is signaled when its corresponding view is presented.
1850 fml::AutoResetWaitableEvent latch_implicit;
1851 fml::AutoResetWaitableEvent latch_second;
1852 fml::AutoResetWaitableEvent latch_third;
1853 // Whether the respective view should be rendered in the frame.
1854 bool second_expected;
1855 bool third_expected;
1856 // The total number of backing stores created to verify caching.
1857 int backing_stores_created;
1858 };
1859 CompositorUserData compositor_user_data{
1860 .producer = &producer,
1861 .backing_stores_created = 0,
1862 };
1863
1864 builder.GetCompositor() = FlutterCompositor{
1865 .struct_size = sizeof(FlutterCompositor),
1866 .user_data = reinterpret_cast<void*>(&compositor_user_data),
1867 .create_backing_store_callback =
1868 [](const FlutterBackingStoreConfig* config,
1869 FlutterBackingStore* backing_store_out, void* user_data) {
1870 // Verify that the backing store comes with the correct view ID.
1871 EXPECT_TRUE(config->view_id == 0 ||
1872 config->view_id == kSecondViewId ||
1873 config->view_id == kThirdViewId);
1874 auto compositor_user_data =
1875 reinterpret_cast<CompositorUserData*>(user_data);
1876 compositor_user_data->backing_stores_created += 1;
1877 bool result = compositor_user_data->producer->Create(
1878 config, backing_store_out);
1879 // The created backing store has a user_data that records the view
1880 // that the store is created for.
1881 backing_store_out->user_data =
1882 reinterpret_cast<void*>(config->view_id);
1883 return result;
1884 },
1885 .collect_backing_store_callback = [](const FlutterBackingStore* renderer,
1886 void* user_data) { return true; },
1887 .present_layers_callback = nullptr,
1888 .avoid_backing_store_cache = false,
1889 .present_view_callback =
1890 [](const FlutterPresentViewInfo* info) {
1891 EXPECT_EQ(info->layers_count, 1u);
1892 // Verify that the given layer's backing store has the same view ID
1893 // as the target view.
1894 int64_t store_view_id = reinterpret_cast<int64_t>(
1895 info->layers[0]->backing_store->user_data);
1896 EXPECT_EQ(store_view_id, info->view_id);
1897 auto compositor_user_data =
1898 reinterpret_cast<CompositorUserData*>(info->user_data);
1899 // Verify that the respective views are rendered.
1900 switch (info->view_id) {
1901 case 0:
1902 compositor_user_data->latch_implicit.Signal();
1903 break;
1904 case kSecondViewId:
1905 EXPECT_TRUE(compositor_user_data->second_expected);
1906 compositor_user_data->latch_second.Signal();
1907 break;
1908 case kThirdViewId:
1909 EXPECT_TRUE(compositor_user_data->third_expected);
1910 compositor_user_data->latch_third.Signal();
1911 break;
1912 default:
1914 }
1915 return true;
1916 },
1917 };
1918
1919 compositor_user_data.second_expected = true;
1920 compositor_user_data.third_expected = false;
1921
1922 /*=== First frame ===*/
1923
1924 auto engine = builder.LaunchEngine();
1925 ASSERT_TRUE(engine.is_valid());
1926
1927 // Give the implicit view a non-zero size so that it renders something.
1928 FlutterWindowMetricsEvent metrics_implicit = {
1930 .width = 800,
1931 .height = 600,
1932 .pixel_ratio = 1.0,
1933 .view_id = 0,
1934 };
1935 ASSERT_EQ(
1936 FlutterEngineSendWindowMetricsEvent(engine.get(), &metrics_implicit),
1937 kSuccess);
1938
1939 // Add the second view.
1940 FlutterWindowMetricsEvent metrics_add = {
1942 .width = 800,
1943 .height = 600,
1944 .pixel_ratio = 1.0,
1945 .view_id = kSecondViewId,
1946 };
1947
1948 FlutterAddViewInfo add_view_info = {};
1949 add_view_info.struct_size = sizeof(FlutterAddViewInfo);
1950 add_view_info.view_id = kSecondViewId;
1951 add_view_info.view_metrics = &metrics_add;
1952 add_view_info.add_view_callback = [](const FlutterAddViewResult* result) {
1953 ASSERT_TRUE(result->added);
1954 };
1955
1956 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
1957
1958 compositor_user_data.latch_implicit.Wait();
1959 compositor_user_data.latch_second.Wait();
1960
1961 /*=== Second frame ===*/
1962
1963 compositor_user_data.second_expected = false;
1964 compositor_user_data.third_expected = true;
1965 EXPECT_EQ(compositor_user_data.backing_stores_created, 2);
1966
1967 // Remove the second view
1968 FlutterRemoveViewInfo remove_view_info = {};
1969 remove_view_info.struct_size = sizeof(FlutterRemoveViewInfo);
1970 remove_view_info.view_id = kSecondViewId;
1971 remove_view_info.remove_view_callback =
1972 [](const FlutterRemoveViewResult* result) {
1973 ASSERT_TRUE(result->removed);
1974 };
1975 ASSERT_EQ(FlutterEngineRemoveView(engine.get(), &remove_view_info), kSuccess);
1976
1977 // Add the third view.
1978 add_view_info.view_id = kThirdViewId;
1979 metrics_add.view_id = kThirdViewId;
1980 ASSERT_EQ(FlutterEngineAddView(engine.get(), &add_view_info), kSuccess);
1981 // Adding the view should have scheduled a frame.
1982
1983 compositor_user_data.latch_implicit.Wait();
1984 compositor_user_data.latch_third.Wait();
1985 EXPECT_EQ(compositor_user_data.backing_stores_created, 3);
1986}
1987
1988TEST_F(EmbedderTest, CanUpdateLocales) {
1989 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
1990 EmbedderConfigBuilder builder(context);
1991 builder.SetSoftwareRendererConfig();
1992 builder.SetDartEntrypoint("can_receive_locale_updates");
1994 context.AddNativeCallback(
1995 "SignalNativeTest",
1997 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
1998
1999 fml::AutoResetWaitableEvent check_latch;
2000 context.AddNativeCallback(
2001 "SignalNativeCount",
2005 2);
2006 check_latch.Signal();
2007 }));
2008
2009 auto engine = builder.LaunchEngine();
2010 ASSERT_TRUE(engine.is_valid());
2011
2012 // Wait for the application to attach the listener.
2013 latch.Wait();
2014
2015 FlutterLocale locale1 = {};
2016 locale1.struct_size = sizeof(locale1);
2017 locale1.language_code = ""; // invalid
2018 locale1.country_code = "US";
2019 locale1.script_code = "";
2020 locale1.variant_code = nullptr;
2021
2022 FlutterLocale locale2 = {};
2023 locale2.struct_size = sizeof(locale2);
2024 locale2.language_code = "zh";
2025 locale2.country_code = "CN";
2026 locale2.script_code = "Hans";
2027 locale2.variant_code = nullptr;
2028
2029 std::vector<const FlutterLocale*> locales;
2030 locales.push_back(&locale1);
2031 locales.push_back(&locale2);
2032
2033 ASSERT_EQ(
2034 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2036
2037 // Fix the invalid code.
2038 locale1.language_code = "en";
2039
2040 ASSERT_EQ(
2041 FlutterEngineUpdateLocales(engine.get(), locales.data(), locales.size()),
2042 kSuccess);
2043
2044 check_latch.Wait();
2045}
2046
2047TEST_F(EmbedderTest, LocalizationCallbacksCalled) {
2048 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2050 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2051 EmbedderConfigBuilder builder(context);
2052 builder.SetSoftwareRendererConfig();
2053 auto engine = builder.LaunchEngine();
2054 ASSERT_TRUE(engine.is_valid());
2055 // Wait for the root isolate to launch.
2056 latch.Wait();
2057
2059 std::vector<std::string> supported_locales;
2060 supported_locales.push_back("es");
2061 supported_locales.push_back("MX");
2062 supported_locales.push_back("");
2063 auto result = shell.GetPlatformView()->ComputePlatformResolvedLocales(
2064 supported_locales);
2065
2066 ASSERT_EQ((*result).size(), supported_locales.size()); // 3
2067 ASSERT_EQ((*result)[0], supported_locales[0]);
2068 ASSERT_EQ((*result)[1], supported_locales[1]);
2069 ASSERT_EQ((*result)[2], supported_locales[2]);
2070
2071 engine.reset();
2072}
2073
2078
2079TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) {
2080 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2081
2082 EmbedderConfigBuilder builder(context);
2083 builder.SetSoftwareRendererConfig(SkISize::Make(1024, 600));
2084 builder.SetCompositor();
2085 builder.SetDartEntrypoint("verify_b143464703");
2086
2087 builder.SetRenderTargetType(
2088 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
2089
2090 // setup the screenshot promise.
2091 auto rendered_scene = context.GetNextSceneImage();
2092
2093 fml::CountDownLatch latch(1);
2094 context.GetCompositor().SetNextPresentCallback(
2095 [&](FlutterViewId view_id, const FlutterLayer** layers,
2096 size_t layers_count) {
2097 ASSERT_EQ(layers_count, 2u);
2098
2099 // Layer 0 (Root)
2100 {
2101 FlutterBackingStore backing_store = *layers[0]->backing_store;
2102 backing_store.type = kFlutterBackingStoreTypeSoftware;
2103 backing_store.did_update = true;
2104
2105 FlutterRect paint_region_rects[] = {
2106 FlutterRectMakeLTRB(0, 0, 1024, 600),
2107 };
2108 FlutterRegion paint_region = {
2109 .struct_size = sizeof(FlutterRegion),
2110 .rects_count = 1,
2111 .rects = paint_region_rects,
2112 };
2113 FlutterBackingStorePresentInfo present_info = {
2115 .paint_region = &paint_region,
2116 };
2117
2118 FlutterLayer layer = {};
2119 layer.struct_size = sizeof(layer);
2121 layer.backing_store = &backing_store;
2122 layer.size = FlutterSizeMake(1024.0, 600.0);
2123 layer.offset = FlutterPointMake(0.0, 0.0);
2124 layer.backing_store_present_info = &present_info;
2125
2126 ASSERT_EQ(*layers[0], layer);
2127 }
2128
2129 // Layer 1
2130 {
2133 platform_view.identifier = 42;
2134
2135 FlutterLayer layer = {};
2136 layer.struct_size = sizeof(layer);
2139 layer.size = FlutterSizeMake(1024.0, 540.0);
2140 layer.offset = FlutterPointMake(135.0, 60.0);
2141
2142 ASSERT_EQ(*layers[1], layer);
2143 }
2144
2145 latch.CountDown();
2146 });
2147
2148 context.GetCompositor().SetPlatformViewRendererCallback(
2149 [](const FlutterLayer& layer,
2150 GrDirectContext* context) -> sk_sp<SkImage> {
2152 layer, nullptr /* null because software compositor */);
2153 auto canvas = surface->getCanvas();
2154 FML_CHECK(canvas != nullptr);
2155
2156 switch (layer.platform_view->identifier) {
2157 case 42: {
2158 SkPaint paint;
2159 // See dart test for total order.
2160 paint.setColor(SK_ColorGREEN);
2161 paint.setAlpha(127);
2162 const auto& rect =
2163 SkRect::MakeWH(layer.size.width, layer.size.height);
2164 canvas->drawRect(rect, paint);
2165 } break;
2166 default:
2167 // Asked to render an unknown platform view.
2168 FML_CHECK(false)
2169 << "Test was asked to composite an unknown platform view.";
2170 }
2171
2172 return surface->makeImageSnapshot();
2173 });
2174
2175 auto engine = builder.LaunchEngine();
2176
2177 // Send a window metrics events so frames may be scheduled.
2178 FlutterWindowMetricsEvent event = {};
2179 event.struct_size = sizeof(event);
2180 event.width = 1024;
2181 event.height = 600;
2182 event.pixel_ratio = 1.0;
2183 event.physical_view_inset_top = 0.0;
2184 event.physical_view_inset_right = 0.0;
2185 event.physical_view_inset_bottom = 0.0;
2186 event.physical_view_inset_left = 0.0;
2188 kSuccess);
2189 ASSERT_TRUE(engine.is_valid());
2190
2191 // wait for scene to be rendered.
2192 latch.Wait();
2193
2194 // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
2195 // platforms.
2196#if !defined(FML_OS_LINUX)
2197 GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
2198#endif // FML_OS_LINUX
2199 ASSERT_TRUE(
2200 ImageMatchesFixture("verifyb143464703_soft_noxform.png", rendered_scene));
2201}
2202
2203TEST_F(EmbedderTest, CanSendLowMemoryNotification) {
2204 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2205
2206 EmbedderConfigBuilder builder(context);
2207 builder.SetSoftwareRendererConfig();
2208
2209 auto engine = builder.LaunchEngine();
2210
2211 ASSERT_TRUE(engine.is_valid());
2212
2213 // TODO(chinmaygarde): The shell ought to have a mechanism for notification
2214 // dispatch that engine subsystems can register handlers to. This would allow
2215 // the raster cache and the secondary context caches to respond to
2216 // notifications. Once that is in place, this test can be updated to actually
2217 // ensure that the dispatched message is visible to engine subsystems.
2219}
2220
2221TEST_F(EmbedderTest, CanPostTaskToAllNativeThreads) {
2223 size_t worker_count = 0;
2224 fml::AutoResetWaitableEvent sync_latch;
2225
2226 // One of the threads that the callback will be posted to is the platform
2227 // thread. So we cannot wait for assertions to complete on the platform
2228 // thread. Create a new thread to manage the engine instance and wait for
2229 // assertions on the test thread.
2230 auto platform_task_runner = CreateNewThread("platform_thread");
2231
2232 platform_task_runner->PostTask([&]() {
2233 auto& context =
2234 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2235
2236 EmbedderConfigBuilder builder(context);
2237 builder.SetSoftwareRendererConfig();
2238
2239 engine = builder.LaunchEngine();
2240
2241 ASSERT_TRUE(engine.is_valid());
2242
2243 worker_count = ToEmbedderEngine(engine.get())
2244 ->GetShell()
2245 .GetDartVM()
2247 ->GetWorkerCount();
2248
2249 sync_latch.Signal();
2250 });
2251
2252 sync_latch.Wait();
2253
2254 const auto engine_threads_count = worker_count + 4u;
2255
2256 struct Captures {
2257 // Waits the adequate number of callbacks to fire.
2258 fml::CountDownLatch latch;
2259
2260 // This class will be accessed from multiple threads concurrently to track
2261 // thread specific information that is later checked. All updates to fields
2262 // in this struct must be made with this mutex acquired.
2263
2264 std::mutex captures_mutex;
2265 // Ensures that the expect number of distinct threads were serviced.
2266 std::set<std::thread::id> thread_ids;
2267
2268 size_t platform_threads_count = 0;
2269 size_t render_threads_count = 0;
2270 size_t ui_threads_count = 0;
2271 size_t worker_threads_count = 0;
2272
2273 explicit Captures(size_t count) : latch(count) {}
2274 };
2275
2276 Captures captures(engine_threads_count);
2277
2278 platform_task_runner->PostTask([&]() {
2280 engine.get(),
2281 [](FlutterNativeThreadType type, void* baton) {
2282 auto captures = reinterpret_cast<Captures*>(baton);
2283 {
2284 std::scoped_lock lock(captures->captures_mutex);
2285 switch (type) {
2286 case kFlutterNativeThreadTypeRender:
2287 captures->render_threads_count++;
2288 break;
2289 case kFlutterNativeThreadTypeWorker:
2290 captures->worker_threads_count++;
2291 break;
2292 case kFlutterNativeThreadTypeUI:
2293 captures->ui_threads_count++;
2294 break;
2295 case kFlutterNativeThreadTypePlatform:
2296 captures->platform_threads_count++;
2297 break;
2298 }
2299 captures->thread_ids.insert(std::this_thread::get_id());
2300 }
2301 captures->latch.CountDown();
2302 },
2303 &captures),
2304 kSuccess);
2305 });
2306
2307 captures.latch.Wait();
2308 ASSERT_EQ(captures.thread_ids.size(), engine_threads_count);
2309 ASSERT_EQ(captures.platform_threads_count, 1u);
2310 ASSERT_EQ(captures.render_threads_count, 1u);
2311 ASSERT_EQ(captures.ui_threads_count, 1u);
2312 ASSERT_EQ(captures.worker_threads_count, worker_count + 1u /* for IO */);
2313
2314 platform_task_runner->PostTask([&]() {
2315 engine.reset();
2316 sync_latch.Signal();
2317 });
2318 sync_latch.Wait();
2319
2320 // The engine should have already been destroyed on the platform task runner.
2321 ASSERT_FALSE(engine.is_valid());
2322}
2323
2324TEST_F(EmbedderTest, InvalidAOTDataSourcesMustReturnError) {
2326 GTEST_SKIP();
2327 return;
2328 }
2329 FlutterEngineAOTDataSource data_in = {};
2330 FlutterEngineAOTData data_out = nullptr;
2331
2332 // Null source specified.
2333 ASSERT_EQ(FlutterEngineCreateAOTData(nullptr, &data_out), kInvalidArguments);
2334 ASSERT_EQ(data_out, nullptr);
2335
2336 // Null data_out specified.
2337 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, nullptr), kInvalidArguments);
2338
2339 // Invalid FlutterEngineAOTDataSourceType type specified.
2340 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2341 data_in.type = static_cast<FlutterEngineAOTDataSourceType>(-1);
2342 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2343 ASSERT_EQ(data_out, nullptr);
2344
2345 // Invalid ELF path specified.
2347 data_in.elf_path = nullptr;
2348 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2349 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2350 ASSERT_EQ(data_in.elf_path, nullptr);
2351 ASSERT_EQ(data_out, nullptr);
2352
2353 // Invalid ELF path specified.
2354 data_in.elf_path = "";
2355 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2356 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2357 ASSERT_EQ(data_in.elf_path, "");
2358 ASSERT_EQ(data_out, nullptr);
2359
2360 // Could not find VM snapshot data.
2361 data_in.elf_path = "/bin/true";
2362 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2363 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2364 ASSERT_EQ(data_in.elf_path, "/bin/true");
2365 ASSERT_EQ(data_out, nullptr);
2366}
2367
2368TEST_F(EmbedderTest, MustNotRunWithMultipleAOTSources) {
2370 GTEST_SKIP();
2371 return;
2372 }
2373 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2374
2375 EmbedderConfigBuilder builder(
2376 context,
2377 EmbedderConfigBuilder::InitializationPreference::kMultiAOTInitialize);
2378
2379 builder.SetSoftwareRendererConfig();
2380
2381 auto engine = builder.LaunchEngine();
2382 ASSERT_FALSE(engine.is_valid());
2383}
2384
2385TEST_F(EmbedderTest, CanCreateAndCollectAValidElfSource) {
2387 GTEST_SKIP();
2388 return;
2389 }
2390 FlutterEngineAOTDataSource data_in = {};
2391 FlutterEngineAOTData data_out = nullptr;
2392
2393 // Collecting a null object should be allowed
2394 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2395
2396 const auto elf_path =
2398
2400 data_in.elf_path = elf_path.c_str();
2401
2402 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kSuccess);
2403 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2404 ASSERT_EQ(data_in.elf_path, elf_path.c_str());
2405 ASSERT_NE(data_out, nullptr);
2406
2407 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2408}
2409
2410TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) {
2412 GTEST_SKIP();
2413 return;
2414 }
2415 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2416
2418 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2419
2420 EmbedderConfigBuilder builder(
2421 context,
2422 EmbedderConfigBuilder::InitializationPreference::kAOTDataInitialize);
2423
2424 builder.SetSoftwareRendererConfig();
2425
2426 auto engine = builder.LaunchEngine();
2427 ASSERT_TRUE(engine.is_valid());
2428
2429 // Wait for the root isolate to launch.
2430 latch.Wait();
2431 engine.reset();
2432}
2433
2434#if defined(__clang_analyzer__)
2435#define TEST_VM_SNAPSHOT_DATA nullptr
2436#define TEST_VM_SNAPSHOT_INSTRUCTIONS nullptr
2437#define TEST_ISOLATE_SNAPSHOT_DATA nullptr
2438#define TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS nullptr
2439#endif
2440
2441//------------------------------------------------------------------------------
2442/// PopulateJITSnapshotMappingCallbacks should successfully change the callbacks
2443/// of the snapshots in the engine's settings when JIT snapshots are explicitly
2444/// defined.
2445///
2446TEST_F(EmbedderTest, CanSuccessfullyPopulateSpecificJITSnapshotCallbacks) {
2447// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2448#if defined(OS_FUCHSIA)
2449 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2450#else
2451
2452 // This test is only relevant in JIT mode.
2454 GTEST_SKIP();
2455 return;
2456 }
2457
2458 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2459 EmbedderConfigBuilder builder(context);
2460 builder.SetSoftwareRendererConfig();
2461
2462 // Construct the location of valid JIT snapshots.
2463 const std::string src_path = GetSourcePath();
2464 const std::string vm_snapshot_data =
2465 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2466 const std::string vm_snapshot_instructions =
2467 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2468 const std::string isolate_snapshot_data =
2469 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2470 const std::string isolate_snapshot_instructions =
2471 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2472
2473 // Explicitly define the locations of the JIT snapshots
2474 builder.GetProjectArgs().vm_snapshot_data =
2475 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2476 builder.GetProjectArgs().vm_snapshot_instructions =
2477 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2478 builder.GetProjectArgs().isolate_snapshot_data =
2479 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2480 builder.GetProjectArgs().isolate_snapshot_instructions =
2481 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2482
2483 auto engine = builder.LaunchEngine();
2484
2486 const Settings settings = shell.GetSettings();
2487
2488 ASSERT_NE(settings.vm_snapshot_data(), nullptr);
2489 ASSERT_NE(settings.vm_snapshot_instr(), nullptr);
2490 ASSERT_NE(settings.isolate_snapshot_data(), nullptr);
2491 ASSERT_NE(settings.isolate_snapshot_instr(), nullptr);
2492 ASSERT_NE(settings.dart_library_sources_kernel(), nullptr);
2493#endif // OS_FUCHSIA
2494}
2495
2496//------------------------------------------------------------------------------
2497/// PopulateJITSnapshotMappingCallbacks should still be able to successfully
2498/// change the callbacks of the snapshots in the engine's settings when JIT
2499/// snapshots are explicitly defined. However, if those snapshot locations are
2500/// invalid, the callbacks should return a nullptr.
2501///
2502TEST_F(EmbedderTest, JITSnapshotCallbacksFailWithInvalidLocation) {
2503// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2504#if defined(OS_FUCHSIA)
2505 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2506#else
2507
2508 // This test is only relevant in JIT mode.
2510 GTEST_SKIP();
2511 return;
2512 }
2513
2514 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2515 EmbedderConfigBuilder builder(context);
2516 builder.SetSoftwareRendererConfig();
2517
2518 // Explicitly define the locations of the invalid JIT snapshots
2519 builder.GetProjectArgs().vm_snapshot_data =
2520 reinterpret_cast<const uint8_t*>("invalid_vm_data");
2521 builder.GetProjectArgs().vm_snapshot_instructions =
2522 reinterpret_cast<const uint8_t*>("invalid_vm_instructions");
2523 builder.GetProjectArgs().isolate_snapshot_data =
2524 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2525 builder.GetProjectArgs().isolate_snapshot_instructions =
2526 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2527
2528 auto engine = builder.LaunchEngine();
2529
2531 const Settings settings = shell.GetSettings();
2532
2533 ASSERT_EQ(settings.vm_snapshot_data(), nullptr);
2534 ASSERT_EQ(settings.vm_snapshot_instr(), nullptr);
2535 ASSERT_EQ(settings.isolate_snapshot_data(), nullptr);
2536 ASSERT_EQ(settings.isolate_snapshot_instr(), nullptr);
2537#endif // OS_FUCHSIA
2538}
2539
2540//------------------------------------------------------------------------------
2541/// The embedder must be able to run explicitly specified snapshots in JIT mode
2542/// (i.e. when those are present in known locations).
2543///
2544TEST_F(EmbedderTest, CanLaunchEngineWithSpecifiedJITSnapshots) {
2545 // This test is only relevant in JIT mode.
2547 GTEST_SKIP();
2548 return;
2549 }
2550
2551 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2552 EmbedderConfigBuilder builder(context);
2553 builder.SetSoftwareRendererConfig();
2554
2555 // Construct the location of valid JIT snapshots.
2556 const std::string src_path = GetSourcePath();
2557 const std::string vm_snapshot_data =
2558 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2559 const std::string vm_snapshot_instructions =
2560 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2561 const std::string isolate_snapshot_data =
2562 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2563 const std::string isolate_snapshot_instructions =
2564 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2565
2566 // Explicitly define the locations of the JIT snapshots
2567 builder.GetProjectArgs().vm_snapshot_data =
2568 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2569 builder.GetProjectArgs().vm_snapshot_instructions =
2570 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2571 builder.GetProjectArgs().isolate_snapshot_data =
2572 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2573 builder.GetProjectArgs().isolate_snapshot_instructions =
2574 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2575
2576 auto engine = builder.LaunchEngine();
2577 ASSERT_TRUE(engine.is_valid());
2578}
2579
2580//------------------------------------------------------------------------------
2581/// The embedder must be able to run in JIT mode when only some snapshots are
2582/// specified.
2583///
2584TEST_F(EmbedderTest, CanLaunchEngineWithSomeSpecifiedJITSnapshots) {
2585 // This test is only relevant in JIT mode.
2587 GTEST_SKIP();
2588 return;
2589 }
2590
2591 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2592 EmbedderConfigBuilder builder(context);
2593 builder.SetSoftwareRendererConfig();
2594
2595 // Construct the location of valid JIT snapshots.
2596 const std::string src_path = GetSourcePath();
2597 const std::string vm_snapshot_data =
2598 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2599 const std::string vm_snapshot_instructions =
2600 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2601
2602 // Explicitly define the locations of the JIT snapshots
2603 builder.GetProjectArgs().vm_snapshot_data =
2604 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2605 builder.GetProjectArgs().vm_snapshot_instructions =
2606 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2607
2608 auto engine = builder.LaunchEngine();
2609 ASSERT_TRUE(engine.is_valid());
2610}
2611
2612//------------------------------------------------------------------------------
2613/// The embedder must be able to run in JIT mode even when the specfied
2614/// snapshots are invalid. It should be able to resolve them as it would when
2615/// the snapshots are not specified.
2616///
2617TEST_F(EmbedderTest, CanLaunchEngineWithInvalidJITSnapshots) {
2618 // This test is only relevant in JIT mode.
2620 GTEST_SKIP();
2621 return;
2622 }
2623
2624 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2625 EmbedderConfigBuilder builder(context);
2626 builder.SetSoftwareRendererConfig();
2627
2628 // Explicitly define the locations of the JIT snapshots
2629 builder.GetProjectArgs().isolate_snapshot_data =
2630 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2631 builder.GetProjectArgs().isolate_snapshot_instructions =
2632 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2633
2634 auto engine = builder.LaunchEngine();
2635 ASSERT_TRUE(engine.is_valid());
2637}
2638
2639//------------------------------------------------------------------------------
2640/// The embedder must be able to launch even when the snapshots are not
2641/// explicitly defined in JIT mode. It must be able to resolve those snapshots.
2642///
2643TEST_F(EmbedderTest, CanLaunchEngineWithUnspecifiedJITSnapshots) {
2644 // This test is only relevant in JIT mode.
2646 GTEST_SKIP();
2647 return;
2648 }
2649
2650 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2651 EmbedderConfigBuilder builder(context);
2652 builder.SetSoftwareRendererConfig();
2653
2654 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_data, nullptr);
2655 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_instructions, nullptr);
2656 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_data, nullptr);
2657 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_instructions, nullptr);
2658
2659 auto engine = builder.LaunchEngine();
2660 ASSERT_TRUE(engine.is_valid());
2661}
2662
2663TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) {
2664 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2665 EmbedderConfigBuilder builder(context);
2666 builder.SetSoftwareRendererConfig();
2667 auto engine = builder.LaunchEngine();
2668
2669 ASSERT_TRUE(engine.is_valid());
2670
2671 FlutterWindowMetricsEvent event = {};
2672 event.struct_size = sizeof(event);
2673 event.width = 800;
2674 event.height = 600;
2675 event.pixel_ratio = 0.0;
2676 event.physical_view_inset_top = 0.0;
2677 event.physical_view_inset_right = 0.0;
2678 event.physical_view_inset_bottom = 0.0;
2679 event.physical_view_inset_left = 0.0;
2680
2681 // Pixel ratio must be positive.
2684
2685 event.pixel_ratio = 1.0;
2686 event.physical_view_inset_top = -1.0;
2687 event.physical_view_inset_right = -1.0;
2688 event.physical_view_inset_bottom = -1.0;
2689 event.physical_view_inset_left = -1.0;
2690
2691 // Physical view insets must be non-negative.
2694
2695 event.physical_view_inset_top = 700;
2696 event.physical_view_inset_right = 900;
2697 event.physical_view_inset_bottom = 700;
2698 event.physical_view_inset_left = 900;
2699
2700 // Top/bottom insets cannot be greater than height.
2701 // Left/right insets cannot be greater than width.
2704}
2705
2708 std::string entrypoint,
2710 const std::vector<uint8_t>& bytes) {
2711 auto& context =
2712 test.GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2713
2714 EmbedderConfigBuilder builder(context);
2716 bool matches = false;
2717
2718 builder.SetSoftwareRendererConfig();
2719 builder.SetCompositor();
2720 builder.SetDartEntrypoint(std::move(entrypoint));
2721 builder.SetRenderTargetType(
2722 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer2,
2723 pixfmt);
2724
2725 auto engine = builder.LaunchEngine();
2726 ASSERT_TRUE(engine.is_valid());
2727
2728 context.GetCompositor().SetNextPresentCallback(
2729 [&matches, &bytes, &latch](FlutterViewId view_id,
2730 const FlutterLayer** layers,
2731 size_t layers_count) {
2732 ASSERT_EQ(layers[0]->type, kFlutterLayerContentTypeBackingStore);
2733 ASSERT_EQ(layers[0]->backing_store->type,
2736 static_cast<SkSurface*>(
2737 layers[0]->backing_store->software2.user_data),
2738 bytes);
2739 latch.Signal();
2740 });
2741
2742 // Send a window metrics events so frames may be scheduled.
2743 FlutterWindowMetricsEvent event = {};
2744 event.struct_size = sizeof(event);
2745 event.width = 1;
2746 event.height = 1;
2747 event.pixel_ratio = 1.0;
2749 kSuccess);
2750
2751 latch.Wait();
2752 ASSERT_TRUE(matches);
2753
2754 engine.reset();
2755}
2756
2757template <typename T>
2760 std::string entrypoint,
2762 T pixelvalue) {
2763 uint8_t* bytes = reinterpret_cast<uint8_t*>(&pixelvalue);
2765 test, std::move(entrypoint), pixfmt,
2766 std::vector<uint8_t>(bytes, bytes + sizeof(T)));
2767}
2768
2769#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher) \
2770 TEST_F(EmbedderTest, SoftwareRenderingPixelFormats##test_name) { \
2771 expectSoftwareRenderingOutputMatches(*this, #dart_entrypoint, pixfmt, \
2772 matcher); \
2773 }
2774
2775// Don't test the pixel formats that contain padding (so an X) and the
2776// kFlutterSoftwarePixelFormatNative32 pixel format here, so we don't add any
2777// flakiness.
2778SW_PIXFMT_TEST_F(RedRGBA565xF800,
2779 draw_solid_red,
2781 (uint16_t)0xF800);
2782SW_PIXFMT_TEST_F(RedRGBA4444xF00F,
2783 draw_solid_red,
2785 (uint16_t)0xF00F);
2786SW_PIXFMT_TEST_F(RedRGBA8888xFFx00x00xFF,
2787 draw_solid_red,
2789 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
2790SW_PIXFMT_TEST_F(RedBGRA8888x00x00xFFxFF,
2791 draw_solid_red,
2793 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
2795 draw_solid_red,
2797 (uint8_t)0x36);
2798
2799SW_PIXFMT_TEST_F(GreenRGB565x07E0,
2800 draw_solid_green,
2802 (uint16_t)0x07E0);
2803SW_PIXFMT_TEST_F(GreenRGBA4444x0F0F,
2804 draw_solid_green,
2806 (uint16_t)0x0F0F);
2807SW_PIXFMT_TEST_F(GreenRGBA8888x00xFFx00xFF,
2808 draw_solid_green,
2810 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
2811SW_PIXFMT_TEST_F(GreenBGRA8888x00xFFx00xFF,
2812 draw_solid_green,
2814 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
2815SW_PIXFMT_TEST_F(GreenGray8xB6,
2816 draw_solid_green,
2818 (uint8_t)0xB6);
2819
2820SW_PIXFMT_TEST_F(BlueRGB565x001F,
2821 draw_solid_blue,
2823 (uint16_t)0x001F);
2824SW_PIXFMT_TEST_F(BlueRGBA4444x00FF,
2825 draw_solid_blue,
2827 (uint16_t)0x00FF);
2828SW_PIXFMT_TEST_F(BlueRGBA8888x00x00xFFxFF,
2829 draw_solid_blue,
2831 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
2832SW_PIXFMT_TEST_F(BlueBGRA8888xFFx00x00xFF,
2833 draw_solid_blue,
2835 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
2836SW_PIXFMT_TEST_F(BlueGray8x12,
2837 draw_solid_blue,
2839 (uint8_t)0x12);
2840
2841//------------------------------------------------------------------------------
2842// Key Data
2843//------------------------------------------------------------------------------
2844
2845typedef struct {
2846 std::shared_ptr<fml::AutoResetWaitableEvent> latch;
2849
2850// Convert `kind` in integer form to its enum form.
2851//
2852// It performs a revesed mapping from `_serializeKeyEventType`
2853// in shell/platform/embedder/fixtures/main.dart.
2855 switch (kind) {
2856 case 1:
2858 case 2:
2860 case 3:
2862 default:
2865 }
2866}
2867
2868// Convert `source` in integer form to its enum form.
2869//
2870// It performs a revesed mapping from `_serializeKeyEventDeviceType`
2871// in shell/platform/embedder/fixtures/main.dart.
2873 switch (source) {
2874 case 1:
2876 case 2:
2878 case 3:
2880 case 4:
2882 case 5:
2884 default:
2887 }
2888}
2889
2890// Checks the equality of two `FlutterKeyEvent` by each of their members except
2891// for `character`. The `character` must be checked separately.
2893 const FlutterKeyEvent& baseline) {
2894 EXPECT_EQ(subject.timestamp, baseline.timestamp);
2895 EXPECT_EQ(subject.type, baseline.type);
2896 EXPECT_EQ(subject.physical, baseline.physical);
2897 EXPECT_EQ(subject.logical, baseline.logical);
2898 EXPECT_EQ(subject.synthesized, baseline.synthesized);
2899 EXPECT_EQ(subject.device_type, baseline.device_type);
2900}
2901
2902TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) {
2903 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
2904 uint64_t echoed_char;
2905 FlutterKeyEvent echoed_event;
2906 echoed_event.struct_size = sizeof(FlutterKeyEvent);
2907
2908 auto native_echo_event = [&](Dart_NativeArguments args) {
2909 echoed_event.type =
2912 echoed_event.timestamp =
2913 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
2921 echoed_event.synthesized =
2923 echoed_event.device_type =
2926
2927 message_latch->Signal();
2928 };
2929
2930 auto platform_task_runner = CreateNewThread("platform_thread");
2931
2934 platform_task_runner->PostTask([&]() {
2935 auto& context =
2936 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2937 EmbedderConfigBuilder builder(context);
2938 builder.SetSoftwareRendererConfig();
2939 builder.SetDartEntrypoint("key_data_echo");
2940 builder.SetPlatformMessageCallback(
2941 [&](const FlutterPlatformMessage* message) {
2943 engine.get(), message->response_handle, nullptr, 0);
2944 });
2945 context.AddNativeCallback(
2946 "SignalNativeTest",
2948 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
2949
2950 context.AddNativeCallback("EchoKeyEvent",
2951 CREATE_NATIVE_ENTRY(native_echo_event));
2952
2953 engine = builder.LaunchEngine();
2954 ASSERT_TRUE(engine.is_valid());
2955 });
2956
2957 ready.Wait();
2958
2959 // A normal down event
2960 const FlutterKeyEvent down_event_upper_a{
2961 .struct_size = sizeof(FlutterKeyEvent),
2962 .timestamp = 1,
2964 .physical = 0x00070004,
2965 .logical = 0x00000000061,
2966 .character = "A",
2967 .synthesized = false,
2969 };
2970 platform_task_runner->PostTask([&]() {
2971 FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, nullptr,
2972 nullptr);
2973 });
2974 message_latch->Wait();
2975
2976 ExpectKeyEventEq(echoed_event, down_event_upper_a);
2977 EXPECT_EQ(echoed_char, 0x41llu);
2978
2979 // A repeat event with multi-byte character
2980 const FlutterKeyEvent repeat_event_wide_char{
2981 .struct_size = sizeof(FlutterKeyEvent),
2982 .timestamp = 1000,
2984 .physical = 0x00070005,
2985 .logical = 0x00000000062,
2986 .character = "∆",
2987 .synthesized = false,
2989 };
2990 platform_task_runner->PostTask([&]() {
2991 FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, nullptr,
2992 nullptr);
2993 });
2994 message_latch->Wait();
2995
2996 ExpectKeyEventEq(echoed_event, repeat_event_wide_char);
2997 EXPECT_EQ(echoed_char, 0x2206llu);
2998
2999 // An up event with no character, synthesized
3000 const FlutterKeyEvent up_event{
3001 .struct_size = sizeof(FlutterKeyEvent),
3002 .timestamp = 1000000,
3004 .physical = 0x00070006,
3005 .logical = 0x00000000063,
3006 .character = nullptr,
3007 .synthesized = true,
3009 };
3010 platform_task_runner->PostTask([&]() {
3011 FlutterEngineSendKeyEvent(engine.get(), &up_event, nullptr, nullptr);
3012 });
3013 message_latch->Wait();
3014
3015 ExpectKeyEventEq(echoed_event, up_event);
3016 EXPECT_EQ(echoed_char, 0llu);
3017
3018 fml::AutoResetWaitableEvent shutdown_latch;
3019 platform_task_runner->PostTask([&]() {
3020 engine.reset();
3021 shutdown_latch.Signal();
3022 });
3023 shutdown_latch.Wait();
3024}
3025
3026TEST_F(EmbedderTest, KeyDataAreBuffered) {
3027 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3028 std::vector<FlutterKeyEvent> echoed_events;
3029
3030 auto native_echo_event = [&](Dart_NativeArguments args) {
3031 echoed_events.push_back(FlutterKeyEvent{
3032 .timestamp =
3033 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3035 .type =
3044 .device_type = UnserializeKeyEventDeviceType(
3047 });
3048
3049 message_latch->Signal();
3050 };
3051
3052 auto platform_task_runner = CreateNewThread("platform_thread");
3053
3056 platform_task_runner->PostTask([&]() {
3057 auto& context =
3058 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3059 EmbedderConfigBuilder builder(context);
3060 builder.SetSoftwareRendererConfig();
3061 builder.SetDartEntrypoint("key_data_late_echo");
3062 builder.SetPlatformMessageCallback(
3063 [&](const FlutterPlatformMessage* message) {
3065 engine.get(), message->response_handle, nullptr, 0);
3066 });
3067 context.AddNativeCallback(
3068 "SignalNativeTest",
3070 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3071
3072 context.AddNativeCallback("EchoKeyEvent",
3073 CREATE_NATIVE_ENTRY(native_echo_event));
3074
3075 engine = builder.LaunchEngine();
3076 ASSERT_TRUE(engine.is_valid());
3077 });
3078 ready.Wait();
3079
3080 FlutterKeyEvent sample_event{
3081 .struct_size = sizeof(FlutterKeyEvent),
3083 .physical = 0x00070004,
3084 .logical = 0x00000000061,
3085 .character = "A",
3086 .synthesized = false,
3088 };
3089
3090 // Send an event.
3091 sample_event.timestamp = 1.0l;
3092 platform_task_runner->PostTask([&]() {
3093 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3094 message_latch->Signal();
3095 });
3096 message_latch->Wait();
3097
3098 // Should not receive echos because the callback is not set yet.
3099 EXPECT_EQ(echoed_events.size(), 0u);
3100
3101 // Send an empty message to 'test/starts_echo' to start echoing.
3102 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
3104 engine.get(), [](const uint8_t* data, size_t size, void* user_data) {},
3105 nullptr, &response_handle);
3106
3109 .channel = "test/starts_echo",
3110 .message = nullptr,
3111 .message_size = 0,
3112 .response_handle = response_handle,
3113 };
3114
3115 platform_task_runner->PostTask([&]() {
3118 ASSERT_EQ(result, kSuccess);
3119
3121 });
3122
3123 // message_latch->Wait();
3124 message_latch->Wait();
3125 // All previous events should be received now.
3126 EXPECT_EQ(echoed_events.size(), 1u);
3127
3128 // Send a second event.
3129 sample_event.timestamp = 10.0l;
3130 platform_task_runner->PostTask([&]() {
3131 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3132 });
3133 message_latch->Wait();
3134
3135 // The event should be echoed, too.
3136 EXPECT_EQ(echoed_events.size(), 2u);
3137
3138 fml::AutoResetWaitableEvent shutdown_latch;
3139 platform_task_runner->PostTask([&]() {
3140 engine.reset();
3141 shutdown_latch.Signal();
3142 });
3143 shutdown_latch.Wait();
3144}
3145
3146TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) {
3148 fml::AutoResetWaitableEvent sync_latch;
3150
3151 // One of the threads that the key data callback will be posted to is the
3152 // platform thread. So we cannot wait for assertions to complete on the
3153 // platform thread. Create a new thread to manage the engine instance and wait
3154 // for assertions on the test thread.
3155 auto platform_task_runner = CreateNewThread("platform_thread");
3156
3157 platform_task_runner->PostTask([&]() {
3158 auto& context =
3159 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3160 EmbedderConfigBuilder builder(context);
3161 builder.SetSoftwareRendererConfig();
3162 builder.SetDartEntrypoint("key_data_echo");
3163 context.AddNativeCallback(
3164 "SignalNativeTest",
3166 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3167 context.AddNativeCallback(
3168 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3169
3170 engine = builder.LaunchEngine();
3171 ASSERT_TRUE(engine.is_valid());
3172
3173 sync_latch.Signal();
3174 });
3175 sync_latch.Wait();
3176 ready.Wait();
3177
3178 // Dispatch a single event
3179 FlutterKeyEvent event{
3180 .struct_size = sizeof(FlutterKeyEvent),
3181 .timestamp = 1000,
3183 .physical = 0x00070005,
3184 .logical = 0x00000000062,
3185 .character = nullptr,
3187 };
3188
3189 KeyEventUserData user_data1{
3190 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3191 };
3192 // Entrypoint `key_data_echo` returns `event.synthesized` as `handled`.
3193 event.synthesized = true;
3194 platform_task_runner->PostTask([&]() {
3195 // Test when the response callback is empty.
3196 // It should not cause a crash.
3197 FlutterEngineSendKeyEvent(engine.get(), &event, nullptr, nullptr);
3198
3199 // Test when the response callback is non-empty.
3200 // It should be invoked (so that the latch can be unlocked.)
3202 engine.get(), &event,
3203 [](bool handled, void* untyped_user_data) {
3204 KeyEventUserData* user_data =
3205 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3206 EXPECT_EQ(handled, true);
3207 user_data->latch->Signal();
3208 },
3209 &user_data1);
3210 });
3211 user_data1.latch->Wait();
3212 fml::AutoResetWaitableEvent shutdown_latch;
3213 platform_task_runner->PostTask([&]() {
3214 engine.reset();
3215 shutdown_latch.Signal();
3216 });
3217 shutdown_latch.Wait();
3218}
3219
3220TEST_F(EmbedderTest, BackToBackKeyEventResponsesCorrectlyInvoked) {
3222 fml::AutoResetWaitableEvent sync_latch;
3224
3225 // One of the threads that the callback will be posted to is the platform
3226 // thread. So we cannot wait for assertions to complete on the platform
3227 // thread. Create a new thread to manage the engine instance and wait for
3228 // assertions on the test thread.
3229 auto platform_task_runner = CreateNewThread("platform_thread");
3230
3231 platform_task_runner->PostTask([&]() {
3232 auto& context =
3233 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3234
3235 EmbedderConfigBuilder builder(context);
3236 builder.SetSoftwareRendererConfig();
3237 builder.SetDartEntrypoint("key_data_echo");
3238 context.AddNativeCallback(
3239 "SignalNativeTest",
3241 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3242
3243 context.AddNativeCallback(
3244 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3245
3246 engine = builder.LaunchEngine();
3247 ASSERT_TRUE(engine.is_valid());
3248
3249 sync_latch.Signal();
3250 });
3251 sync_latch.Wait();
3252 ready.Wait();
3253
3254 // Dispatch a single event
3255 FlutterKeyEvent event{
3256 .struct_size = sizeof(FlutterKeyEvent),
3257 .timestamp = 1000,
3259 .physical = 0x00070005,
3260 .logical = 0x00000000062,
3261 .character = nullptr,
3262 .synthesized = false,
3264 };
3265
3266 // Dispatch two events back to back, using the same callback on different
3267 // user_data
3268 KeyEventUserData user_data2{
3269 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3270 .returned = false,
3271 };
3272 KeyEventUserData user_data3{
3273 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3274 .returned = false,
3275 };
3276 auto callback23 = [](bool handled, void* untyped_user_data) {
3278 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3279 EXPECT_EQ(handled, false);
3280 user_data->returned = true;
3281 user_data->latch->Signal();
3282 };
3283 platform_task_runner->PostTask([&]() {
3284 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2);
3285 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3);
3286 });
3287 user_data2.latch->Wait();
3288 user_data3.latch->Wait();
3289
3290 EXPECT_TRUE(user_data2.returned);
3291 EXPECT_TRUE(user_data3.returned);
3292
3293 fml::AutoResetWaitableEvent shutdown_latch;
3294 platform_task_runner->PostTask([&]() {
3295 engine.reset();
3296 shutdown_latch.Signal();
3297 });
3298 shutdown_latch.Wait();
3299}
3300
3301//------------------------------------------------------------------------------
3302// Vsync waiter
3303//------------------------------------------------------------------------------
3304
3305// This test schedules a frame for the future and asserts that vsync waiter
3306// posts the event at the right frame start time (which is in the future).
3307TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) {
3309 fml::AutoResetWaitableEvent present_latch;
3310 fml::AutoResetWaitableEvent vsync_latch;
3311
3312 // One of the threads that the callback (FlutterEngineOnVsync) will be posted
3313 // to is the platform thread. So we cannot wait for assertions to complete on
3314 // the platform thread. Create a new thread to manage the engine instance and
3315 // wait for assertions on the test thread.
3316 auto platform_task_runner = CreateNewThread("platform_thread");
3317
3318 platform_task_runner->PostTask([&]() {
3319 auto& context =
3320 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3321
3322 context.SetVsyncCallback([&](intptr_t baton) {
3323 platform_task_runner->PostTask([baton = baton, &engine, &vsync_latch]() {
3324 FlutterEngineOnVsync(engine.get(), baton, NanosFromEpoch(16),
3325 NanosFromEpoch(32));
3326 vsync_latch.Signal();
3327 });
3328 });
3329 context.AddNativeCallback(
3330 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3331 present_latch.Signal();
3332 }));
3333
3334 EmbedderConfigBuilder builder(context);
3335 builder.SetSoftwareRendererConfig();
3336 builder.SetupVsyncCallback();
3337 builder.SetDartEntrypoint("empty_scene");
3338 engine = builder.LaunchEngine();
3339 ASSERT_TRUE(engine.is_valid());
3340
3341 // Send a window metrics events so frames may be scheduled.
3342 FlutterWindowMetricsEvent event = {};
3343 event.struct_size = sizeof(event);
3344 event.width = 800;
3345 event.height = 600;
3346 event.pixel_ratio = 1.0;
3347
3349 kSuccess);
3350 });
3351
3352 vsync_latch.Wait();
3353 present_latch.Wait();
3354
3355 fml::AutoResetWaitableEvent shutdown_latch;
3356 platform_task_runner->PostTask([&]() {
3357 engine.reset();
3358 shutdown_latch.Signal();
3359 });
3360 shutdown_latch.Wait();
3361}
3362
3363TEST_F(EmbedderTest, CanScheduleFrame) {
3364 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3365 EmbedderConfigBuilder builder(context);
3366 builder.SetSoftwareRendererConfig();
3367 builder.SetDartEntrypoint("can_schedule_frame");
3369 context.AddNativeCallback(
3370 "SignalNativeTest",
3372 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
3373
3374 fml::AutoResetWaitableEvent check_latch;
3375 context.AddNativeCallback(
3376 "SignalNativeCount",
3378 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
3379
3380 auto engine = builder.LaunchEngine();
3381 ASSERT_TRUE(engine.is_valid());
3382
3383 // Wait for the application to attach the listener.
3384 latch.Wait();
3385
3386 ASSERT_EQ(FlutterEngineScheduleFrame(engine.get()), kSuccess);
3387
3388 check_latch.Wait();
3389}
3390
3391TEST_F(EmbedderTest, CanSetNextFrameCallback) {
3392 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3393 EmbedderConfigBuilder builder(context);
3394 builder.SetSoftwareRendererConfig();
3395 builder.SetDartEntrypoint("draw_solid_red");
3396
3397 auto engine = builder.LaunchEngine();
3398 ASSERT_TRUE(engine.is_valid());
3399
3400 // Register the callback that is executed once the next frame is drawn.
3401 fml::AutoResetWaitableEvent callback_latch;
3402 VoidCallback callback = [](void* user_data) {
3403 fml::AutoResetWaitableEvent* callback_latch =
3405
3406 callback_latch->Signal();
3407 };
3408
3410 &callback_latch);
3411 ASSERT_EQ(result, kSuccess);
3412
3413 // Send a window metrics events so frames may be scheduled.
3414 FlutterWindowMetricsEvent event = {};
3415 event.struct_size = sizeof(event);
3416 event.width = 800;
3417 event.height = 600;
3418 event.pixel_ratio = 1.0;
3419 event.physical_view_inset_top = 0.0;
3420 event.physical_view_inset_right = 0.0;
3421 event.physical_view_inset_bottom = 0.0;
3422 event.physical_view_inset_left = 0.0;
3424 kSuccess);
3425
3426 callback_latch.Wait();
3427}
3428
3429#if defined(FML_OS_MACOSX)
3430
3431static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
3432 pthread_t tid = pthread_self();
3433 struct sched_param param;
3434 int policy = SCHED_OTHER;
3435 switch (config.priority) {
3437 param.sched_priority = 10;
3438 break;
3439 default:
3440 param.sched_priority = 1;
3441 }
3442 pthread_setschedparam(tid, policy, &param);
3443}
3444
3445TEST_F(EmbedderTest, EmbedderThreadHostUseCustomThreadConfig) {
3446 auto thread_host =
3448 nullptr, MockThreadConfigSetter);
3449
3451 int ui_policy;
3452 struct sched_param ui_param;
3453
3454 thread_host->GetTaskRunners().GetUITaskRunner()->PostTask([&] {
3455 pthread_t current_thread = pthread_self();
3456 pthread_getschedparam(current_thread, &ui_policy, &ui_param);
3457 ASSERT_EQ(ui_param.sched_priority, 10);
3458 ui_latch.Signal();
3459 });
3460
3462 int io_policy;
3463 struct sched_param io_param;
3464 thread_host->GetTaskRunners().GetIOTaskRunner()->PostTask([&] {
3465 pthread_t current_thread = pthread_self();
3466 pthread_getschedparam(current_thread, &io_policy, &io_param);
3467 ASSERT_EQ(io_param.sched_priority, 1);
3468 io_latch.Signal();
3469 });
3470
3471 ui_latch.Wait();
3472 io_latch.Wait();
3473}
3474#endif
3475
3476/// Send a pointer event to Dart and wait until the Dart code signals
3477/// it received the event.
3478TEST_F(EmbedderTest, CanSendPointer) {
3479 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3480 EmbedderConfigBuilder builder(context);
3481 builder.SetSoftwareRendererConfig();
3482 builder.SetDartEntrypoint("pointer_data_packet");
3483
3484 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
3485 context.AddNativeCallback(
3486 "SignalNativeTest",
3488 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3489 context.AddNativeCallback(
3490 "SignalNativeCount",
3494 ASSERT_EQ(count, 1);
3495 count_latch.Signal();
3496 }));
3497 context.AddNativeCallback(
3498 "SignalNativeMessage",
3502 ASSERT_EQ("PointerData(viewId: 0, x: 123.0, y: 456.0)", message);
3503 message_latch.Signal();
3504 }));
3505
3506 auto engine = builder.LaunchEngine();
3507 ASSERT_TRUE(engine.is_valid());
3508
3509 ready_latch.Wait();
3510
3511 FlutterPointerEvent pointer_event = {};
3512 pointer_event.struct_size = sizeof(FlutterPointerEvent);
3513 pointer_event.phase = FlutterPointerPhase::kAdd;
3514 pointer_event.x = 123;
3515 pointer_event.y = 456;
3516 pointer_event.timestamp = static_cast<size_t>(1234567890);
3517 pointer_event.view_id = 0;
3518
3520 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
3521 ASSERT_EQ(result, kSuccess);
3522
3523 count_latch.Wait();
3524 message_latch.Wait();
3525}
3526
3527/// Send a pointer event to Dart and wait until the Dart code echos with the
3528/// view ID.
3529TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
3530 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3531 EmbedderConfigBuilder builder(context);
3532 builder.SetSoftwareRendererConfig();
3533 builder.SetDartEntrypoint("pointer_data_packet_view_id");
3534
3535 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
3536 context.AddNativeCallback(
3537 "SignalNativeTest",
3539 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3540 context.AddNativeCallback(
3541 "SignalNativeMessage",
3545 ASSERT_EQ("ViewID: 2", message);
3546 message_latch.Signal();
3547 }));
3548
3549 auto engine = builder.LaunchEngine();
3550 ASSERT_TRUE(engine.is_valid());
3551
3552 ready_latch.Wait();
3553
3554 FlutterPointerEvent pointer_event = {};
3555 pointer_event.struct_size = sizeof(FlutterPointerEvent);
3556 pointer_event.phase = FlutterPointerPhase::kAdd;
3557 pointer_event.x = 123;
3558 pointer_event.y = 456;
3559 pointer_event.timestamp = static_cast<size_t>(1234567890);
3560 pointer_event.view_id = 2;
3561
3563 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
3564 ASSERT_EQ(result, kSuccess);
3565
3566 message_latch.Wait();
3567}
3568
3569TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
3570 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3571 EmbedderConfigBuilder builder(context);
3572 builder.SetSoftwareRendererConfig();
3573 builder.SetDartEntrypoint("window_metrics_event_view_id");
3574
3575 fml::AutoResetWaitableEvent ready_latch, message_latch;
3576 context.AddNativeCallback(
3577 "SignalNativeTest",
3579 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3580 context.AddNativeCallback(
3581 "SignalNativeMessage",
3585 ASSERT_EQ("Changed: [0]", message);
3586 message_latch.Signal();
3587 }));
3588
3589 auto engine = builder.LaunchEngine();
3590 ASSERT_TRUE(engine.is_valid());
3591
3592 ready_latch.Wait();
3593
3594 FlutterWindowMetricsEvent event = {};
3595 // Simulate an event that comes from an old version of embedder.h that doesn't
3596 // have the view_id field.
3597 event.struct_size = offsetof(FlutterWindowMetricsEvent, view_id);
3598 event.width = 200;
3599 event.height = 300;
3600 event.pixel_ratio = 1.5;
3601 // Skip assigning event.view_id here to test the default behavior.
3602
3605 ASSERT_EQ(result, kSuccess);
3606
3607 message_latch.Wait();
3608}
3609
3610TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
3611 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3612 EmbedderConfigBuilder builder(context);
3613 builder.SetSoftwareRendererConfig();
3614 builder.SetDartEntrypoint("window_metrics_event_view_id");
3615
3616 fml::AutoResetWaitableEvent ready_latch, message_latch;
3617 context.AddNativeCallback(
3618 "SignalNativeTest",
3620 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3621
3622 context.AddNativeCallback(
3623 "SignalNativeMessage",
3627 // Message latch should only be signaled once as the bad
3628 // view metric should be dropped by the engine.
3629 ASSERT_FALSE(message_latch.IsSignaledForTest());
3630 ASSERT_EQ("Changed: [0]", message);
3631 message_latch.Signal();
3632 }));
3633
3634 auto engine = builder.LaunchEngine();
3635 ASSERT_TRUE(engine.is_valid());
3636
3637 ready_latch.Wait();
3638
3639 // Send a window metric for a nonexistent view, which should be dropped by the
3640 // engine.
3641 FlutterWindowMetricsEvent bad_event = {};
3642 bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
3643 bad_event.width = 200;
3644 bad_event.height = 300;
3645 bad_event.pixel_ratio = 1.5;
3646 bad_event.view_id = 100;
3647
3650 ASSERT_EQ(result, kSuccess);
3651
3652 // Send a window metric for a valid view. The engine notifies the Dart app.
3653 FlutterWindowMetricsEvent event = {};
3654 event.struct_size = sizeof(FlutterWindowMetricsEvent);
3655 event.width = 200;
3656 event.height = 300;
3657 event.pixel_ratio = 1.5;
3658 event.view_id = 0;
3659
3661 ASSERT_EQ(result, kSuccess);
3662
3663 message_latch.Wait();
3664}
3665
3666TEST_F(EmbedderTest, RegisterChannelListener) {
3667 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3668
3671 bool listening = false;
3672 context.AddNativeCallback(
3673 "SignalNativeTest",
3675 context.SetChannelUpdateCallback([&](const FlutterChannelUpdate* update) {
3676 EXPECT_STREQ(update->channel, "test/listen");
3677 EXPECT_TRUE(update->listening);
3678 listening = true;
3679 latch2.Signal();
3680 });
3681
3682 EmbedderConfigBuilder builder(context);
3683 builder.SetSoftwareRendererConfig();
3684 builder.SetDartEntrypoint("channel_listener_response");
3685
3686 auto engine = builder.LaunchEngine();
3687 ASSERT_TRUE(engine.is_valid());
3688
3689 latch.Wait();
3690 // Drain tasks posted to platform thread task runner.
3692 latch2.Wait();
3693
3694 ASSERT_TRUE(listening);
3695}
3696
3697TEST_F(EmbedderTest, PlatformThreadIsolatesWithCustomPlatformTaskRunner) {
3698 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3699 static fml::AutoResetWaitableEvent latch;
3700
3701 static std::thread::id ffi_call_thread_id;
3702 static void (*ffi_signal_native_test)() = []() -> void {
3703 ffi_call_thread_id = std::this_thread::get_id();
3704 latch.Signal();
3705 };
3706
3707 Dart_FfiNativeResolver ffi_resolver = [](const char* name,
3708 uintptr_t args_n) -> void* {
3709 if (std::string_view(name) == "FFISignalNativeTest") {
3710 return reinterpret_cast<void*>(ffi_signal_native_test);
3711 }
3712 return nullptr;
3713 };
3714
3715 // The test's Dart code will call this native function which overrides the
3716 // FFI resolver. After that, the Dart code will invoke the FFI function
3717 // using runOnPlatformThread.
3718 context.AddNativeCallback(
3719 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3721 }));
3722
3723 auto platform_task_runner = CreateNewThread("test_platform_thread");
3724
3726
3727 EmbedderTestTaskRunner test_task_runner(
3728 platform_task_runner, [&](FlutterTask task) {
3729 if (!engine.is_valid()) {
3730 return;
3731 }
3732 FlutterEngineRunTask(engine.get(), &task);
3733 });
3734
3735 std::thread::id platform_thread_id;
3736 platform_task_runner->PostTask([&]() {
3737 platform_thread_id = std::this_thread::get_id();
3738
3739 EmbedderConfigBuilder builder(context);
3740 const auto task_runner_description =
3741 test_task_runner.GetFlutterTaskRunnerDescription();
3742 builder.SetSoftwareRendererConfig();
3743 builder.SetPlatformTaskRunner(&task_runner_description);
3744 builder.SetDartEntrypoint("invokePlatformThreadIsolate");
3745 builder.AddCommandLineArgument("--enable-platform-isolates");
3746 engine = builder.LaunchEngine();
3747 ASSERT_TRUE(engine.is_valid());
3748 });
3749
3750 latch.Wait();
3751
3752 fml::AutoResetWaitableEvent kill_latch;
3753 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
3754 engine.reset();
3755
3756 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
3757 }));
3758 kill_latch.Wait();
3759
3760 // Check that the FFI call was executed on the platform thread.
3761 ASSERT_EQ(platform_thread_id, ffi_call_thread_id);
3762}
3763
3764} // namespace testing
3765} // namespace flutter
3766
3767// NOLINTEND(clang-analyzer-core.StackAddressEscape)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define TEST(S, s, D, expected)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
int count
constexpr SkColor SK_ColorMAGENTA
Definition SkColor.h:147
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
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:281
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:205
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:550
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:828
A task runner that we expect the embedder to provide but whose implementation is a real FML task runn...
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
void RunExpiredTasksNow()
virtual void PostTask(const fml::closure &task) override
@ 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
const Paint & paint
DART_EXPORT Dart_Handle Dart_SetFfiNativeResolver(Dart_Handle library, Dart_FfiNativeResolver resolver)
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, int index)
void *(* Dart_FfiNativeResolver)(const char *name, uintptr_t args_n)
Definition dart_api.h:3253
struct _Dart_NativeArguments * Dart_NativeArguments
Definition dart_api.h:3010
void(* Dart_NativeFunction)(Dart_NativeArguments arguments)
Definition dart_api.h:3198
DART_EXPORT Dart_Handle Dart_RootLibrary(void)
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 engine via the FlutterTa...
Definition embedder.cc:2953
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:2869
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:2998
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Schedule a new frame to redraw the content.
Definition embedder.cc:3287
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2314
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:2949
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:3299
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:2291
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:3207
FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterAddViewInfo *info)
Adds a view.
Definition embedder.cc:2187
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:2681
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data)
Collects the AOT data.
Definition embedder.cc:1476
FlutterEngineResult FlutterEngineSendPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *flutter_message)
Definition embedder.cc:2630
bool FlutterEngineRunsAOTCompiledDartCode(void)
Returns if the Flutter engine instance will run AOT compiled Dart code. This call has no threading re...
Definition embedder.cc:3057
FlutterEngineResult FlutterEngineReloadSystemFonts(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Reloads the system fonts in engine.
Definition embedder.cc:2896
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2423
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition embedder.cc:2148
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:3185
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:2575
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:2731
FLUTTER_EXPORT FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterRemoveViewInfo *info)
Removes a view.
Definition embedder.cc:2248
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterPlatformMessageResponseHandle *response)
Collects the handle created using FlutterPlatformMessageCreateResponseHandle.
Definition embedder.cc:2716
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:1422
FlutterKeyEventDeviceType
Definition embedder.h:1078
@ kFlutterKeyEventDeviceTypeKeyboard
Definition embedder.h:1079
@ kFlutterKeyEventDeviceTypeDirectionalPad
Definition embedder.h:1080
@ kFlutterKeyEventDeviceTypeHdmi
Definition embedder.h:1083
@ kFlutterKeyEventDeviceTypeJoystick
Definition embedder.h:1082
@ kFlutterKeyEventDeviceTypeGamepad
Definition embedder.h:1081
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition embedder.h:1793
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:1791
FlutterEngineAOTDataSourceType
AOT data source type.
Definition embedder.h:2107
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2108
@ kAdd
Definition embedder.h:988
FlutterSoftwarePixelFormat
Definition embedder.h:333
@ kFlutterSoftwarePixelFormatRGBA4444
Definition embedder.h:345
@ kFlutterSoftwarePixelFormatRGBA8888
Definition embedder.h:349
@ kFlutterSoftwarePixelFormatBGRA8888
Definition embedder.h:357
@ kFlutterSoftwarePixelFormatGray8
Definition embedder.h:337
@ kFlutterSoftwarePixelFormatRGB565
Definition embedder.h:341
FlutterEngineResult
Definition embedder.h:72
@ kInvalidArguments
Definition embedder.h:75
FlutterNativeThreadType
Definition embedder.h:2084
FlutterKeyEventType
Definition embedder.h:1072
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1075
@ kFlutterBackingStoreTypeSoftware2
Definition embedder.h:1749
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:1742
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)
#define FAIL(name, result)
FlutterEngine engine
Definition main.cc:68
VkSurfaceKHR surface
Definition main.cc:49
SkBitmap source
Definition examples.cpp:28
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
GAsyncResult * result
#define FML_LOG(severity)
Definition logging.h:82
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_UNREACHABLE()
Definition logging.h:109
Win32Message message
TEST_F(DisplayListTest, Defaults)
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
sk_sp< SkSurface > CreateRenderSurface(const FlutterLayer &layer, GrDirectContext *context)
FlutterKeyEventType UnserializeKeyEventType(uint64_t kind)
bool SurfacePixelDataMatchesBytes(SkSurface *surface, const std::vector< uint8_t > &bytes)
const char * GetSourcePath()
testing::EmbedderTest EmbedderTest
void ExpectKeyEventEq(const FlutterKeyEvent &subject, const FlutterKeyEvent &baseline)
constexpr const char * kDefaultAOTAppELFFileName
Definition elf_loader.h:17
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image)
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
DEF_SWITCHES_START aot vmservice shared library name
Definition switches.h:32
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 switches.h:41
it will be possible to load the file into Perfetto s trace viewer 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
Definition switches.h:248
int64_t FlutterViewId
it will be possible to load the file into Perfetto s trace viewer 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
Definition switches.h:259
TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders)
std::string JoinPaths(std::initializer_list< std::string > components)
Definition paths.cc:14
internal::CopyableLambda< T > MakeCopyable(T lambda)
#define T
int32_t height
int32_t width
FlutterAddViewCallback add_view_callback
Definition embedder.h:911
FlutterViewId view_id
The identifier for the view to add. This must be unique.
Definition embedder.h:891
const FlutterWindowMetricsEvent * view_metrics
Definition embedder.h:896
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:1760
FlutterSoftwareBackingStore software
The description of the software backing store.
Definition embedder.h:1768
An update to whether a message channel has a listener set or not.
Definition embedder.h:1533
FlutterEngineAOTDataSourceType type
Definition embedder.h:2114
const char * elf_path
Absolute path to an ELF library file.
Definition embedder.h:2117
uint64_t logical
Definition embedder.h:1132
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1110
uint64_t physical
Definition embedder.h:1124
FlutterKeyEventDeviceType device_type
The source device for the key event.
Definition embedder.h:1150
FlutterKeyEventType type
The event kind.
Definition embedder.h:1116
FlutterPoint offset
Definition embedder.h:1833
FlutterLayerContentType type
Definition embedder.h:1822
const FlutterBackingStore * backing_store
Definition embedder.h:1826
FlutterBackingStorePresentInfo * backing_store_present_info
Definition embedder.h:1839
const FlutterPlatformView * platform_view
Definition embedder.h:1829
size_t struct_size
This size of this struct. Must be sizeof(FlutterLayer).
Definition embedder.h:1819
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:1835
const char * language_code
Definition embedder.h:1939
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:1935
const char * script_code
Definition embedder.h:1949
const char * country_code
Definition embedder.h:1944
const char * variant_code
Definition embedder.h:1954
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1162
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1172
const char * channel
Definition embedder.h:1163
const uint8_t * message
Definition embedder.h:1164
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformView).
Definition embedder.h:1714
FlutterPlatformViewIdentifier identifier
Definition embedder.h:1718
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1034
FlutterViewId view_id
The identifier of the view that received the pointer event.
Definition embedder.h:1069
double y
The y coordinate of the pointer event in physical pixels.
Definition embedder.h:1043
double x
The x coordinate of the pointer event in physical pixels.
Definition embedder.h:1041
FlutterPointerPhase phase
Definition embedder.h:1035
A structure to represent a rectangle.
Definition embedder.h:435
A region represented by a collection of non-overlapping rectangles.
Definition embedder.h:1797
size_t struct_size
The size of this struct. Must be sizeof(FlutterRegion).
Definition embedder.h:1799
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:959
FlutterViewId view_id
Definition embedder.h:942
double height
Definition embedder.h:423
double width
Definition embedder.h:422
size_t height
The number of rows in the allocation.
Definition embedder.h:1625
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:841
size_t height
Physical height of the window.
Definition embedder.h:845
int64_t view_id
The view that this event is describing.
Definition embedder.h:863
double pixel_ratio
Scale factor for the physical screen.
Definition embedder.h:847
size_t width
Physical width of the window.
Definition embedder.h:843
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
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)
#define EXPECT_TRUE(handle)
Definition unit_test.h:685