Flutter Engine
The Flutter Engine
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
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(); });
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);
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
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);
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
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
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([&]() {
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);
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([&]() {
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);
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);
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
402 message.struct_size = sizeof(FlutterPlatformMessage);
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);
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);
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);
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);
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);
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);
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);
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);
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
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
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
716 DISABLED_CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
717#else
719 CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
720#endif // FML_OS_MACOSX && FML_ARCH_CPU_ARM64
721
722 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
723
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 {
775 platform_view.struct_size = sizeof(platform_view);
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 {
822 platform_view.struct_size = sizeof(platform_view);
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
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 {
994 platform_view.struct_size = sizeof(platform_view);
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
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 {
1131 platform_view.struct_size = sizeof(platform_view);
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) {
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) {
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) {
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);
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
1305 info.struct_size = sizeof(FlutterAddViewInfo);
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);
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
1350 info.struct_size = sizeof(FlutterAddViewInfo);
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);
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);
1428 builder.SetSoftwareRendererConfig();
1429
1430 auto engine = builder.LaunchEngine();
1431 ASSERT_TRUE(engine.is_valid());
1432
1434 info.struct_size = sizeof(FlutterRemoveViewInfo);
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);
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);
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);
1585 builder.SetSoftwareRendererConfig();
1586
1587 auto engine = builder.LaunchEngine();
1588 ASSERT_TRUE(engine.is_valid());
1589
1592 info.struct_size = sizeof(FlutterRemoveViewInfo);
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);
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);
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
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);
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(); });
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
2074TEST_F(EmbedderTest, CanQueryDartAOTMode) {
2077}
2078
2079TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) {
2080 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2081
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 {
2132 platform_view.struct_size = sizeof(platform_view);
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
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
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 EXPECT_GE(captures.worker_threads_count - 1, 2u);
2314 EXPECT_LE(captures.worker_threads_count - 1, 4u);
2315
2316 platform_task_runner->PostTask([&]() {
2317 engine.reset();
2318 sync_latch.Signal();
2319 });
2320 sync_latch.Wait();
2321
2322 // The engine should have already been destroyed on the platform task runner.
2323 ASSERT_FALSE(engine.is_valid());
2324}
2325
2326TEST_F(EmbedderTest, InvalidAOTDataSourcesMustReturnError) {
2328 GTEST_SKIP();
2329 return;
2330 }
2331 FlutterEngineAOTDataSource data_in = {};
2332 FlutterEngineAOTData data_out = nullptr;
2333
2334 // Null source specified.
2335 ASSERT_EQ(FlutterEngineCreateAOTData(nullptr, &data_out), kInvalidArguments);
2336 ASSERT_EQ(data_out, nullptr);
2337
2338 // Null data_out specified.
2339 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, nullptr), kInvalidArguments);
2340
2341 // Invalid FlutterEngineAOTDataSourceType type specified.
2342 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2343 data_in.type = static_cast<FlutterEngineAOTDataSourceType>(-1);
2344 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2345 ASSERT_EQ(data_out, nullptr);
2346
2347 // Invalid ELF path specified.
2349 data_in.elf_path = nullptr;
2350 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2351 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2352 ASSERT_EQ(data_in.elf_path, nullptr);
2353 ASSERT_EQ(data_out, nullptr);
2354
2355 // Invalid ELF path specified.
2356 data_in.elf_path = "";
2357 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2358 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2359 ASSERT_EQ(data_in.elf_path, "");
2360 ASSERT_EQ(data_out, nullptr);
2361
2362 // Could not find VM snapshot data.
2363 data_in.elf_path = "/bin/true";
2364 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kInvalidArguments);
2365 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2366 ASSERT_EQ(data_in.elf_path, "/bin/true");
2367 ASSERT_EQ(data_out, nullptr);
2368}
2369
2370TEST_F(EmbedderTest, MustNotRunWithMultipleAOTSources) {
2372 GTEST_SKIP();
2373 return;
2374 }
2375 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2376
2378 context,
2379 EmbedderConfigBuilder::InitializationPreference::kMultiAOTInitialize);
2380
2381 builder.SetSoftwareRendererConfig();
2382
2383 auto engine = builder.LaunchEngine();
2384 ASSERT_FALSE(engine.is_valid());
2385}
2386
2387TEST_F(EmbedderTest, CanCreateAndCollectAValidElfSource) {
2389 GTEST_SKIP();
2390 return;
2391 }
2392 FlutterEngineAOTDataSource data_in = {};
2393 FlutterEngineAOTData data_out = nullptr;
2394
2395 // Collecting a null object should be allowed
2396 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2397
2398 const auto elf_path =
2400
2402 data_in.elf_path = elf_path.c_str();
2403
2404 ASSERT_EQ(FlutterEngineCreateAOTData(&data_in, &data_out), kSuccess);
2405 ASSERT_EQ(data_in.type, kFlutterEngineAOTDataSourceTypeElfPath);
2406 ASSERT_EQ(data_in.elf_path, elf_path.c_str());
2407 ASSERT_NE(data_out, nullptr);
2408
2409 ASSERT_EQ(FlutterEngineCollectAOTData(data_out), kSuccess);
2410}
2411
2412TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) {
2414 GTEST_SKIP();
2415 return;
2416 }
2417 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2418
2420 context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
2421
2423 context,
2424 EmbedderConfigBuilder::InitializationPreference::kAOTDataInitialize);
2425
2426 builder.SetSoftwareRendererConfig();
2427
2428 auto engine = builder.LaunchEngine();
2429 ASSERT_TRUE(engine.is_valid());
2430
2431 // Wait for the root isolate to launch.
2432 latch.Wait();
2433 engine.reset();
2434}
2435
2436#if defined(__clang_analyzer__)
2437#define TEST_VM_SNAPSHOT_DATA nullptr
2438#define TEST_VM_SNAPSHOT_INSTRUCTIONS nullptr
2439#define TEST_ISOLATE_SNAPSHOT_DATA nullptr
2440#define TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS nullptr
2441#endif
2442
2443//------------------------------------------------------------------------------
2444/// PopulateJITSnapshotMappingCallbacks should successfully change the callbacks
2445/// of the snapshots in the engine's settings when JIT snapshots are explicitly
2446/// defined.
2447///
2448TEST_F(EmbedderTest, CanSuccessfullyPopulateSpecificJITSnapshotCallbacks) {
2449// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2450#if defined(OS_FUCHSIA)
2451 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2452#else
2453
2454 // This test is only relevant in JIT mode.
2456 GTEST_SKIP();
2457 return;
2458 }
2459
2460 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2462 builder.SetSoftwareRendererConfig();
2463
2464 // Construct the location of valid JIT snapshots.
2465 const std::string src_path = GetSourcePath();
2466 const std::string vm_snapshot_data =
2467 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2468 const std::string vm_snapshot_instructions =
2469 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2470 const std::string isolate_snapshot_data =
2471 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2472 const std::string isolate_snapshot_instructions =
2473 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2474
2475 // Explicitly define the locations of the JIT snapshots
2476 builder.GetProjectArgs().vm_snapshot_data =
2477 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2478 builder.GetProjectArgs().vm_snapshot_instructions =
2479 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2480 builder.GetProjectArgs().isolate_snapshot_data =
2481 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2482 builder.GetProjectArgs().isolate_snapshot_instructions =
2483 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2484
2485 auto engine = builder.LaunchEngine();
2486
2488 const Settings settings = shell.GetSettings();
2489
2490 ASSERT_NE(settings.vm_snapshot_data(), nullptr);
2491 ASSERT_NE(settings.vm_snapshot_instr(), nullptr);
2492 ASSERT_NE(settings.isolate_snapshot_data(), nullptr);
2493 ASSERT_NE(settings.isolate_snapshot_instr(), nullptr);
2494 ASSERT_NE(settings.dart_library_sources_kernel(), nullptr);
2495#endif // OS_FUCHSIA
2496}
2497
2498//------------------------------------------------------------------------------
2499/// PopulateJITSnapshotMappingCallbacks should still be able to successfully
2500/// change the callbacks of the snapshots in the engine's settings when JIT
2501/// snapshots are explicitly defined. However, if those snapshot locations are
2502/// invalid, the callbacks should return a nullptr.
2503///
2504TEST_F(EmbedderTest, JITSnapshotCallbacksFailWithInvalidLocation) {
2505// TODO(#107263): Inconsistent snapshot paths in the Linux Fuchsia FEMU test.
2506#if defined(OS_FUCHSIA)
2507 GTEST_SKIP() << "Inconsistent paths in Fuchsia.";
2508#else
2509
2510 // This test is only relevant in JIT mode.
2512 GTEST_SKIP();
2513 return;
2514 }
2515
2516 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2518 builder.SetSoftwareRendererConfig();
2519
2520 // Explicitly define the locations of the invalid JIT snapshots
2521 builder.GetProjectArgs().vm_snapshot_data =
2522 reinterpret_cast<const uint8_t*>("invalid_vm_data");
2523 builder.GetProjectArgs().vm_snapshot_instructions =
2524 reinterpret_cast<const uint8_t*>("invalid_vm_instructions");
2525 builder.GetProjectArgs().isolate_snapshot_data =
2526 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2527 builder.GetProjectArgs().isolate_snapshot_instructions =
2528 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2529
2530 auto engine = builder.LaunchEngine();
2531
2533 const Settings settings = shell.GetSettings();
2534
2535 ASSERT_EQ(settings.vm_snapshot_data(), nullptr);
2536 ASSERT_EQ(settings.vm_snapshot_instr(), nullptr);
2537 ASSERT_EQ(settings.isolate_snapshot_data(), nullptr);
2538 ASSERT_EQ(settings.isolate_snapshot_instr(), nullptr);
2539#endif // OS_FUCHSIA
2540}
2541
2542//------------------------------------------------------------------------------
2543/// The embedder must be able to run explicitly specified snapshots in JIT mode
2544/// (i.e. when those are present in known locations).
2545///
2546TEST_F(EmbedderTest, CanLaunchEngineWithSpecifiedJITSnapshots) {
2547 // This test is only relevant in JIT mode.
2549 GTEST_SKIP();
2550 return;
2551 }
2552
2553 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2555 builder.SetSoftwareRendererConfig();
2556
2557 // Construct the location of valid JIT snapshots.
2558 const std::string src_path = GetSourcePath();
2559 const std::string vm_snapshot_data =
2560 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2561 const std::string vm_snapshot_instructions =
2562 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2563 const std::string isolate_snapshot_data =
2564 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_DATA});
2565 const std::string isolate_snapshot_instructions =
2566 fml::paths::JoinPaths({src_path, TEST_ISOLATE_SNAPSHOT_INSTRUCTIONS});
2567
2568 // Explicitly define the locations of the JIT snapshots
2569 builder.GetProjectArgs().vm_snapshot_data =
2570 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2571 builder.GetProjectArgs().vm_snapshot_instructions =
2572 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2573 builder.GetProjectArgs().isolate_snapshot_data =
2574 reinterpret_cast<const uint8_t*>(isolate_snapshot_data.c_str());
2575 builder.GetProjectArgs().isolate_snapshot_instructions =
2576 reinterpret_cast<const uint8_t*>(isolate_snapshot_instructions.c_str());
2577
2578 auto engine = builder.LaunchEngine();
2579 ASSERT_TRUE(engine.is_valid());
2580}
2581
2582//------------------------------------------------------------------------------
2583/// The embedder must be able to run in JIT mode when only some snapshots are
2584/// specified.
2585///
2586TEST_F(EmbedderTest, CanLaunchEngineWithSomeSpecifiedJITSnapshots) {
2587 // This test is only relevant in JIT mode.
2589 GTEST_SKIP();
2590 return;
2591 }
2592
2593 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2595 builder.SetSoftwareRendererConfig();
2596
2597 // Construct the location of valid JIT snapshots.
2598 const std::string src_path = GetSourcePath();
2599 const std::string vm_snapshot_data =
2600 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_DATA});
2601 const std::string vm_snapshot_instructions =
2602 fml::paths::JoinPaths({src_path, TEST_VM_SNAPSHOT_INSTRUCTIONS});
2603
2604 // Explicitly define the locations of the JIT snapshots
2605 builder.GetProjectArgs().vm_snapshot_data =
2606 reinterpret_cast<const uint8_t*>(vm_snapshot_data.c_str());
2607 builder.GetProjectArgs().vm_snapshot_instructions =
2608 reinterpret_cast<const uint8_t*>(vm_snapshot_instructions.c_str());
2609
2610 auto engine = builder.LaunchEngine();
2611 ASSERT_TRUE(engine.is_valid());
2612}
2613
2614//------------------------------------------------------------------------------
2615/// The embedder must be able to run in JIT mode even when the specfied
2616/// snapshots are invalid. It should be able to resolve them as it would when
2617/// the snapshots are not specified.
2618///
2619TEST_F(EmbedderTest, CanLaunchEngineWithInvalidJITSnapshots) {
2620 // This test is only relevant in JIT mode.
2622 GTEST_SKIP();
2623 return;
2624 }
2625
2626 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2628 builder.SetSoftwareRendererConfig();
2629
2630 // Explicitly define the locations of the JIT snapshots
2631 builder.GetProjectArgs().isolate_snapshot_data =
2632 reinterpret_cast<const uint8_t*>("invalid_snapshot_data");
2633 builder.GetProjectArgs().isolate_snapshot_instructions =
2634 reinterpret_cast<const uint8_t*>("invalid_snapshot_instructions");
2635
2636 auto engine = builder.LaunchEngine();
2637 ASSERT_TRUE(engine.is_valid());
2639}
2640
2641//------------------------------------------------------------------------------
2642/// The embedder must be able to launch even when the snapshots are not
2643/// explicitly defined in JIT mode. It must be able to resolve those snapshots.
2644///
2645TEST_F(EmbedderTest, CanLaunchEngineWithUnspecifiedJITSnapshots) {
2646 // This test is only relevant in JIT mode.
2648 GTEST_SKIP();
2649 return;
2650 }
2651
2652 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2654 builder.SetSoftwareRendererConfig();
2655
2656 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_data, nullptr);
2657 ASSERT_EQ(builder.GetProjectArgs().vm_snapshot_instructions, nullptr);
2658 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_data, nullptr);
2659 ASSERT_EQ(builder.GetProjectArgs().isolate_snapshot_instructions, nullptr);
2660
2661 auto engine = builder.LaunchEngine();
2662 ASSERT_TRUE(engine.is_valid());
2663}
2664
2665TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) {
2666 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2668 builder.SetSoftwareRendererConfig();
2669 auto engine = builder.LaunchEngine();
2670
2671 ASSERT_TRUE(engine.is_valid());
2672
2673 FlutterWindowMetricsEvent event = {};
2674 event.struct_size = sizeof(event);
2675 event.width = 800;
2676 event.height = 600;
2677 event.pixel_ratio = 0.0;
2678 event.physical_view_inset_top = 0.0;
2679 event.physical_view_inset_right = 0.0;
2680 event.physical_view_inset_bottom = 0.0;
2681 event.physical_view_inset_left = 0.0;
2682
2683 // Pixel ratio must be positive.
2686
2687 event.pixel_ratio = 1.0;
2688 event.physical_view_inset_top = -1.0;
2689 event.physical_view_inset_right = -1.0;
2690 event.physical_view_inset_bottom = -1.0;
2691 event.physical_view_inset_left = -1.0;
2692
2693 // Physical view insets must be non-negative.
2696
2697 event.physical_view_inset_top = 700;
2698 event.physical_view_inset_right = 900;
2699 event.physical_view_inset_bottom = 700;
2700 event.physical_view_inset_left = 900;
2701
2702 // Top/bottom insets cannot be greater than height.
2703 // Left/right insets cannot be greater than width.
2706}
2707
2710 std::string entrypoint,
2712 const std::vector<uint8_t>& bytes) {
2713 auto& context =
2714 test.GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2715
2718 bool matches = false;
2719
2720 builder.SetSoftwareRendererConfig();
2721 builder.SetCompositor();
2722 builder.SetDartEntrypoint(std::move(entrypoint));
2723 builder.SetRenderTargetType(
2724 EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer2,
2725 pixfmt);
2726
2727 auto engine = builder.LaunchEngine();
2728 ASSERT_TRUE(engine.is_valid());
2729
2730 context.GetCompositor().SetNextPresentCallback(
2731 [&matches, &bytes, &latch](FlutterViewId view_id,
2732 const FlutterLayer** layers,
2733 size_t layers_count) {
2734 ASSERT_EQ(layers[0]->type, kFlutterLayerContentTypeBackingStore);
2735 ASSERT_EQ(layers[0]->backing_store->type,
2738 static_cast<SkSurface*>(
2739 layers[0]->backing_store->software2.user_data),
2740 bytes);
2741 latch.Signal();
2742 });
2743
2744 // Send a window metrics events so frames may be scheduled.
2745 FlutterWindowMetricsEvent event = {};
2746 event.struct_size = sizeof(event);
2747 event.width = 1;
2748 event.height = 1;
2749 event.pixel_ratio = 1.0;
2751 kSuccess);
2752
2753 latch.Wait();
2754 ASSERT_TRUE(matches);
2755
2756 engine.reset();
2757}
2758
2759template <typename T>
2762 std::string entrypoint,
2764 T pixelvalue) {
2765 uint8_t* bytes = reinterpret_cast<uint8_t*>(&pixelvalue);
2767 test, std::move(entrypoint), pixfmt,
2768 std::vector<uint8_t>(bytes, bytes + sizeof(T)));
2769}
2770
2771#define SW_PIXFMT_TEST_F(test_name, dart_entrypoint, pixfmt, matcher) \
2772 TEST_F(EmbedderTest, SoftwareRenderingPixelFormats##test_name) { \
2773 expectSoftwareRenderingOutputMatches(*this, #dart_entrypoint, pixfmt, \
2774 matcher); \
2775 }
2776
2777// Don't test the pixel formats that contain padding (so an X) and the
2778// kFlutterSoftwarePixelFormatNative32 pixel format here, so we don't add any
2779// flakiness.
2780SW_PIXFMT_TEST_F(RedRGBA565xF800,
2781 draw_solid_red,
2783 (uint16_t)0xF800);
2784SW_PIXFMT_TEST_F(RedRGBA4444xF00F,
2785 draw_solid_red,
2787 (uint16_t)0xF00F);
2788SW_PIXFMT_TEST_F(RedRGBA8888xFFx00x00xFF,
2789 draw_solid_red,
2791 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
2792SW_PIXFMT_TEST_F(RedBGRA8888x00x00xFFxFF,
2793 draw_solid_red,
2795 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
2797 draw_solid_red,
2799 (uint8_t)0x36);
2800
2801SW_PIXFMT_TEST_F(GreenRGB565x07E0,
2802 draw_solid_green,
2804 (uint16_t)0x07E0);
2805SW_PIXFMT_TEST_F(GreenRGBA4444x0F0F,
2806 draw_solid_green,
2808 (uint16_t)0x0F0F);
2809SW_PIXFMT_TEST_F(GreenRGBA8888x00xFFx00xFF,
2810 draw_solid_green,
2812 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
2813SW_PIXFMT_TEST_F(GreenBGRA8888x00xFFx00xFF,
2814 draw_solid_green,
2816 (std::vector<uint8_t>{0x00, 0xFF, 0x00, 0xFF}));
2817SW_PIXFMT_TEST_F(GreenGray8xB6,
2818 draw_solid_green,
2820 (uint8_t)0xB6);
2821
2822SW_PIXFMT_TEST_F(BlueRGB565x001F,
2823 draw_solid_blue,
2825 (uint16_t)0x001F);
2826SW_PIXFMT_TEST_F(BlueRGBA4444x00FF,
2827 draw_solid_blue,
2829 (uint16_t)0x00FF);
2830SW_PIXFMT_TEST_F(BlueRGBA8888x00x00xFFxFF,
2831 draw_solid_blue,
2833 (std::vector<uint8_t>{0x00, 0x00, 0xFF, 0xFF}));
2834SW_PIXFMT_TEST_F(BlueBGRA8888xFFx00x00xFF,
2835 draw_solid_blue,
2837 (std::vector<uint8_t>{0xFF, 0x00, 0x00, 0xFF}));
2838SW_PIXFMT_TEST_F(BlueGray8x12,
2839 draw_solid_blue,
2841 (uint8_t)0x12);
2842
2843//------------------------------------------------------------------------------
2844// Key Data
2845//------------------------------------------------------------------------------
2846
2847typedef struct {
2848 std::shared_ptr<fml::AutoResetWaitableEvent> latch;
2851
2852// Convert `kind` in integer form to its enum form.
2853//
2854// It performs a revesed mapping from `_serializeKeyEventType`
2855// in shell/platform/embedder/fixtures/main.dart.
2857 switch (kind) {
2858 case 1:
2860 case 2:
2862 case 3:
2864 default:
2867 }
2868}
2869
2870// Convert `source` in integer form to its enum form.
2871//
2872// It performs a revesed mapping from `_serializeKeyEventDeviceType`
2873// in shell/platform/embedder/fixtures/main.dart.
2875 switch (source) {
2876 case 1:
2878 case 2:
2880 case 3:
2882 case 4:
2884 case 5:
2886 default:
2889 }
2890}
2891
2892// Checks the equality of two `FlutterKeyEvent` by each of their members except
2893// for `character`. The `character` must be checked separately.
2895 const FlutterKeyEvent& baseline) {
2896 EXPECT_EQ(subject.timestamp, baseline.timestamp);
2897 EXPECT_EQ(subject.type, baseline.type);
2898 EXPECT_EQ(subject.physical, baseline.physical);
2899 EXPECT_EQ(subject.logical, baseline.logical);
2900 EXPECT_EQ(subject.synthesized, baseline.synthesized);
2901 EXPECT_EQ(subject.device_type, baseline.device_type);
2902}
2903
2904TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) {
2905 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
2906 uint64_t echoed_char;
2907 FlutterKeyEvent echoed_event;
2908 echoed_event.struct_size = sizeof(FlutterKeyEvent);
2909
2910 auto native_echo_event = [&](Dart_NativeArguments args) {
2911 echoed_event.type =
2914 echoed_event.timestamp =
2915 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
2923 echoed_event.synthesized =
2925 echoed_event.device_type =
2928
2929 message_latch->Signal();
2930 };
2931
2932 auto platform_task_runner = CreateNewThread("platform_thread");
2933
2936 platform_task_runner->PostTask([&]() {
2937 auto& context =
2938 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2940 builder.SetSoftwareRendererConfig();
2941 builder.SetDartEntrypoint("key_data_echo");
2942 builder.SetPlatformMessageCallback(
2943 [&](const FlutterPlatformMessage* message) {
2945 engine.get(), message->response_handle, nullptr, 0);
2946 });
2947 context.AddNativeCallback(
2948 "SignalNativeTest",
2950 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
2951
2952 context.AddNativeCallback("EchoKeyEvent",
2953 CREATE_NATIVE_ENTRY(native_echo_event));
2954
2955 engine = builder.LaunchEngine();
2956 ASSERT_TRUE(engine.is_valid());
2957 });
2958
2959 ready.Wait();
2960
2961 // A normal down event
2962 const FlutterKeyEvent down_event_upper_a{
2963 .struct_size = sizeof(FlutterKeyEvent),
2964 .timestamp = 1,
2966 .physical = 0x00070004,
2967 .logical = 0x00000000061,
2968 .character = "A",
2969 .synthesized = false,
2971 };
2972 platform_task_runner->PostTask([&]() {
2973 FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, nullptr,
2974 nullptr);
2975 });
2976 message_latch->Wait();
2977
2978 ExpectKeyEventEq(echoed_event, down_event_upper_a);
2979 EXPECT_EQ(echoed_char, 0x41llu);
2980
2981 // A repeat event with multi-byte character
2982 const FlutterKeyEvent repeat_event_wide_char{
2983 .struct_size = sizeof(FlutterKeyEvent),
2984 .timestamp = 1000,
2986 .physical = 0x00070005,
2987 .logical = 0x00000000062,
2988 .character = "∆",
2989 .synthesized = false,
2991 };
2992 platform_task_runner->PostTask([&]() {
2993 FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, nullptr,
2994 nullptr);
2995 });
2996 message_latch->Wait();
2997
2998 ExpectKeyEventEq(echoed_event, repeat_event_wide_char);
2999 EXPECT_EQ(echoed_char, 0x2206llu);
3000
3001 // An up event with no character, synthesized
3002 const FlutterKeyEvent up_event{
3003 .struct_size = sizeof(FlutterKeyEvent),
3004 .timestamp = 1000000,
3006 .physical = 0x00070006,
3007 .logical = 0x00000000063,
3008 .character = nullptr,
3009 .synthesized = true,
3011 };
3012 platform_task_runner->PostTask([&]() {
3013 FlutterEngineSendKeyEvent(engine.get(), &up_event, nullptr, nullptr);
3014 });
3015 message_latch->Wait();
3016
3017 ExpectKeyEventEq(echoed_event, up_event);
3018 EXPECT_EQ(echoed_char, 0llu);
3019
3020 fml::AutoResetWaitableEvent shutdown_latch;
3021 platform_task_runner->PostTask([&]() {
3022 engine.reset();
3023 shutdown_latch.Signal();
3024 });
3025 shutdown_latch.Wait();
3026}
3027
3028TEST_F(EmbedderTest, KeyDataAreBuffered) {
3029 auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
3030 std::vector<FlutterKeyEvent> echoed_events;
3031
3032 auto native_echo_event = [&](Dart_NativeArguments args) {
3033 echoed_events.push_back(FlutterKeyEvent{
3034 .timestamp =
3035 static_cast<double>(tonic::DartConverter<uint64_t>::FromDart(
3037 .type =
3046 .device_type = UnserializeKeyEventDeviceType(
3049 });
3050
3051 message_latch->Signal();
3052 };
3053
3054 auto platform_task_runner = CreateNewThread("platform_thread");
3055
3058 platform_task_runner->PostTask([&]() {
3059 auto& context =
3060 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3062 builder.SetSoftwareRendererConfig();
3063 builder.SetDartEntrypoint("key_data_late_echo");
3064 builder.SetPlatformMessageCallback(
3065 [&](const FlutterPlatformMessage* message) {
3067 engine.get(), message->response_handle, nullptr, 0);
3068 });
3069 context.AddNativeCallback(
3070 "SignalNativeTest",
3072 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3073
3074 context.AddNativeCallback("EchoKeyEvent",
3075 CREATE_NATIVE_ENTRY(native_echo_event));
3076
3077 engine = builder.LaunchEngine();
3078 ASSERT_TRUE(engine.is_valid());
3079 });
3080 ready.Wait();
3081
3082 FlutterKeyEvent sample_event{
3083 .struct_size = sizeof(FlutterKeyEvent),
3085 .physical = 0x00070004,
3086 .logical = 0x00000000061,
3087 .character = "A",
3088 .synthesized = false,
3090 };
3091
3092 // Send an event.
3093 sample_event.timestamp = 1.0l;
3094 platform_task_runner->PostTask([&]() {
3095 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3096 message_latch->Signal();
3097 });
3098 message_latch->Wait();
3099
3100 // Should not receive echos because the callback is not set yet.
3101 EXPECT_EQ(echoed_events.size(), 0u);
3102
3103 // Send an empty message to 'test/starts_echo' to start echoing.
3104 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
3106 engine.get(), [](const uint8_t* data, size_t size, void* user_data) {},
3107 nullptr, &response_handle);
3108
3110 .struct_size = sizeof(FlutterPlatformMessage),
3111 .channel = "test/starts_echo",
3112 .message = nullptr,
3113 .message_size = 0,
3114 .response_handle = response_handle,
3115 };
3116
3117 platform_task_runner->PostTask([&]() {
3120 ASSERT_EQ(result, kSuccess);
3121
3123 });
3124
3125 // message_latch->Wait();
3126 message_latch->Wait();
3127 // All previous events should be received now.
3128 EXPECT_EQ(echoed_events.size(), 1u);
3129
3130 // Send a second event.
3131 sample_event.timestamp = 10.0l;
3132 platform_task_runner->PostTask([&]() {
3133 FlutterEngineSendKeyEvent(engine.get(), &sample_event, nullptr, nullptr);
3134 });
3135 message_latch->Wait();
3136
3137 // The event should be echoed, too.
3138 EXPECT_EQ(echoed_events.size(), 2u);
3139
3140 fml::AutoResetWaitableEvent shutdown_latch;
3141 platform_task_runner->PostTask([&]() {
3142 engine.reset();
3143 shutdown_latch.Signal();
3144 });
3145 shutdown_latch.Wait();
3146}
3147
3148TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) {
3150 fml::AutoResetWaitableEvent sync_latch;
3152
3153 // One of the threads that the key data callback will be posted to is the
3154 // platform thread. So we cannot wait for assertions to complete on the
3155 // platform thread. Create a new thread to manage the engine instance and wait
3156 // for assertions on the test thread.
3157 auto platform_task_runner = CreateNewThread("platform_thread");
3158
3159 platform_task_runner->PostTask([&]() {
3160 auto& context =
3161 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3163 builder.SetSoftwareRendererConfig();
3164 builder.SetDartEntrypoint("key_data_echo");
3165 context.AddNativeCallback(
3166 "SignalNativeTest",
3168 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3169 context.AddNativeCallback(
3170 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3171
3172 engine = builder.LaunchEngine();
3173 ASSERT_TRUE(engine.is_valid());
3174
3175 sync_latch.Signal();
3176 });
3177 sync_latch.Wait();
3178 ready.Wait();
3179
3180 // Dispatch a single event
3181 FlutterKeyEvent event{
3182 .struct_size = sizeof(FlutterKeyEvent),
3183 .timestamp = 1000,
3185 .physical = 0x00070005,
3186 .logical = 0x00000000062,
3187 .character = nullptr,
3189 };
3190
3191 KeyEventUserData user_data1{
3192 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3193 };
3194 // Entrypoint `key_data_echo` returns `event.synthesized` as `handled`.
3195 event.synthesized = true;
3196 platform_task_runner->PostTask([&]() {
3197 // Test when the response callback is empty.
3198 // It should not cause a crash.
3199 FlutterEngineSendKeyEvent(engine.get(), &event, nullptr, nullptr);
3200
3201 // Test when the response callback is non-empty.
3202 // It should be invoked (so that the latch can be unlocked.)
3204 engine.get(), &event,
3205 [](bool handled, void* untyped_user_data) {
3206 KeyEventUserData* user_data =
3207 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3208 EXPECT_EQ(handled, true);
3209 user_data->latch->Signal();
3210 },
3211 &user_data1);
3212 });
3213 user_data1.latch->Wait();
3214 fml::AutoResetWaitableEvent shutdown_latch;
3215 platform_task_runner->PostTask([&]() {
3216 engine.reset();
3217 shutdown_latch.Signal();
3218 });
3219 shutdown_latch.Wait();
3220}
3221
3222TEST_F(EmbedderTest, BackToBackKeyEventResponsesCorrectlyInvoked) {
3224 fml::AutoResetWaitableEvent sync_latch;
3226
3227 // One of the threads that the callback will be posted to is the platform
3228 // thread. So we cannot wait for assertions to complete on the platform
3229 // thread. Create a new thread to manage the engine instance and wait for
3230 // assertions on the test thread.
3231 auto platform_task_runner = CreateNewThread("platform_thread");
3232
3233 platform_task_runner->PostTask([&]() {
3234 auto& context =
3235 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3236
3238 builder.SetSoftwareRendererConfig();
3239 builder.SetDartEntrypoint("key_data_echo");
3240 context.AddNativeCallback(
3241 "SignalNativeTest",
3243 [&ready](Dart_NativeArguments args) { ready.Signal(); }));
3244
3245 context.AddNativeCallback(
3246 "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {}));
3247
3248 engine = builder.LaunchEngine();
3249 ASSERT_TRUE(engine.is_valid());
3250
3251 sync_latch.Signal();
3252 });
3253 sync_latch.Wait();
3254 ready.Wait();
3255
3256 // Dispatch a single event
3257 FlutterKeyEvent event{
3258 .struct_size = sizeof(FlutterKeyEvent),
3259 .timestamp = 1000,
3261 .physical = 0x00070005,
3262 .logical = 0x00000000062,
3263 .character = nullptr,
3264 .synthesized = false,
3266 };
3267
3268 // Dispatch two events back to back, using the same callback on different
3269 // user_data
3270 KeyEventUserData user_data2{
3271 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3272 .returned = false,
3273 };
3274 KeyEventUserData user_data3{
3275 .latch = std::make_shared<fml::AutoResetWaitableEvent>(),
3276 .returned = false,
3277 };
3278 auto callback23 = [](bool handled, void* untyped_user_data) {
3280 reinterpret_cast<KeyEventUserData*>(untyped_user_data);
3281 EXPECT_EQ(handled, false);
3282 user_data->returned = true;
3283 user_data->latch->Signal();
3284 };
3285 platform_task_runner->PostTask([&]() {
3286 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2);
3287 FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3);
3288 });
3289 user_data2.latch->Wait();
3290 user_data3.latch->Wait();
3291
3292 EXPECT_TRUE(user_data2.returned);
3293 EXPECT_TRUE(user_data3.returned);
3294
3295 fml::AutoResetWaitableEvent shutdown_latch;
3296 platform_task_runner->PostTask([&]() {
3297 engine.reset();
3298 shutdown_latch.Signal();
3299 });
3300 shutdown_latch.Wait();
3301}
3302
3303//------------------------------------------------------------------------------
3304// Vsync waiter
3305//------------------------------------------------------------------------------
3306
3307// This test schedules a frame for the future and asserts that vsync waiter
3308// posts the event at the right frame start time (which is in the future).
3309TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) {
3311 fml::AutoResetWaitableEvent present_latch;
3312 fml::AutoResetWaitableEvent vsync_latch;
3313
3314 // One of the threads that the callback (FlutterEngineOnVsync) will be posted
3315 // to is the platform thread. So we cannot wait for assertions to complete on
3316 // the platform thread. Create a new thread to manage the engine instance and
3317 // wait for assertions on the test thread.
3318 auto platform_task_runner = CreateNewThread("platform_thread");
3319
3320 platform_task_runner->PostTask([&]() {
3321 auto& context =
3322 GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3323
3324 context.SetVsyncCallback([&](intptr_t baton) {
3325 platform_task_runner->PostTask([baton = baton, &engine, &vsync_latch]() {
3326 FlutterEngineOnVsync(engine.get(), baton, NanosFromEpoch(16),
3327 NanosFromEpoch(32));
3328 vsync_latch.Signal();
3329 });
3330 });
3331 context.AddNativeCallback(
3332 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3333 present_latch.Signal();
3334 }));
3335
3337 builder.SetSoftwareRendererConfig();
3338 builder.SetupVsyncCallback();
3339 builder.SetDartEntrypoint("empty_scene");
3340 engine = builder.LaunchEngine();
3341 ASSERT_TRUE(engine.is_valid());
3342
3343 // Send a window metrics events so frames may be scheduled.
3344 FlutterWindowMetricsEvent event = {};
3345 event.struct_size = sizeof(event);
3346 event.width = 800;
3347 event.height = 600;
3348 event.pixel_ratio = 1.0;
3349
3351 kSuccess);
3352 });
3353
3354 vsync_latch.Wait();
3355 present_latch.Wait();
3356
3357 fml::AutoResetWaitableEvent shutdown_latch;
3358 platform_task_runner->PostTask([&]() {
3359 engine.reset();
3360 shutdown_latch.Signal();
3361 });
3362 shutdown_latch.Wait();
3363}
3364
3365TEST_F(EmbedderTest, CanScheduleFrame) {
3366 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3368 builder.SetSoftwareRendererConfig();
3369 builder.SetDartEntrypoint("can_schedule_frame");
3371 context.AddNativeCallback(
3372 "SignalNativeTest",
3374 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
3375
3376 fml::AutoResetWaitableEvent check_latch;
3377 context.AddNativeCallback(
3378 "SignalNativeCount",
3380 [&check_latch](Dart_NativeArguments args) { check_latch.Signal(); }));
3381
3382 auto engine = builder.LaunchEngine();
3383 ASSERT_TRUE(engine.is_valid());
3384
3385 // Wait for the application to attach the listener.
3386 latch.Wait();
3387
3388 ASSERT_EQ(FlutterEngineScheduleFrame(engine.get()), kSuccess);
3389
3390 check_latch.Wait();
3391}
3392
3393TEST_F(EmbedderTest, CanSetNextFrameCallback) {
3394 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3396 builder.SetSoftwareRendererConfig();
3397 builder.SetDartEntrypoint("draw_solid_red");
3398
3399 auto engine = builder.LaunchEngine();
3400 ASSERT_TRUE(engine.is_valid());
3401
3402 // Register the callback that is executed once the next frame is drawn.
3403 fml::AutoResetWaitableEvent callback_latch;
3404 VoidCallback callback = [](void* user_data) {
3405 fml::AutoResetWaitableEvent* callback_latch =
3407
3408 callback_latch->Signal();
3409 };
3410
3412 &callback_latch);
3413 ASSERT_EQ(result, kSuccess);
3414
3415 // Send a window metrics events so frames may be scheduled.
3416 FlutterWindowMetricsEvent event = {};
3417 event.struct_size = sizeof(event);
3418 event.width = 800;
3419 event.height = 600;
3420 event.pixel_ratio = 1.0;
3421 event.physical_view_inset_top = 0.0;
3422 event.physical_view_inset_right = 0.0;
3423 event.physical_view_inset_bottom = 0.0;
3424 event.physical_view_inset_left = 0.0;
3426 kSuccess);
3427
3428 callback_latch.Wait();
3429}
3430
3431#if defined(FML_OS_MACOSX)
3432
3433static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
3434 pthread_t tid = pthread_self();
3435 struct sched_param param;
3436 int policy = SCHED_OTHER;
3437 switch (config.priority) {
3439 param.sched_priority = 10;
3440 break;
3441 default:
3442 param.sched_priority = 1;
3443 }
3444 pthread_setschedparam(tid, policy, &param);
3445}
3446
3447TEST_F(EmbedderTest, EmbedderThreadHostUseCustomThreadConfig) {
3448 auto thread_host =
3450 nullptr, MockThreadConfigSetter);
3451
3453 int ui_policy;
3454 struct sched_param ui_param;
3455
3456 thread_host->GetTaskRunners().GetUITaskRunner()->PostTask([&] {
3457 pthread_t current_thread = pthread_self();
3458 pthread_getschedparam(current_thread, &ui_policy, &ui_param);
3459 ASSERT_EQ(ui_param.sched_priority, 10);
3460 ui_latch.Signal();
3461 });
3462
3464 int io_policy;
3465 struct sched_param io_param;
3466 thread_host->GetTaskRunners().GetIOTaskRunner()->PostTask([&] {
3467 pthread_t current_thread = pthread_self();
3468 pthread_getschedparam(current_thread, &io_policy, &io_param);
3469 ASSERT_EQ(io_param.sched_priority, 1);
3470 io_latch.Signal();
3471 });
3472
3473 ui_latch.Wait();
3474 io_latch.Wait();
3475}
3476#endif
3477
3478/// Send a pointer event to Dart and wait until the Dart code signals
3479/// it received the event.
3480TEST_F(EmbedderTest, CanSendPointer) {
3481 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3483 builder.SetSoftwareRendererConfig();
3484 builder.SetDartEntrypoint("pointer_data_packet");
3485
3486 fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
3487 context.AddNativeCallback(
3488 "SignalNativeTest",
3490 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3491 context.AddNativeCallback(
3492 "SignalNativeCount",
3496 ASSERT_EQ(count, 1);
3497 count_latch.Signal();
3498 }));
3499 context.AddNativeCallback(
3500 "SignalNativeMessage",
3504 ASSERT_EQ("PointerData(viewId: 0, x: 123.0, y: 456.0)", message);
3505 message_latch.Signal();
3506 }));
3507
3508 auto engine = builder.LaunchEngine();
3509 ASSERT_TRUE(engine.is_valid());
3510
3511 ready_latch.Wait();
3512
3513 FlutterPointerEvent pointer_event = {};
3514 pointer_event.struct_size = sizeof(FlutterPointerEvent);
3515 pointer_event.phase = FlutterPointerPhase::kAdd;
3516 pointer_event.x = 123;
3517 pointer_event.y = 456;
3518 pointer_event.timestamp = static_cast<size_t>(1234567890);
3519 pointer_event.view_id = 0;
3520
3522 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
3523 ASSERT_EQ(result, kSuccess);
3524
3525 count_latch.Wait();
3526 message_latch.Wait();
3527}
3528
3529/// Send a pointer event to Dart and wait until the Dart code echos with the
3530/// view ID.
3531TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
3532 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3534 builder.SetSoftwareRendererConfig();
3535 builder.SetDartEntrypoint("pointer_data_packet_view_id");
3536
3537 fml::AutoResetWaitableEvent ready_latch, add_view_latch, message_latch;
3538 context.AddNativeCallback(
3539 "SignalNativeTest",
3541 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3542 context.AddNativeCallback(
3543 "SignalNativeMessage",
3547 ASSERT_EQ("ViewID: 2", message);
3548 message_latch.Signal();
3549 }));
3550
3551 auto engine = builder.LaunchEngine();
3552 ASSERT_TRUE(engine.is_valid());
3553 ready_latch.Wait();
3554
3555 // Add view 2
3556 FlutterWindowMetricsEvent metrics = {};
3557 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
3558 metrics.width = 800;
3559 metrics.height = 600;
3560 metrics.pixel_ratio = 1.0;
3561 metrics.view_id = 2;
3562
3564 info.struct_size = sizeof(FlutterAddViewInfo);
3565 info.view_id = 2;
3566 info.view_metrics = &metrics;
3567 info.add_view_callback = [](const FlutterAddViewResult* result) {
3568 EXPECT_TRUE(result->added);
3569 fml::AutoResetWaitableEvent* add_view_latch =
3570 reinterpret_cast<fml::AutoResetWaitableEvent*>(result->user_data);
3571 add_view_latch->Signal();
3572 };
3573 info.user_data = &add_view_latch;
3574 ASSERT_EQ(FlutterEngineAddView(engine.get(), &info), kSuccess);
3575 add_view_latch.Wait();
3576
3577 // Send a pointer event for view 2
3578 FlutterPointerEvent pointer_event = {};
3579 pointer_event.struct_size = sizeof(FlutterPointerEvent);
3580 pointer_event.phase = FlutterPointerPhase::kAdd;
3581 pointer_event.x = 123;
3582 pointer_event.y = 456;
3583 pointer_event.timestamp = static_cast<size_t>(1234567890);
3584 pointer_event.view_id = 2;
3585
3587 FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
3588 ASSERT_EQ(result, kSuccess);
3589
3590 message_latch.Wait();
3591}
3592
3593TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
3594 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3596 builder.SetSoftwareRendererConfig();
3597 builder.SetDartEntrypoint("window_metrics_event_view_id");
3598
3599 fml::AutoResetWaitableEvent ready_latch, message_latch;
3600 context.AddNativeCallback(
3601 "SignalNativeTest",
3603 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3604 context.AddNativeCallback(
3605 "SignalNativeMessage",
3609 ASSERT_EQ("Changed: [0]", message);
3610 message_latch.Signal();
3611 }));
3612
3613 auto engine = builder.LaunchEngine();
3614 ASSERT_TRUE(engine.is_valid());
3615
3616 ready_latch.Wait();
3617
3618 FlutterWindowMetricsEvent event = {};
3619 // Simulate an event that comes from an old version of embedder.h that doesn't
3620 // have the view_id field.
3621 event.struct_size = offsetof(FlutterWindowMetricsEvent, view_id);
3622 event.width = 200;
3623 event.height = 300;
3624 event.pixel_ratio = 1.5;
3625 // Skip assigning event.view_id here to test the default behavior.
3626
3629 ASSERT_EQ(result, kSuccess);
3630
3631 message_latch.Wait();
3632}
3633
3634TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
3635 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3637 builder.SetSoftwareRendererConfig();
3638 builder.SetDartEntrypoint("window_metrics_event_view_id");
3639
3640 fml::AutoResetWaitableEvent ready_latch, message_latch;
3641 context.AddNativeCallback(
3642 "SignalNativeTest",
3644 [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
3645
3646 context.AddNativeCallback(
3647 "SignalNativeMessage",
3651 // Message latch should only be signaled once as the bad
3652 // view metric should be dropped by the engine.
3653 ASSERT_FALSE(message_latch.IsSignaledForTest());
3654 ASSERT_EQ("Changed: [0]", message);
3655 message_latch.Signal();
3656 }));
3657
3658 auto engine = builder.LaunchEngine();
3659 ASSERT_TRUE(engine.is_valid());
3660
3661 ready_latch.Wait();
3662
3663 // Send a window metric for a nonexistent view, which should be dropped by the
3664 // engine.
3665 FlutterWindowMetricsEvent bad_event = {};
3666 bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
3667 bad_event.width = 200;
3668 bad_event.height = 300;
3669 bad_event.pixel_ratio = 1.5;
3670 bad_event.view_id = 100;
3671
3674 ASSERT_EQ(result, kSuccess);
3675
3676 // Send a window metric for a valid view. The engine notifies the Dart app.
3677 FlutterWindowMetricsEvent event = {};
3678 event.struct_size = sizeof(FlutterWindowMetricsEvent);
3679 event.width = 200;
3680 event.height = 300;
3681 event.pixel_ratio = 1.5;
3682 event.view_id = 0;
3683
3685 ASSERT_EQ(result, kSuccess);
3686
3687 message_latch.Wait();
3688}
3689
3690TEST_F(EmbedderTest, RegisterChannelListener) {
3691 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3692
3695 bool listening = false;
3696 context.AddNativeCallback(
3697 "SignalNativeTest",
3699 context.SetChannelUpdateCallback([&](const FlutterChannelUpdate* update) {
3700 EXPECT_STREQ(update->channel, "test/listen");
3701 EXPECT_TRUE(update->listening);
3702 listening = true;
3703 latch2.Signal();
3704 });
3705
3707 builder.SetSoftwareRendererConfig();
3708 builder.SetDartEntrypoint("channel_listener_response");
3709
3710 auto engine = builder.LaunchEngine();
3711 ASSERT_TRUE(engine.is_valid());
3712
3713 latch.Wait();
3714 // Drain tasks posted to platform thread task runner.
3716 latch2.Wait();
3717
3718 ASSERT_TRUE(listening);
3719}
3720
3721TEST_F(EmbedderTest, PlatformThreadIsolatesWithCustomPlatformTaskRunner) {
3722 auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
3723 static fml::AutoResetWaitableEvent latch;
3724
3725 static std::thread::id ffi_call_thread_id;
3726 static void (*ffi_signal_native_test)() = []() -> void {
3727 ffi_call_thread_id = std::this_thread::get_id();
3728 latch.Signal();
3729 };
3730
3731 Dart_FfiNativeResolver ffi_resolver = [](const char* name,
3732 uintptr_t args_n) -> void* {
3733 if (std::string_view(name) == "FFISignalNativeTest") {
3734 return reinterpret_cast<void*>(ffi_signal_native_test);
3735 }
3736 return nullptr;
3737 };
3738
3739 // The test's Dart code will call this native function which overrides the
3740 // FFI resolver. After that, the Dart code will invoke the FFI function
3741 // using runOnPlatformThread.
3742 context.AddNativeCallback(
3743 "SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3745 }));
3746
3747 auto platform_task_runner = CreateNewThread("test_platform_thread");
3748
3750
3751 EmbedderTestTaskRunner test_task_runner(
3752 platform_task_runner, [&](FlutterTask task) {
3753 if (!engine.is_valid()) {
3754 return;
3755 }
3756 FlutterEngineRunTask(engine.get(), &task);
3757 });
3758
3759 std::thread::id platform_thread_id;
3760 platform_task_runner->PostTask([&]() {
3761 platform_thread_id = std::this_thread::get_id();
3762
3764 const auto task_runner_description =
3765 test_task_runner.GetFlutterTaskRunnerDescription();
3766 builder.SetSoftwareRendererConfig();
3767 builder.SetPlatformTaskRunner(&task_runner_description);
3768 builder.SetDartEntrypoint("invokePlatformThreadIsolate");
3769 builder.AddCommandLineArgument("--enable-platform-isolates");
3770 engine = builder.LaunchEngine();
3771 ASSERT_TRUE(engine.is_valid());
3772 });
3773
3774 latch.Wait();
3775
3776 fml::AutoResetWaitableEvent kill_latch;
3777 platform_task_runner->PostTask(fml::MakeCopyable([&]() mutable {
3778 engine.reset();
3779
3780 platform_task_runner->PostTask([&kill_latch] { kill_latch.Signal(); });
3781 }));
3782 kill_latch.Wait();
3783
3784 // Check that the FFI call was executed on the platform thread.
3785 ASSERT_EQ(platform_thread_id, ffi_call_thread_id);
3786}
3787
3788} // namespace testing
3789} // namespace flutter
3790
3791// NOLINTEND(clang-analyzer-core.StackAddressEscape)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
std::unique_ptr< flutter::PlatformViewIOS > platform_view
int count
Definition: FontMgrTest.cpp:50
constexpr SkColor SK_ColorMAGENTA
Definition: SkColor.h:147
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
GLenum type
static size_t GetVMLaunchCount()
The number of times the VM has been launched in the process. This call is inherently racy because the...
Definition: dart_vm.cc: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:557
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:834
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()
Definition: message_loop.cc:19
void RunExpiredTasksNow()
Definition: message_loop.cc:72
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
@ 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
Definition: color_source.cc:38
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:3262
struct _Dart_NativeArguments * Dart_NativeArguments
Definition: dart_api.h:3019
void(* Dart_NativeFunction)(Dart_NativeArguments arguments)
Definition: dart_api.h:3207
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:2959
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:2875
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:3004
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Schedule a new frame to redraw the content.
Definition: embedder.cc:3293
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition: embedder.cc:2320
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:2955
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:3305
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:2297
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:3213
FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterAddViewInfo *info)
Adds a view.
Definition: embedder.cc:2193
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:2687
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data)
Collects the AOT data.
Definition: embedder.cc:1480
FlutterEngineResult FlutterEngineSendPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *flutter_message)
Definition: embedder.cc:2636
bool FlutterEngineRunsAOTCompiledDartCode(void)
Returns if the Flutter engine instance will run AOT compiled Dart code. This call has no threading re...
Definition: embedder.cc:3063
FlutterEngineResult FlutterEngineReloadSystemFonts(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Reloads the system fonts in engine.
Definition: embedder.cc:2902
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition: embedder.cc:2429
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition: embedder.cc:2154
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:3191
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:2581
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:2737
FLUTTER_EXPORT FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterRemoveViewInfo *info)
Removes a view.
Definition: embedder.cc:2254
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterPlatformMessageResponseHandle *response)
Collects the handle created using FlutterPlatformMessageCreateResponseHandle.
Definition: embedder.cc:2722
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:1426
FlutterKeyEventDeviceType
Definition: embedder.h:1080
@ kFlutterKeyEventDeviceTypeKeyboard
Definition: embedder.h:1081
@ kFlutterKeyEventDeviceTypeDirectionalPad
Definition: embedder.h:1082
@ kFlutterKeyEventDeviceTypeHdmi
Definition: embedder.h:1085
@ kFlutterKeyEventDeviceTypeJoystick
Definition: embedder.h:1084
@ kFlutterKeyEventDeviceTypeGamepad
Definition: embedder.h:1083
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition: embedder.h:1795
@ kFlutterLayerContentTypeBackingStore
Definition: embedder.h:1793
FlutterEngineAOTDataSourceType
AOT data source type.
Definition: embedder.h:2109
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition: embedder.h:2110
@ kAdd
Definition: embedder.h:990
FlutterSoftwarePixelFormat
Definition: embedder.h:335
@ kFlutterSoftwarePixelFormatRGBA4444
Definition: embedder.h:347
@ kFlutterSoftwarePixelFormatRGBA8888
Definition: embedder.h:351
@ kFlutterSoftwarePixelFormatBGRA8888
Definition: embedder.h:359
@ kFlutterSoftwarePixelFormatGray8
Definition: embedder.h:339
@ kFlutterSoftwarePixelFormatRGB565
Definition: embedder.h:343
FlutterEngineResult
Definition: embedder.h:72
@ kInvalidArguments
Definition: embedder.h:75
@ kSuccess
Definition: embedder.h:73
FlutterNativeThreadType
Definition: embedder.h:2086
FlutterKeyEventType
Definition: embedder.h:1074
@ kFlutterKeyEventTypeDown
Definition: embedder.h:1076
@ kFlutterKeyEventTypeUp
Definition: embedder.h:1075
@ kFlutterKeyEventTypeRepeat
Definition: embedder.h:1077
@ kFlutterBackingStoreTypeSoftware2
Definition: embedder.h:1751
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition: embedder.h:1744
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
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
const uint8_t * isolate_snapshot_data
Definition: gen_snapshot.cc:69
const uint8_t * vm_snapshot_data
Definition: main_impl.cc:52
const uint8_t * vm_snapshot_instructions
Definition: main_impl.cc:53
const uint8_t * isolate_snapshot_instructions
Definition: gen_snapshot.cc:70
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
static void expectSoftwareRenderingOutputMatches(EmbedderTest &test, std::string entrypoint, FlutterSoftwarePixelFormat pixfmt, T pixelvalue)
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image)
TEST(DisplayListComplexity, EmptyDisplayList)
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
Definition: flutter_view.h:13
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)
Definition: make_copyable.h:57
def matches(file)
Definition: gen_manifest.py:38
Definition: update.py:1
#define T
Definition: precompiler.cc:65
int32_t height
int32_t width
FlutterAddViewCallback add_view_callback
Definition: embedder.h:913
size_t struct_size
Definition: embedder.h:890
FlutterViewId view_id
The identifier for the view to add. This must be unique.
Definition: embedder.h:893
const FlutterWindowMetricsEvent * view_metrics
Definition: embedder.h:898
FlutterViewId view_id
Definition: embedder.h:1787
FlutterBackingStoreType type
Specifies the type of backing store.
Definition: embedder.h:1762
FlutterSoftwareBackingStore software
The description of the software backing store.
Definition: embedder.h:1770
An update to whether a message channel has a listener set or not.
Definition: embedder.h:1535
FlutterEngineAOTDataSourceType type
Definition: embedder.h:2116
const char * elf_path
Absolute path to an ELF library file.
Definition: embedder.h:2119
uint64_t logical
Definition: embedder.h:1134
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition: embedder.h:1112
double timestamp
Definition: embedder.h:1116
uint64_t physical
Definition: embedder.h:1126
FlutterKeyEventDeviceType device_type
The source device for the key event.
Definition: embedder.h:1152
FlutterKeyEventType type
The event kind.
Definition: embedder.h:1118
FlutterPoint offset
Definition: embedder.h:1835
FlutterLayerContentType type
Definition: embedder.h:1824
const FlutterBackingStore * backing_store
Definition: embedder.h:1828
FlutterBackingStorePresentInfo * backing_store_present_info
Definition: embedder.h:1841
const FlutterPlatformView * platform_view
Definition: embedder.h:1831
size_t struct_size
This size of this struct. Must be sizeof(FlutterLayer).
Definition: embedder.h:1821
FlutterSize size
The size of the layer (in physical pixels).
Definition: embedder.h:1837
const char * language_code
Definition: embedder.h:1941
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition: embedder.h:1937
const char * script_code
Definition: embedder.h:1951
const char * country_code
Definition: embedder.h:1946
const char * variant_code
Definition: embedder.h:1956
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition: embedder.h:1164
const FlutterPlatformMessageResponseHandle * response_handle
Definition: embedder.h:1174
const char * channel
Definition: embedder.h:1165
const uint8_t * message
Definition: embedder.h:1166
FlutterPlatformViewIdentifier identifier
Definition: embedder.h:1720
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition: embedder.h:1036
FlutterViewId view_id
The identifier of the view that received the pointer event.
Definition: embedder.h:1071
double y
The y coordinate of the pointer event in physical pixels.
Definition: embedder.h:1045
double x
The x coordinate of the pointer event in physical pixels.
Definition: embedder.h:1043
FlutterPointerPhase phase
Definition: embedder.h:1037
A structure to represent a rectangle.
Definition: embedder.h:437
A region represented by a collection of non-overlapping rectangles.
Definition: embedder.h:1799
size_t struct_size
The size of this struct. Must be sizeof(FlutterRegion).
Definition: embedder.h:1801
FlutterRemoveViewCallback remove_view_callback
Definition: embedder.h:961
FlutterViewId view_id
Definition: embedder.h:944
double height
Definition: embedder.h:425
double width
Definition: embedder.h:424
size_t height
The number of rows in the allocation.
Definition: embedder.h:1627
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:843
size_t height
Physical height of the window.
Definition: embedder.h:847
int64_t view_id
The view that this event is describing.
Definition: embedder.h:865
double pixel_ratio
Scale factor for the physical screen.
Definition: embedder.h:849
size_t width
Physical width of the window.
Definition: embedder.h:845
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)
void * user_data
const uintptr_t id
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678