Flutter Engine
 
Loading...
Searching...
No Matches
shell_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 <algorithm>
8#include <ctime>
9#include <future>
10#include <memory>
11#include <thread>
12#include <utility>
13#include <vector>
14
15#if SHELL_ENABLE_GL
16#include <EGL/egl.h>
17#endif // SHELL_ENABLE_GL
18
47#include "fml/mapping.h"
48#include "gmock/gmock.h"
51#include "third_party/rapidjson/include/rapidjson/writer.h"
52#include "third_party/skia/include/codec/SkCodecAnimation.h"
54
55#ifdef SHELL_ENABLE_VULKAN
56#include "flutter/vulkan/vulkan_application.h" // nogncheck
57#endif
58
59// CREATE_NATIVE_ENTRY is leaky by design
60// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
61
62namespace flutter {
63namespace testing {
64
65constexpr int64_t kImplicitViewId = 0ll;
66
67using ::testing::_;
68using ::testing::Return;
69
70namespace {
71
72std::unique_ptr<PlatformMessage> MakePlatformMessage(
73 const std::string& channel,
74 const std::map<std::string, std::string>& values,
76 rapidjson::Document document;
77 auto& allocator = document.GetAllocator();
78 document.SetObject();
79
80 for (const auto& pair : values) {
81 rapidjson::Value key(pair.first.c_str(), strlen(pair.first.c_str()),
82 allocator);
83 rapidjson::Value value(pair.second.c_str(), strlen(pair.second.c_str()),
84 allocator);
85 document.AddMember(key, value, allocator);
86 }
87
88 rapidjson::StringBuffer buffer;
89 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
90 document.Accept(writer);
91 const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
92
93 std::unique_ptr<PlatformMessage> message = std::make_unique<PlatformMessage>(
94 channel, fml::MallocMapping::Copy(data, buffer.GetSize()), response);
95 return message;
96}
97
98class MockPlatformViewDelegate : public PlatformView::Delegate {
99 MOCK_METHOD(void,
100 OnPlatformViewCreated,
101 (std::unique_ptr<Surface> surface),
102 (override));
103
104 MOCK_METHOD(void, OnPlatformViewDestroyed, (), (override));
105
106 MOCK_METHOD(void, OnPlatformViewScheduleFrame, (), (override));
107
108 MOCK_METHOD(void,
109 OnPlatformViewAddView,
110 (int64_t view_id,
111 const ViewportMetrics& viewport_metrics,
112 AddViewCallback callback),
113 (override));
114
115 MOCK_METHOD(void,
116 OnPlatformViewRemoveView,
117 (int64_t view_id, RemoveViewCallback callback),
118 (override));
119
120 MOCK_METHOD(void,
121 OnPlatformViewSendViewFocusEvent,
122 (const ViewFocusEvent& event),
123 (override));
124
125 MOCK_METHOD(void,
126 OnPlatformViewSetNextFrameCallback,
127 (const fml::closure& closure),
128 (override));
129
130 MOCK_METHOD(void,
131 OnPlatformViewSetViewportMetrics,
132 (int64_t view_id, const ViewportMetrics& metrics),
133 (override));
134
135 MOCK_METHOD(void,
136 OnPlatformViewDispatchPlatformMessage,
137 (std::unique_ptr<PlatformMessage> message),
138 (override));
139
140 MOCK_METHOD(void,
141 OnPlatformViewDispatchPointerDataPacket,
142 (std::unique_ptr<PointerDataPacket> packet),
143 (override));
144
145 MOCK_METHOD(void,
146 OnPlatformViewDispatchSemanticsAction,
147 (int64_t view_id,
148 int32_t id,
151 (override));
152
153 MOCK_METHOD(void,
154 OnPlatformViewSetSemanticsEnabled,
155 (bool enabled),
156 (override));
157
158 MOCK_METHOD(void,
159 OnPlatformViewSetAccessibilityFeatures,
160 (int32_t flags),
161 (override));
162
163 MOCK_METHOD(void,
164 OnPlatformViewRegisterTexture,
165 (std::shared_ptr<Texture> texture),
166 (override));
167
168 MOCK_METHOD(void,
169 OnPlatformViewUnregisterTexture,
170 (int64_t texture_id),
171 (override));
172
173 MOCK_METHOD(void,
174 OnPlatformViewMarkTextureFrameAvailable,
175 (int64_t texture_id),
176 (override));
177
178 MOCK_METHOD(const Settings&,
179 OnPlatformViewGetSettings,
180 (),
181 (const, override));
182
183 MOCK_METHOD(void,
185 (intptr_t loading_unit_id,
186 std::unique_ptr<const fml::Mapping> snapshot_data,
187 std::unique_ptr<const fml::Mapping> snapshot_instructions),
188 (override));
189
190 MOCK_METHOD(void,
191 LoadDartDeferredLibraryError,
192 (intptr_t loading_unit_id,
193 const std::string error_message,
194 bool transient),
195 (override));
196
197 MOCK_METHOD(void,
198 UpdateAssetResolverByType,
199 (std::unique_ptr<AssetResolver> updated_asset_resolver,
201 (override));
202};
203
204class MockSurface : public Surface {
205 public:
206 MOCK_METHOD(bool, IsValid, (), (override));
207
208 MOCK_METHOD(std::unique_ptr<SurfaceFrame>,
209 AcquireFrame,
210 (const DlISize& size),
211 (override));
212
213 MOCK_METHOD(DlMatrix, GetRootTransformation, (), (const, override));
214
215 MOCK_METHOD(GrDirectContext*, GetContext, (), (override));
216
217 MOCK_METHOD(std::unique_ptr<GLContextResult>,
218 MakeRenderContextCurrent,
219 (),
220 (override));
221
222 MOCK_METHOD(bool, ClearRenderContext, (), (override));
223};
224
225class MockPlatformView : public PlatformView {
226 public:
227 MockPlatformView(MockPlatformViewDelegate& delegate,
228 const TaskRunners& task_runners)
229 : PlatformView(delegate, task_runners) {}
230 MOCK_METHOD(std::unique_ptr<Surface>, CreateRenderingSurface, (), (override));
231 MOCK_METHOD(std::shared_ptr<PlatformMessageHandler>,
232 GetPlatformMessageHandler,
233 (),
234 (const, override));
235};
236
237class TestPlatformView : public PlatformView {
238 public:
239 TestPlatformView(Shell& shell, const TaskRunners& task_runners)
240 : PlatformView(shell, task_runners) {}
241 MOCK_METHOD(std::unique_ptr<Surface>, CreateRenderingSurface, (), (override));
242 MOCK_METHOD(void, ReleaseResourceContext, (), (const, override));
243};
244
245class MockPlatformMessageHandler : public PlatformMessageHandler {
246 public:
247 MOCK_METHOD(void,
248 HandlePlatformMessage,
249 (std::unique_ptr<PlatformMessage> message),
250 (override));
251 MOCK_METHOD(bool,
252 DoesHandlePlatformMessageOnPlatformThread,
253 (),
254 (const, override));
255 MOCK_METHOD(void,
257 (int response_id, std::unique_ptr<fml::Mapping> mapping),
258 (override));
259 MOCK_METHOD(void,
261 (int response_id),
262 (override));
263};
264
265class MockPlatformMessageResponse : public PlatformMessageResponse {
266 public:
268 return fml::AdoptRef(new MockPlatformMessageResponse());
269 }
270 MOCK_METHOD(void, Complete, (std::unique_ptr<fml::Mapping> data), (override));
271 MOCK_METHOD(void, CompleteEmpty, (), (override));
272};
273} // namespace
274
276 public:
278 : valid_(valid), type_(type) {}
279
280 bool IsValid() const override { return true; }
281
282 // This is used to identify if replacement was made or not.
283 bool IsValidAfterAssetManagerChange() const override { return valid_; }
284
285 AssetResolver::AssetResolverType GetType() const override { return type_; }
286
287 std::unique_ptr<fml::Mapping> GetAsMapping(
288 const std::string& asset_name) const override {
289 return nullptr;
290 }
291
292 std::vector<std::unique_ptr<fml::Mapping>> GetAsMappings(
293 const std::string& asset_pattern,
294 const std::optional<std::string>& subdir) const override {
295 return {};
296 };
297
298 bool operator==(const AssetResolver& other) const override {
299 return this == &other;
300 }
301
302 private:
303 bool valid_;
305};
306
308 public:
310 std::shared_ptr<fml::ConcurrentMessageLoop> concurrent_loop)
311 : concurrent_loop_(std::move(concurrent_loop)) {}
312
313 // |AssetResolver|
314 bool IsValid() const override { return true; }
315
316 // |AssetResolver|
317 bool IsValidAfterAssetManagerChange() const override { return true; }
318
319 // |AssetResolver|
323
324 // |AssetResolver|
325 std::unique_ptr<fml::Mapping> GetAsMapping(
326 const std::string& asset_name) const override {
327 if (asset_name == "FontManifest.json" ||
328 asset_name == "NativeAssetsManifest.json") {
329 // This file is loaded directly by the engine.
330 return nullptr;
331 }
332 mapping_requests.push_back(asset_name);
333 EXPECT_TRUE(concurrent_loop_->RunsTasksOnCurrentThread())
335 return nullptr;
336 }
337
338 mutable std::vector<std::string> mapping_requests;
339
340 bool operator==(const AssetResolver& other) const override {
341 return this == &other;
342 }
343
344 private:
345 std::shared_ptr<fml::ConcurrentMessageLoop> concurrent_loop_;
346};
347
348static bool ValidateShell(Shell* shell) {
349 if (!shell) {
350 return false;
351 }
352
353 if (!shell->IsSetup()) {
354 return false;
355 }
356
358
359 {
362 shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() {
363 shell->GetPlatformView()->NotifyDestroyed();
364 latch.Signal();
365 });
366 latch.Wait();
367 }
368
369 return true;
370}
371
372static bool RasterizerIsTornDown(Shell* shell) {
374 bool is_torn_down = false;
377 [shell, &latch, &is_torn_down]() {
378 is_torn_down = shell->GetRasterizer()->IsTornDown();
379 latch.Signal();
380 });
381 latch.Wait();
382 return is_torn_down;
383}
384
386 ASSERT_TRUE(shell != nullptr);
387 ASSERT_TRUE(shell->IsSetup());
388
389 ASSERT_FALSE(RasterizerIsTornDown(shell));
391 ASSERT_TRUE(RasterizerIsTornDown(shell));
392}
393
394static std::string CreateFlagsString(std::vector<const char*>& flags) {
395 if (flags.empty()) {
396 return "";
397 }
398 std::string flags_string = flags[0];
399 for (size_t i = 1; i < flags.size(); ++i) {
400 flags_string += ",";
401 flags_string += flags[i];
402 }
403 return flags_string;
404}
405
406static void TestDartVmFlags(std::vector<const char*>& flags) {
407 std::string flags_string = CreateFlagsString(flags);
408 const std::vector<fml::CommandLine::Option> options = {
409 fml::CommandLine::Option("dart-flags", flags_string)};
410 fml::CommandLine command_line("", options, std::vector<std::string>());
412 EXPECT_EQ(settings.dart_flags.size(), flags.size());
413 for (size_t i = 0; i < flags.size(); ++i) {
414 EXPECT_EQ(settings.dart_flags[i], flags[i]);
415 }
416}
417
418static void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
419 const fml::closure& task) {
421 fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] {
422 task();
423 latch.Signal();
424 });
425 latch.Wait();
426}
427
428static sk_sp<DisplayList> MakeSizedDisplayList(int width, int height) {
430 builder.DrawRect(DlRect::MakeXYWH(0, 0, width, height),
432 return builder.Build();
433}
434
435TEST_F(ShellTest, InitializeWithInvalidThreads) {
436 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
437 Settings settings = CreateSettingsForFixture();
438 TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr);
439 auto shell = CreateShell(settings, task_runners);
440 ASSERT_FALSE(shell);
441 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
442}
443
444TEST_F(ShellTest, InitializeWithDifferentThreads) {
445 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
446 Settings settings = CreateSettingsForFixture();
447 std::string name_prefix = "io.flutter.test." + GetCurrentTestName() + ".";
451 ASSERT_EQ(thread_host.name_prefix, name_prefix);
452
453 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
454 thread_host.raster_thread->GetTaskRunner(),
455 thread_host.ui_thread->GetTaskRunner(),
456 thread_host.io_thread->GetTaskRunner());
457 auto shell = CreateShell(settings, task_runners);
458 ASSERT_TRUE(ValidateShell(shell.get()));
459 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
460 DestroyShell(std::move(shell), task_runners);
461 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
462}
463
464TEST_F(ShellTest, InitializeWithSingleThread) {
465 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
466 Settings settings = CreateSettingsForFixture();
467 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
469 auto task_runner = thread_host.platform_thread->GetTaskRunner();
470 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
471 task_runner);
472 auto shell = CreateShell(settings, task_runners);
473 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
474 ASSERT_TRUE(ValidateShell(shell.get()));
475 DestroyShell(std::move(shell), task_runners);
476 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
477}
478
479TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) {
480 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
481 Settings settings = CreateSettingsForFixture();
483 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
484 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
485 task_runner);
486 auto shell = CreateShell(settings, task_runners);
487 ASSERT_TRUE(ValidateShell(shell.get()));
488 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
489 DestroyShell(std::move(shell), task_runners);
490 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
491}
492
494 InitializeWithMultipleThreadButCallingThreadAsPlatformThread) {
495 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
496 Settings settings = CreateSettingsForFixture();
497 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
501 TaskRunners task_runners("test",
502 fml::MessageLoop::GetCurrent().GetTaskRunner(),
503 thread_host.raster_thread->GetTaskRunner(),
504 thread_host.ui_thread->GetTaskRunner(),
505 thread_host.io_thread->GetTaskRunner());
506 auto shell = Shell::Create(
507 flutter::PlatformData(), task_runners, settings,
508 [](Shell& shell) {
509 // This is unused in the platform view as we are not using the simulated
510 // vsync mechanism. We should have better DI in the tests.
511 const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
514 shell.GetTaskRunners(), vsync_clock,
515 [task_runners = shell.GetTaskRunners()]() {
516 return static_cast<std::unique_ptr<VsyncWaiter>>(
517 std::make_unique<VsyncWaiterFallback>(task_runners));
518 },
519 nullptr, shell.GetIsGpuDisabledSyncSwitch());
520 },
521 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
522 ASSERT_TRUE(ValidateShell(shell.get()));
523 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
524 DestroyShell(std::move(shell), task_runners);
525 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
526}
527
528TEST_F(ShellTest, InitializeWithDisabledGpu) {
529 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
530 Settings settings = CreateSettingsForFixture();
531 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
533 auto task_runner = thread_host.platform_thread->GetTaskRunner();
534 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
535 task_runner);
536 auto shell = CreateShell({
537 .settings = settings,
538 .task_runners = task_runners,
539 .is_gpu_disabled = true,
540 });
541 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
542 ASSERT_TRUE(ValidateShell(shell.get()));
543
544 bool is_disabled = false;
545 shell->GetIsGpuDisabledSyncSwitch()->Execute(
546 fml::SyncSwitch::Handlers().SetIfTrue([&] { is_disabled = true; }));
547 ASSERT_TRUE(is_disabled);
548
549 DestroyShell(std::move(shell), task_runners);
550 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
551}
552
553TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) {
554 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
555 Settings settings = CreateSettingsForFixture();
556 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
559 TaskRunners task_runners(
560 "test",
561 thread_host.platform_thread->GetTaskRunner(), // platform
562 thread_host.platform_thread->GetTaskRunner(), // raster
563 thread_host.ui_thread->GetTaskRunner(), // ui
564 thread_host.io_thread->GetTaskRunner() // io
565 );
566 auto shell = CreateShell(settings, task_runners);
567 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
568 ASSERT_TRUE(ValidateShell(shell.get()));
569 DestroyShell(std::move(shell), task_runners);
570 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
571}
572
573TEST_F(ShellTest, FixturesAreFunctional) {
574 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
575 auto settings = CreateSettingsForFixture();
576 auto shell = CreateShell(settings);
577 ASSERT_TRUE(ValidateShell(shell.get()));
578
579 auto configuration = RunConfiguration::InferFromSettings(settings);
580 ASSERT_TRUE(configuration.IsValid());
581 configuration.SetEntrypoint("fixturesAreFunctionalMain");
582
584 AddNativeCallback(
585 "SayHiFromFixturesAreFunctionalMain",
586 CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); }));
587
588 RunEngine(shell.get(), std::move(configuration));
589 main_latch.Wait();
590 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
591 DestroyShell(std::move(shell));
592 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
593}
594
595TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) {
596 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
597 auto settings = CreateSettingsForFixture();
598 auto shell = CreateShell(settings);
599 ASSERT_TRUE(ValidateShell(shell.get()));
600
601 auto configuration = RunConfiguration::InferFromSettings(settings);
602 ASSERT_TRUE(configuration.IsValid());
603 configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
604
605 fml::CountDownLatch latch(2);
606 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
607 latch.CountDown();
608 }));
609
610 RunEngine(shell.get(), std::move(configuration));
611
612 latch.Wait();
613
614 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
615 DestroyShell(std::move(shell));
616 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
617}
618
619TEST_F(ShellTest, LastEntrypoint) {
620 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
621 auto settings = CreateSettingsForFixture();
622 auto shell = CreateShell(settings);
623 ASSERT_TRUE(ValidateShell(shell.get()));
624
625 auto configuration = RunConfiguration::InferFromSettings(settings);
626 ASSERT_TRUE(configuration.IsValid());
627 std::string entry_point = "fixturesAreFunctionalMain";
628 configuration.SetEntrypoint(entry_point);
629
631 std::string last_entry_point;
632 AddNativeCallback(
633 "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
634 last_entry_point = shell->GetEngine()->GetLastEntrypoint();
635 main_latch.Signal();
636 }));
637
638 RunEngine(shell.get(), std::move(configuration));
639 main_latch.Wait();
640 EXPECT_EQ(entry_point, last_entry_point);
641 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
642 DestroyShell(std::move(shell));
643 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
644}
645
646TEST_F(ShellTest, LastEntrypointArgs) {
647 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
648 auto settings = CreateSettingsForFixture();
649 auto shell = CreateShell(settings);
650 ASSERT_TRUE(ValidateShell(shell.get()));
651
652 auto configuration = RunConfiguration::InferFromSettings(settings);
653 ASSERT_TRUE(configuration.IsValid());
654 std::string entry_point = "fixturesAreFunctionalMain";
655 std::vector<std::string> entry_point_args = {"arg1"};
656 configuration.SetEntrypoint(entry_point);
657 configuration.SetEntrypointArgs(entry_point_args);
658
660 std::vector<std::string> last_entry_point_args;
661 AddNativeCallback(
662 "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
663 last_entry_point_args = shell->GetEngine()->GetLastEntrypointArgs();
664 main_latch.Signal();
665 }));
666
667 RunEngine(shell.get(), std::move(configuration));
668 main_latch.Wait();
669#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
670 EXPECT_EQ(last_entry_point_args, entry_point_args);
671#else
672 ASSERT_TRUE(last_entry_point_args.empty());
673#endif
674 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
675 DestroyShell(std::move(shell));
676 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
677}
678
679TEST_F(ShellTest, DisallowedDartVMFlag) {
680#if defined(OS_FUCHSIA)
681 GTEST_SKIP() << "This test flakes on Fuchsia. https://fxbug.dev/110006 ";
682#else
683
684 // Run this test in a thread-safe manner, otherwise gtest will complain.
685 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
686
687 const std::vector<fml::CommandLine::Option> options = {
688 fml::CommandLine::Option("dart-flags", "--verify_after_gc")};
689 fml::CommandLine command_line("", options, std::vector<std::string>());
690
691 // Upon encountering a disallowed Dart flag the process terminates.
692 const char* expected =
693 "Encountered disallowed Dart VM flag: --verify_after_gc";
694 ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected);
695#endif // OS_FUCHSIA
696}
697
698TEST_F(ShellTest, AllowedDartVMFlag) {
699 std::vector<const char*> flags = {
700 "--enable-isolate-groups",
701 "--no-enable-isolate-groups",
702 };
703#if !FLUTTER_RELEASE
704 flags.push_back("--max_profile_depth 1");
705 flags.push_back("--random_seed 42");
706 flags.push_back("--max_subtype_cache_entries=22");
708 flags.push_back("--enable_mirrors");
709 }
710#endif
711
712 TestDartVmFlags(flags);
713}
714
715TEST_F(ShellTest, NoNeedToReportTimingsByDefault) {
716 auto settings = CreateSettingsForFixture();
717 std::unique_ptr<Shell> shell = CreateShell(settings);
718
719 // Create the surface needed by rasterizer
720 PlatformViewNotifyCreated(shell.get());
721
722 auto configuration = RunConfiguration::InferFromSettings(settings);
723 configuration.SetEntrypoint("emptyMain");
724
725 RunEngine(shell.get(), std::move(configuration));
726 PumpOneFrame(shell.get());
727 ASSERT_FALSE(GetNeedsReportTimings(shell.get()));
728
729 // This assertion may or may not be the direct result of needs_report_timings_
730 // being false. The count could be 0 simply because we just cleared
731 // unreported timings by reporting them. Hence this can't replace the
732 // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added
733 // this assertion for an additional confidence that we're not pushing
734 // back to unreported timings unnecessarily.
735 //
736 // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be
737 // positive in any tests. Otherwise those tests will be flaky as the clearing
738 // of unreported timings is unpredictive.
739 ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0);
740 DestroyShell(std::move(shell));
741}
742
743TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) {
744 auto settings = CreateSettingsForFixture();
745 std::unique_ptr<Shell> shell = CreateShell(settings);
746
747 // Create the surface needed by rasterizer
748 PlatformViewNotifyCreated(shell.get());
749
750 auto configuration = RunConfiguration::InferFromSettings(settings);
751 configuration.SetEntrypoint("dummyReportTimingsMain");
752
753 RunEngine(shell.get(), std::move(configuration));
754 PumpOneFrame(shell.get());
755 ASSERT_TRUE(GetNeedsReportTimings(shell.get()));
756 DestroyShell(std::move(shell));
757}
758
759static void CheckFrameTimings(const std::vector<FrameTiming>& timings,
760 fml::TimePoint start,
761 fml::TimePoint finish) {
762 fml::TimePoint last_frame_start;
763 for (size_t i = 0; i < timings.size(); i += 1) {
764 // Ensure that timings are sorted.
765 ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start);
766 last_frame_start = timings[i].Get(FrameTiming::kPhases[0]);
767
768 fml::TimePoint last_phase_time;
769 for (auto phase : FrameTiming::kPhases) {
770 // raster finish wall time doesn't use the same clock base
771 // as rest of the frame timings.
773 continue;
774 }
775
776 ASSERT_TRUE(timings[i].Get(phase) >= start);
777 ASSERT_TRUE(timings[i].Get(phase) <= finish);
778
779 // phases should have weakly increasing time points
780 ASSERT_TRUE(last_phase_time <= timings[i].Get(phase));
781 last_phase_time = timings[i].Get(phase);
782 }
783 }
784}
785
786TEST_F(ShellTest, ReportTimingsIsCalled) {
787 auto settings = CreateSettingsForFixture();
788 std::unique_ptr<Shell> shell = CreateShell(settings);
789
790 // We MUST put |start| after |CreateShell| because the clock source will be
791 // reset through |TimePoint::SetClockSource()| in
792 // |DartVMInitializer::Initialize()| within |CreateShell()|.
794
795 // Create the surface needed by rasterizer
796 PlatformViewNotifyCreated(shell.get());
797
798 auto configuration = RunConfiguration::InferFromSettings(settings);
799 ASSERT_TRUE(configuration.IsValid());
800 configuration.SetEntrypoint("reportTimingsMain");
801 fml::AutoResetWaitableEvent reportLatch;
802 std::vector<int64_t> timestamps;
803 auto nativeTimingCallback = [&reportLatch,
804 &timestamps](Dart_NativeArguments args) {
805 Dart_Handle exception = nullptr;
806 ASSERT_EQ(timestamps.size(), 0ul);
807 timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
808 args, 0, exception);
809 reportLatch.Signal();
810 };
811 AddNativeCallback("NativeReportTimingsCallback",
812 CREATE_NATIVE_ENTRY(nativeTimingCallback));
813 RunEngine(shell.get(), std::move(configuration));
814
815 // Pump many frames so we can trigger the report quickly instead of waiting
816 // for the 1 second threshold.
817 for (int i = 0; i < 200; i += 1) {
818 PumpOneFrame(shell.get());
819 }
820
821 reportLatch.Wait();
822 DestroyShell(std::move(shell));
823
825 ASSERT_TRUE(!timestamps.empty());
826 ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0);
827 std::vector<FrameTiming> timings(timestamps.size() / FrameTiming::kCount);
828
829 for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) {
830 for (auto phase : FrameTiming::kPhases) {
831 timings[i].Set(
832 phase,
834 timestamps[i * FrameTiming::kCount + phase])));
835 }
836 }
837 CheckFrameTimings(timings, start, finish);
838}
839
840TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) {
841 auto settings = CreateSettingsForFixture();
842
843 FrameTiming timing;
844 fml::AutoResetWaitableEvent timingLatch;
845 settings.frame_rasterized_callback = [&timing,
846 &timingLatch](const FrameTiming& t) {
847 timing = t;
848 timingLatch.Signal();
849 };
850
851 std::unique_ptr<Shell> shell = CreateShell(settings);
852
853 // Wait to make |start| bigger than zero
854 using namespace std::chrono_literals;
855 std::this_thread::sleep_for(1ms);
856
857 // We MUST put |start| after |CreateShell()| because the clock source will be
858 // reset through |TimePoint::SetClockSource()| in
859 // |DartVMInitializer::Initialize()| within |CreateShell()|.
861
862 for (auto phase : FrameTiming::kPhases) {
863 timing.Set(phase, fml::TimePoint());
864 // Check that the time points are initially smaller than start, so
865 // CheckFrameTimings will fail if they're not properly set later.
866 ASSERT_TRUE(timing.Get(phase) < start);
867 }
868
869 // Create the surface needed by rasterizer
870 PlatformViewNotifyCreated(shell.get());
871
872 auto configuration = RunConfiguration::InferFromSettings(settings);
873 configuration.SetEntrypoint("onBeginFrameMain");
874
875 int64_t frame_target_time;
876 auto nativeOnBeginFrame = [&frame_target_time](Dart_NativeArguments args) {
877 Dart_Handle exception = nullptr;
878 frame_target_time =
880 };
881 AddNativeCallback("NativeOnBeginFrame",
882 CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
883
884 RunEngine(shell.get(), std::move(configuration));
885 PumpOneFrame(shell.get());
886
887 // Check that timing is properly set. This implies that
888 // settings.frame_rasterized_callback is called.
889 timingLatch.Wait();
891 std::vector<FrameTiming> timings = {timing};
892 CheckFrameTimings(timings, start, finish);
893
894 // Check that onBeginFrame, which is the frame_target_time, is after
895 // FrameTiming's build start
896 int64_t build_start =
898 ASSERT_GT(frame_target_time, build_start);
899 DestroyShell(std::move(shell));
900}
901
902TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) {
903 auto settings = CreateSettingsForFixture();
904 fml::AutoResetWaitableEvent end_frame_latch;
905 bool end_frame_called = false;
906 auto end_frame_callback =
907 [&](bool should_resubmit_frame,
908 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
909 ASSERT_TRUE(raster_thread_merger.get() == nullptr);
910 ASSERT_FALSE(should_resubmit_frame);
911 end_frame_called = true;
912 end_frame_latch.Signal();
913 };
914 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
915 end_frame_callback, PostPrerollResult::kResubmitFrame, false);
916 auto shell = CreateShell({
917 .settings = settings,
918 .platform_view_create_callback = ShellTestPlatformViewBuilder({
919 .shell_test_external_view_embedder = external_view_embedder,
920 }),
921 });
922
923 // Create the surface needed by rasterizer
924 PlatformViewNotifyCreated(shell.get());
925
926 auto configuration = RunConfiguration::InferFromSettings(settings);
927 configuration.SetEntrypoint("emptyMain");
928
929 RunEngine(shell.get(), std::move(configuration));
930
931 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
932 auto display_list_layer = std::make_shared<DisplayListLayer>(
933 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
934 root->Add(display_list_layer);
935 };
936
937 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
938 end_frame_latch.Wait();
939 ASSERT_TRUE(end_frame_called);
940
941 DestroyShell(std::move(shell));
942}
943
944TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) {
945#if defined(OS_FUCHSIA)
946 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
947 "https://github.com/flutter/flutter/issues/59816 ";
948#else
949
950 auto settings = CreateSettingsForFixture();
951
952 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
953
954 fml::AutoResetWaitableEvent end_frame_latch;
955 bool end_frame_called = false;
956 std::vector<int64_t> visited_platform_views;
957 MutatorsStack stack_50;
958 MutatorsStack stack_75;
959 auto end_frame_callback =
960 [&](bool should_resubmit_frame,
961 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
962 if (end_frame_called) {
963 return;
964 }
965 ASSERT_TRUE(raster_thread_merger.get() == nullptr);
966 ASSERT_FALSE(should_resubmit_frame);
967 end_frame_called = true;
968 visited_platform_views =
969 external_view_embedder->GetVisitedPlatformViews();
970 stack_50 = external_view_embedder->GetStack(50);
971 stack_75 = external_view_embedder->GetStack(75);
972 end_frame_latch.Signal();
973 };
974
975 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
976 end_frame_callback, PostPrerollResult::kResubmitFrame, false);
977 auto shell = CreateShell({
978 .settings = settings,
979 .platform_view_create_callback = ShellTestPlatformViewBuilder({
980 .shell_test_external_view_embedder = external_view_embedder,
981 }),
982 });
983
984 // Create the surface needed by rasterizer
985 PlatformViewNotifyCreated(shell.get());
986
987 auto configuration = RunConfiguration::InferFromSettings(settings);
988 configuration.SetEntrypoint("emptyMain");
989
990 RunEngine(shell.get(), std::move(configuration));
991
992 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
993 auto platform_view_layer = std::make_shared<PlatformViewLayer>(
994 DlPoint(10, 10), DlSize(10, 10), 50);
995 root->Add(platform_view_layer);
996 auto transform_layer = std::make_shared<TransformLayer>(
997 DlMatrix::MakeTranslation({1.0f, 1.0f}));
998 root->Add(transform_layer);
999 auto clip_rect_layer = std::make_shared<ClipRectLayer>(
1000 DlRect::MakeLTRB(0, 0, 30, 30), Clip::kHardEdge);
1001 transform_layer->Add(clip_rect_layer);
1002 auto filter = DlImageFilter::MakeBlur(5, 5, DlTileMode::kClamp);
1003 auto backdrop_filter_layer =
1004 std::make_shared<BackdropFilterLayer>(filter, DlBlendMode::kSrcOver);
1005 clip_rect_layer->Add(backdrop_filter_layer);
1006 auto platform_view_layer2 = std::make_shared<PlatformViewLayer>(
1007 DlPoint(10, 10), DlSize(10, 10), 75);
1008 backdrop_filter_layer->Add(platform_view_layer2);
1009 };
1010
1011 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1012 end_frame_latch.Wait();
1013 ASSERT_EQ(visited_platform_views, (std::vector<int64_t>{50, 75}));
1014 ASSERT_TRUE(stack_75.is_empty());
1015 ASSERT_FALSE(stack_50.is_empty());
1016
1017 auto filter = DlImageFilter::MakeBlur(5, 5, DlTileMode::kClamp);
1018 auto mutator = *stack_50.Begin();
1019 ASSERT_EQ(mutator->GetType(), MutatorType::kBackdropFilter);
1020 ASSERT_EQ(mutator->GetFilterMutation().GetFilter(), *filter);
1021 // Make sure the filterRect is in global coordinates (contains the (1,1)
1022 // translation).
1023 ASSERT_EQ(mutator->GetFilterMutation().GetFilterRect(),
1024 DlRect::MakeLTRB(1, 1, 31, 31));
1025
1026 DestroyShell(std::move(shell));
1027#endif // OS_FUCHSIA
1028}
1029
1030// TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1032 ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit) {
1033#if defined(OS_FUCHSIA)
1034 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1035 "https://github.com/flutter/flutter/issues/59816 ";
1036#else
1037
1038 auto settings = CreateSettingsForFixture();
1039 fml::AutoResetWaitableEvent end_frame_latch;
1040 bool end_frame_called = false;
1041 auto end_frame_callback =
1042 [&](bool should_resubmit_frame,
1043 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1044 ASSERT_TRUE(raster_thread_merger.get() != nullptr);
1045 ASSERT_TRUE(should_resubmit_frame);
1046 end_frame_called = true;
1047 end_frame_latch.Signal();
1048 };
1049 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1050 end_frame_callback, PostPrerollResult::kResubmitFrame, true);
1051 auto shell = CreateShell({
1052 .settings = settings,
1053 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1054 .shell_test_external_view_embedder = external_view_embedder,
1055 }),
1056 });
1057
1058 // Create the surface needed by rasterizer
1059 PlatformViewNotifyCreated(shell.get());
1060
1061 auto configuration = RunConfiguration::InferFromSettings(settings);
1062 configuration.SetEntrypoint("emptyMain");
1063
1064 RunEngine(shell.get(), std::move(configuration));
1065
1066 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1067 auto display_list_layer = std::make_shared<DisplayListLayer>(
1068 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1069 root->Add(display_list_layer);
1070 };
1071
1072 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1073 end_frame_latch.Wait();
1074
1075 ASSERT_TRUE(end_frame_called);
1076
1077 DestroyShell(std::move(shell));
1078#endif // OS_FUCHSIA
1079}
1080
1081TEST_F(ShellTest, OnPlatformViewDestroyDisablesThreadMerger) {
1082#if defined(OS_FUCHSIA)
1083 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1084 "https://github.com/flutter/flutter/issues/59816 ";
1085#else
1086
1087 auto settings = CreateSettingsForFixture();
1088 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;
1089 auto end_frame_callback =
1090 [&](bool should_resubmit_frame,
1092 raster_thread_merger = std::move(thread_merger);
1093 };
1094 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1095 end_frame_callback, PostPrerollResult::kSuccess, true);
1096
1097 auto shell = CreateShell({
1098 .settings = settings,
1099 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1100 .shell_test_external_view_embedder = external_view_embedder,
1101 }),
1102 });
1103
1104 // Create the surface needed by rasterizer
1105 PlatformViewNotifyCreated(shell.get());
1106
1107 auto configuration = RunConfiguration::InferFromSettings(settings);
1108 configuration.SetEntrypoint("emptyMain");
1109
1110 RunEngine(shell.get(), std::move(configuration));
1111
1112 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1113 auto display_list_layer = std::make_shared<DisplayListLayer>(
1114 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1115 root->Add(display_list_layer);
1116 };
1117
1118 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1119
1120 auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1121 // Wait for the rasterizer to process the frame. WaitForFirstFrame only waits
1122 // for the Animator, but end_frame_callback is called by the Rasterizer.
1123 PostSync(shell->GetTaskRunners().GetRasterTaskRunner(), [] {});
1124 ASSERT_TRUE(result.ok()) << "Result: " << static_cast<int>(result.code())
1125 << ": " << result.message();
1126
1127 ASSERT_TRUE(raster_thread_merger->IsEnabled());
1128
1129 ValidateDestroyPlatformView(shell.get());
1130 ASSERT_TRUE(raster_thread_merger->IsEnabled());
1131
1132 // Validate the platform view can be recreated and destroyed again
1133 ValidateShell(shell.get());
1134 ASSERT_TRUE(raster_thread_merger->IsEnabled());
1135 DestroyShell(std::move(shell));
1136#endif // OS_FUCHSIA
1137}
1138
1139TEST_F(ShellTest, OnPlatformViewDestroyAfterMergingThreads) {
1140#if defined(OS_FUCHSIA)
1141 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1142 "https://github.com/flutter/flutter/issues/59816 ";
1143#else
1144
1145 const int ThreadMergingLease = 10;
1146 auto settings = CreateSettingsForFixture();
1147 fml::AutoResetWaitableEvent end_frame_latch;
1148 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1149
1150 auto end_frame_callback =
1151 [&](bool should_resubmit_frame,
1152 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1153 if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1154 raster_thread_merger->MergeWithLease(ThreadMergingLease);
1155
1156 ASSERT_TRUE(raster_thread_merger->IsMerged());
1157 external_view_embedder->UpdatePostPrerollResult(
1159 }
1160 end_frame_latch.Signal();
1161 };
1162 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1163 end_frame_callback, PostPrerollResult::kSuccess, true);
1164 // Set resubmit once to trigger thread merging.
1165 external_view_embedder->UpdatePostPrerollResult(
1167 auto shell = CreateShell({
1168 .settings = settings,
1169 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1170 .shell_test_external_view_embedder = external_view_embedder,
1171 }),
1172 });
1173
1174 // Create the surface needed by rasterizer
1175 PlatformViewNotifyCreated(shell.get());
1176
1177 auto configuration = RunConfiguration::InferFromSettings(settings);
1178 configuration.SetEntrypoint("emptyMain");
1179
1180 RunEngine(shell.get(), std::move(configuration));
1181
1182 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1183 auto display_list_layer = std::make_shared<DisplayListLayer>(
1184 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1185 root->Add(display_list_layer);
1186 };
1187
1188 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1189 // Pump one frame to trigger thread merging.
1190 end_frame_latch.Wait();
1191 // Pump another frame to ensure threads are merged and a regular layer tree is
1192 // submitted.
1193 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1194 // Threads are merged here. PlatformViewNotifyDestroy should be executed
1195 // successfully.
1197 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1198 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1199 ValidateDestroyPlatformView(shell.get());
1200
1201 // Ensure threads are unmerged after platform view destroy
1203 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1204 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1205
1206 // Validate the platform view can be recreated and destroyed again
1207 ValidateShell(shell.get());
1208
1209 DestroyShell(std::move(shell));
1210#endif // OS_FUCHSIA
1211}
1212
1213TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) {
1214#if defined(OS_FUCHSIA)
1215 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1216 "https://github.com/flutter/flutter/issues/59816 ";
1217#else
1218
1219 const int kThreadMergingLease = 10;
1220 auto settings = CreateSettingsForFixture();
1221 fml::AutoResetWaitableEvent end_frame_latch;
1222 auto end_frame_callback =
1223 [&](bool should_resubmit_frame,
1224 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1225 if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1226 raster_thread_merger->MergeWithLease(kThreadMergingLease);
1227 }
1228 end_frame_latch.Signal();
1229 };
1230 // Start with a regular layer tree with `PostPrerollResult::kSuccess` so we
1231 // can later check if the rasterizer is tore down using
1232 // |ValidateDestroyPlatformView|
1233 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1234 end_frame_callback, PostPrerollResult::kSuccess, true);
1235
1236 auto shell = CreateShell({
1237 .settings = settings,
1238 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1239 .shell_test_external_view_embedder = external_view_embedder,
1240 }),
1241 });
1242
1243 // Create the surface needed by rasterizer
1244 PlatformViewNotifyCreated(shell.get());
1245
1246 auto configuration = RunConfiguration::InferFromSettings(settings);
1247 configuration.SetEntrypoint("emptyMain");
1248
1249 RunEngine(shell.get(), std::move(configuration));
1250
1251 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1252 auto display_list_layer = std::make_shared<DisplayListLayer>(
1253 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1254 root->Add(display_list_layer);
1255 };
1256
1257 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1258 // Pump one frame and threads aren't merged
1259 end_frame_latch.Wait();
1261 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1262 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1263
1264 // Pump a frame with `PostPrerollResult::kResubmitFrame` to start merging
1265 // threads
1266 external_view_embedder->UpdatePostPrerollResult(
1268 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1269
1270 // Now destroy the platform view immediately.
1271 // Two things can happen here:
1272 // 1. Threads haven't merged. 2. Threads has already merged.
1273 // |Shell:OnPlatformViewDestroy| should be able to handle both cases.
1274 ValidateDestroyPlatformView(shell.get());
1275
1276 // Ensure threads are unmerged after platform view destroy
1278 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1279 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1280
1281 // Validate the platform view can be recreated and destroyed again
1282 ValidateShell(shell.get());
1283
1284 DestroyShell(std::move(shell));
1285#endif // OS_FUCHSIA
1286}
1287
1289 OnPlatformViewDestroyWithThreadMergerWhileThreadsAreUnmerged) {
1290#if defined(OS_FUCHSIA)
1291 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1292 "https://github.com/flutter/flutter/issues/59816 ";
1293#else
1294
1295 auto settings = CreateSettingsForFixture();
1296 fml::AutoResetWaitableEvent end_frame_latch;
1297 auto end_frame_callback =
1298 [&](bool should_resubmit_frame,
1299 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1300 end_frame_latch.Signal();
1301 };
1302 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1303 end_frame_callback, PostPrerollResult::kSuccess, true);
1304 auto shell = CreateShell({
1305 .settings = settings,
1306 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1307 .shell_test_external_view_embedder = external_view_embedder,
1308 }),
1309 });
1310
1311 // Create the surface needed by rasterizer
1312 PlatformViewNotifyCreated(shell.get());
1313
1314 auto configuration = RunConfiguration::InferFromSettings(settings);
1315 configuration.SetEntrypoint("emptyMain");
1316
1317 RunEngine(shell.get(), std::move(configuration));
1318
1319 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1320 auto display_list_layer = std::make_shared<DisplayListLayer>(
1321 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1322 root->Add(display_list_layer);
1323 };
1324 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1325 end_frame_latch.Wait();
1326
1327 // Threads should not be merged.
1329 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1330 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1331 ValidateDestroyPlatformView(shell.get());
1332
1333 // Ensure threads are unmerged after platform view destroy
1335 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1336 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1337
1338 // Validate the platform view can be recreated and destroyed again
1339 ValidateShell(shell.get());
1340
1341 DestroyShell(std::move(shell));
1342#endif // OS_FUCHSIA
1343}
1344
1345TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) {
1346 auto settings = CreateSettingsForFixture();
1347
1348 auto shell = CreateShell(settings);
1349
1350 // Create the surface needed by rasterizer
1351 PlatformViewNotifyCreated(shell.get());
1352
1353 auto configuration = RunConfiguration::InferFromSettings(settings);
1354 configuration.SetEntrypoint("emptyMain");
1355
1356 RunEngine(shell.get(), std::move(configuration));
1357
1358 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1359 auto display_list_layer = std::make_shared<DisplayListLayer>(
1360 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1361 root->Add(display_list_layer);
1362 };
1363 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1364
1365 // Threads should not be merged.
1367 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1368 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1369 ValidateDestroyPlatformView(shell.get());
1370
1371 // Ensure threads are unmerged after platform view destroy
1373 shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1374 shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1375
1376 // Validate the platform view can be recreated and destroyed again
1377 ValidateShell(shell.get());
1378
1379 DestroyShell(std::move(shell));
1380}
1381
1382// TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1383TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) {
1384#if defined(OS_FUCHSIA)
1385 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1386 "https://github.com/flutter/flutter/issues/59816 ";
1387#else
1388
1389 auto settings = CreateSettingsForFixture();
1390 fml::AutoResetWaitableEvent end_frame_latch;
1391 auto end_frame_callback =
1392 [&](bool should_resubmit_frame,
1393 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1394 end_frame_latch.Signal();
1395 };
1396 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1397 end_frame_callback, PostPrerollResult::kSuccess, true);
1398 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
1401 TaskRunners task_runners(
1402 "test",
1403 thread_host.platform_thread->GetTaskRunner(), // platform
1404 thread_host.platform_thread->GetTaskRunner(), // raster
1405 thread_host.ui_thread->GetTaskRunner(), // ui
1406 thread_host.io_thread->GetTaskRunner() // io
1407 );
1408 auto shell = CreateShell({
1409 .settings = settings,
1410 .task_runners = task_runners,
1411 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1412 .shell_test_external_view_embedder = external_view_embedder,
1413 }),
1414 });
1415
1416 // Create the surface needed by rasterizer
1417 PlatformViewNotifyCreated(shell.get());
1418
1419 auto configuration = RunConfiguration::InferFromSettings(settings);
1420 configuration.SetEntrypoint("emptyMain");
1421
1422 RunEngine(shell.get(), std::move(configuration));
1423
1424 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1425 auto display_list_layer = std::make_shared<DisplayListLayer>(
1426 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1427 root->Add(display_list_layer);
1428 };
1429 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1430 end_frame_latch.Wait();
1431
1432 ValidateDestroyPlatformView(shell.get());
1433
1434 // Validate the platform view can be recreated and destroyed again
1435 ValidateShell(shell.get());
1436
1437 DestroyShell(std::move(shell), task_runners);
1438#endif // OS_FUCHSIA
1439}
1440
1441TEST_F(ShellTest, GetUsedThisFrameShouldBeSetBeforeEndFrame) {
1442 auto settings = CreateSettingsForFixture();
1443 fml::AutoResetWaitableEvent end_frame_latch;
1444 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1445 bool used_this_frame = true;
1446 auto end_frame_callback =
1447 [&](bool should_resubmit_frame,
1448 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1449 // We expect `used_this_frame` to be false.
1450 used_this_frame = external_view_embedder->GetUsedThisFrame();
1451 end_frame_latch.Signal();
1452 };
1453 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1454 end_frame_callback, PostPrerollResult::kSuccess, true);
1455 auto shell = CreateShell({
1456 .settings = settings,
1457 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1458 .shell_test_external_view_embedder = external_view_embedder,
1459 }),
1460 });
1461
1462 // Create the surface needed by rasterizer
1463 PlatformViewNotifyCreated(shell.get());
1464
1465 auto configuration = RunConfiguration::InferFromSettings(settings);
1466 configuration.SetEntrypoint("emptyMain");
1467
1468 RunEngine(shell.get(), std::move(configuration));
1469
1470 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
1471 auto display_list_layer = std::make_shared<DisplayListLayer>(
1472 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
1473 root->Add(display_list_layer);
1474 };
1475 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
1476 end_frame_latch.Wait();
1477 ASSERT_FALSE(used_this_frame);
1478
1479 // Validate the platform view can be recreated and destroyed again
1480 ValidateShell(shell.get());
1481
1482 DestroyShell(std::move(shell));
1483}
1484
1485// TODO(https://github.com/flutter/flutter/issues/66056): Deflake on all other
1486// platforms
1487TEST_F(ShellTest, DISABLED_SkipAndSubmitFrame) {
1488#if defined(OS_FUCHSIA)
1489 GTEST_SKIP() << "RasterThreadMerger flakes on Fuchsia. "
1490 "https://github.com/flutter/flutter/issues/59816 ";
1491#else
1492
1493 auto settings = CreateSettingsForFixture();
1494 fml::AutoResetWaitableEvent end_frame_latch;
1495 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1496
1497 auto end_frame_callback =
1498 [&](bool should_resubmit_frame,
1499 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
1500 if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1501 raster_thread_merger->MergeWithLease(10);
1502 external_view_embedder->UpdatePostPrerollResult(
1504 }
1505 end_frame_latch.Signal();
1506 };
1507 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1508 end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true);
1509
1510 auto shell = CreateShell({
1511 .settings = settings,
1512 .platform_view_create_callback = ShellTestPlatformViewBuilder({
1513 .shell_test_external_view_embedder = external_view_embedder,
1514 }),
1515 });
1516
1517 PlatformViewNotifyCreated(shell.get());
1518
1519 auto configuration = RunConfiguration::InferFromSettings(settings);
1520 configuration.SetEntrypoint("emptyMain");
1521 RunEngine(shell.get(), std::move(configuration));
1522
1523 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1524
1525 PumpOneFrame(shell.get());
1526
1527 // `EndFrame` changed the post preroll result to `kSuccess`.
1528 end_frame_latch.Wait();
1529
1530 // Let the resubmitted frame to run and `GetSubmittedFrameCount` should be
1531 // called.
1532 end_frame_latch.Wait();
1533 // 2 frames are submitted because `kSkipAndRetryFrame`, but only the 2nd frame
1534 // should be submitted with `external_view_embedder`, hence the below check.
1535 ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1536
1537 PlatformViewNotifyDestroyed(shell.get());
1538 DestroyShell(std::move(shell));
1539#endif // OS_FUCHSIA
1540}
1541
1542TEST(SettingsTest, FrameTimingSetsAndGetsProperly) {
1543 // Ensure that all phases are in kPhases.
1544 ASSERT_EQ(sizeof(FrameTiming::kPhases),
1546
1547 int lastPhaseIndex = -1;
1548 FrameTiming timing;
1549 for (auto phase : FrameTiming::kPhases) {
1550 ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in order.
1551 lastPhaseIndex = phase;
1552 auto fake_time =
1554 timing.Set(phase, fake_time);
1555 ASSERT_TRUE(timing.Get(phase) == fake_time);
1556 }
1557}
1558
1559TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) {
1560 auto settings = CreateSettingsForFixture();
1561 std::unique_ptr<Shell> shell = CreateShell(settings);
1562
1563 // Create the surface needed by rasterizer
1564 PlatformViewNotifyCreated(shell.get());
1565
1566 auto configuration = RunConfiguration::InferFromSettings(settings);
1567 ASSERT_TRUE(configuration.IsValid());
1568 configuration.SetEntrypoint("reportTimingsMain");
1569 fml::AutoResetWaitableEvent reportLatch;
1570 std::vector<int64_t> timestamps;
1571 auto nativeTimingCallback = [&reportLatch,
1572 &timestamps](Dart_NativeArguments args) {
1573 Dart_Handle exception = nullptr;
1574 ASSERT_EQ(timestamps.size(), 0ul);
1575 timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
1576 args, 0, exception);
1577 reportLatch.Signal();
1578 };
1579 AddNativeCallback("NativeReportTimingsCallback",
1580 CREATE_NATIVE_ENTRY(nativeTimingCallback));
1581 ASSERT_TRUE(configuration.IsValid());
1582 RunEngine(shell.get(), std::move(configuration));
1583
1584 for (int i = 0; i < 10; i += 1) {
1585 PumpOneFrame(shell.get());
1586 }
1587
1588 reportLatch.Wait();
1589 DestroyShell(std::move(shell));
1590
1591 // Check for the immediate callback of the first frame that doesn't wait for
1592 // the other 9 frames to be rasterized.
1593 ASSERT_EQ(timestamps.size(), FrameTiming::kCount);
1594}
1595
1596TEST_F(ShellTest, WaitForFirstFrame) {
1597 auto settings = CreateSettingsForFixture();
1598 std::unique_ptr<Shell> shell = CreateShell(settings);
1599
1600 // Create the surface needed by rasterizer
1601 PlatformViewNotifyCreated(shell.get());
1602
1603 auto configuration = RunConfiguration::InferFromSettings(settings);
1604 configuration.SetEntrypoint("emptyMain");
1605
1606 RunEngine(shell.get(), std::move(configuration));
1607 PumpOneFrame(shell.get());
1608 fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1609 ASSERT_TRUE(result.ok());
1610
1611 DestroyShell(std::move(shell));
1612}
1613
1614TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) {
1615 auto settings = CreateSettingsForFixture();
1616 std::unique_ptr<Shell> shell = CreateShell(settings);
1617
1618 // Create the surface needed by rasterizer
1619 PlatformViewNotifyCreated(shell.get());
1620
1621 auto configuration = RunConfiguration::InferFromSettings(settings);
1622 configuration.SetEntrypoint("emptyMain");
1623
1624 RunEngine(shell.get(), std::move(configuration));
1625 PumpOneFrame(shell.get(), ViewContent::DummyView({1.0, 0.0, 0.0, 22, 0}));
1626 fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1627 EXPECT_FALSE(result.ok());
1628 EXPECT_EQ(result.message(), "timeout");
1629 EXPECT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1630
1631 DestroyShell(std::move(shell));
1632}
1633
1634TEST_F(ShellTest, WaitForFirstFrameTimeout) {
1635 auto settings = CreateSettingsForFixture();
1636 std::unique_ptr<Shell> shell = CreateShell(settings);
1637
1638 // Create the surface needed by rasterizer
1639 PlatformViewNotifyCreated(shell.get());
1640
1641 auto configuration = RunConfiguration::InferFromSettings(settings);
1642 configuration.SetEntrypoint("emptyMain");
1643
1644 RunEngine(shell.get(), std::move(configuration));
1645 fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1646 ASSERT_FALSE(result.ok());
1647 ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1648
1649 DestroyShell(std::move(shell));
1650}
1651
1652TEST_F(ShellTest, WaitForFirstFrameMultiple) {
1653 auto settings = CreateSettingsForFixture();
1654 std::unique_ptr<Shell> shell = CreateShell(settings);
1655
1656 // Create the surface needed by rasterizer
1657 PlatformViewNotifyCreated(shell.get());
1658
1659 auto configuration = RunConfiguration::InferFromSettings(settings);
1660 configuration.SetEntrypoint("emptyMain");
1661
1662 RunEngine(shell.get(), std::move(configuration));
1663 PumpOneFrame(shell.get());
1664 fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1665 ASSERT_TRUE(result.ok());
1666 for (int i = 0; i < 100; ++i) {
1667 result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1668 ASSERT_TRUE(result.ok());
1669 }
1670
1671 DestroyShell(std::move(shell));
1672}
1673
1674/// Makes sure that WaitForFirstFrame works if we rendered a frame with the
1675/// single-thread setup.
1676TEST_F(ShellTest, WaitForFirstFrameInlined) {
1677 Settings settings = CreateSettingsForFixture();
1678 auto task_runner = CreateNewThread();
1679 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1680 task_runner);
1681 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
1682
1683 // Create the surface needed by rasterizer
1684 PlatformViewNotifyCreated(shell.get());
1685
1686 auto configuration = RunConfiguration::InferFromSettings(settings);
1687 configuration.SetEntrypoint("emptyMain");
1688
1689 RunEngine(shell.get(), std::move(configuration));
1690 PumpOneFrame(shell.get());
1692 task_runner->PostTask([&shell, &event] {
1693 fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1694 ASSERT_FALSE(result.ok());
1695 ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition);
1696 event.Signal();
1697 });
1698 ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::Max()));
1699
1700 DestroyShell(std::move(shell), task_runners);
1701}
1702
1703static size_t GetRasterizerResourceCacheBytesSync(const Shell& shell) {
1704 size_t bytes = 0;
1707 shell.GetTaskRunners().GetRasterTaskRunner(), [&]() {
1708 if (auto rasterizer = shell.GetRasterizer()) {
1709 bytes = rasterizer->GetResourceCacheMaxBytes().value_or(0U);
1710 }
1711 latch.Signal();
1712 });
1713 latch.Wait();
1714 return bytes;
1715}
1716
1717TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) {
1718 TaskRunners task_runners = GetTaskRunnersForFixture();
1719 auto settings = CreateSettingsForFixture();
1720 settings.resource_cache_max_bytes_threshold = 4000000U;
1721 GrMockOptions main_context_options;
1722 sk_sp<GrDirectContext> main_context =
1723 GrDirectContext::MakeMock(&main_context_options);
1724 Shell::CreateCallback<PlatformView> platform_view_create_callback =
1725 [task_runners, main_context](flutter::Shell& shell) {
1726 auto result = std::make_unique<TestPlatformView>(shell, task_runners);
1727 ON_CALL(*result, CreateRenderingSurface())
1728 .WillByDefault(::testing::Invoke([main_context] {
1729 auto surface = std::make_unique<MockSurface>();
1730 ON_CALL(*surface, GetContext())
1731 .WillByDefault(Return(main_context.get()));
1732 ON_CALL(*surface, IsValid()).WillByDefault(Return(true));
1733 ON_CALL(*surface, MakeRenderContextCurrent())
1734 .WillByDefault(::testing::Invoke([] {
1735 return std::make_unique<GLContextDefaultResult>(true);
1736 }));
1737 return surface;
1738 }));
1739 return result;
1740 };
1741
1742 auto shell = CreateShell({
1743 .settings = settings,
1744 .task_runners = task_runners,
1745 .platform_view_create_callback = platform_view_create_callback,
1746 });
1747
1748 // Create the surface needed by rasterizer
1749 PlatformViewNotifyCreated(shell.get());
1750
1751 auto configuration = RunConfiguration::InferFromSettings(settings);
1752 configuration.SetEntrypoint("emptyMain");
1753
1754 RunEngine(shell.get(), std::move(configuration));
1755 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1756 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
1757 {1.0, 100, 100, 22, 0});
1758 });
1759
1760 // first cache bytes
1761 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1762 static_cast<size_t>(480000U));
1763
1764 auto shell_spawn_callback = [&]() {
1765 std::unique_ptr<Shell> spawn;
1766 PostSync(
1767 shell->GetTaskRunners().GetPlatformTaskRunner(),
1768 [this, &spawn, &spawner = shell, platform_view_create_callback]() {
1769 auto configuration =
1770 RunConfiguration::InferFromSettings(CreateSettingsForFixture());
1771 configuration.SetEntrypoint("emptyMain");
1772 spawn = spawner->Spawn(
1773 std::move(configuration), "", platform_view_create_callback,
1774 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
1775 ASSERT_NE(nullptr, spawn.get());
1776 ASSERT_TRUE(ValidateShell(spawn.get()));
1777 });
1778 return spawn;
1779 };
1780
1781 std::unique_ptr<Shell> second_shell = shell_spawn_callback();
1782 PlatformViewNotifyCreated(second_shell.get());
1783 PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
1784 [&second_shell]() {
1785 second_shell->GetPlatformView()->SetViewportMetrics(
1786 kImplicitViewId, {1.0, 100, 100, 22, 0});
1787 });
1788 // first cache bytes + second cache bytes
1789 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1790 static_cast<size_t>(960000U));
1791
1792 PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
1793 [&second_shell]() {
1794 second_shell->GetPlatformView()->SetViewportMetrics(
1795 kImplicitViewId, {1.0, 100, 300, 22, 0});
1796 });
1797 // first cache bytes + second cache bytes
1798 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1799 static_cast<size_t>(1920000U));
1800
1801 std::unique_ptr<Shell> third_shell = shell_spawn_callback();
1802 PlatformViewNotifyCreated(third_shell.get());
1803 PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(),
1804 [&third_shell]() {
1805 third_shell->GetPlatformView()->SetViewportMetrics(
1806 kImplicitViewId, {1.0, 400, 100, 22, 0});
1807 });
1808 // first cache bytes + second cache bytes + third cache bytes
1809 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1810 static_cast<size_t>(3840000U));
1811
1812 PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(),
1813 [&third_shell]() {
1814 third_shell->GetPlatformView()->SetViewportMetrics(
1815 kImplicitViewId, {1.0, 800, 100, 22, 0});
1816 });
1817 // max bytes threshold
1818 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1819 static_cast<size_t>(4000000U));
1820 DestroyShell(std::move(third_shell), task_runners);
1821 // max bytes threshold
1822 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1823 static_cast<size_t>(4000000U));
1824
1825 PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
1826 [&second_shell]() {
1827 second_shell->GetPlatformView()->SetViewportMetrics(
1828 kImplicitViewId, {1.0, 100, 100, 22, 0});
1829 });
1830 // first cache bytes + second cache bytes
1831 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1832 static_cast<size_t>(960000U));
1833
1834 DestroyShell(std::move(second_shell), task_runners);
1835 DestroyShell(std::move(shell), task_runners);
1836}
1837
1838TEST_F(ShellTest, SetResourceCacheSize) {
1839 Settings settings = CreateSettingsForFixture();
1840 auto task_runner = CreateNewThread();
1841 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1842 task_runner);
1843 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
1844
1845 // Create the surface needed by rasterizer
1846 PlatformViewNotifyCreated(shell.get());
1847
1848 auto configuration = RunConfiguration::InferFromSettings(settings);
1849 configuration.SetEntrypoint("emptyMain");
1850
1851 RunEngine(shell.get(), std::move(configuration));
1852 PumpOneFrame(shell.get());
1853
1854 // The Vulkan and GL backends set different default values for the resource
1855 // cache size. The default backend (specified by the default param of
1856 // `CreateShell` in this test) will only resolve to Vulkan (in
1857 // `ShellTestPlatformView::Create`) if GL is disabled. This situation arises
1858 // when targeting the Fuchsia Emulator.
1859#if defined(SHELL_ENABLE_VULKAN) && !defined(SHELL_ENABLE_GL)
1860 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1862#elif defined(SHELL_ENABLE_METAL)
1863 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1864 static_cast<size_t>(256 * (1 << 20)));
1865#else
1866 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1867 static_cast<size_t>(24 * (1 << 20)));
1868#endif
1869
1871 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1872 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
1873 {1.0, 400, 200, 22, 0});
1874 });
1875 PumpOneFrame(shell.get());
1876
1877 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 3840000U);
1878
1879 std::string request_json = R"json({
1880 "method": "Skia.setResourceCacheMaxBytes",
1881 "args": 10000
1882 })json";
1883 auto data =
1884 fml::MallocMapping::Copy(request_json.c_str(), request_json.length());
1885 auto platform_message = std::make_unique<PlatformMessage>(
1886 "flutter/skia", std::move(data), nullptr);
1887 SendEnginePlatformMessage(shell.get(), std::move(platform_message));
1888 PumpOneFrame(shell.get());
1889 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1890
1892 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1893 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
1894 {1.0, 800, 400, 22, 0});
1895 });
1896 PumpOneFrame(shell.get());
1897
1898 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1899 DestroyShell(std::move(shell), task_runners);
1900}
1901
1902TEST_F(ShellTest, SetResourceCacheSizeEarly) {
1903 Settings settings = CreateSettingsForFixture();
1904 auto task_runner = CreateNewThread();
1905 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1906 task_runner);
1907 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
1908
1910 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1911 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
1912 {1.0, 400, 200, 22, 0});
1913 });
1914 PumpOneFrame(shell.get());
1915
1916 // Create the surface needed by rasterizer
1917 PlatformViewNotifyCreated(shell.get());
1918
1919 auto configuration = RunConfiguration::InferFromSettings(settings);
1920 configuration.SetEntrypoint("emptyMain");
1921
1922 RunEngine(shell.get(), std::move(configuration));
1923 PumpOneFrame(shell.get());
1924
1925 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1926 static_cast<size_t>(3840000U));
1927 DestroyShell(std::move(shell), task_runners);
1928}
1929
1930TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) {
1931 Settings settings = CreateSettingsForFixture();
1932 auto task_runner = CreateNewThread();
1933 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1934 task_runner);
1935 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
1936
1938 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1939 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
1940 {1.0, 400, 200, 22, 0});
1941 });
1942 PumpOneFrame(shell.get());
1943
1944 // Create the surface needed by rasterizer
1945 PlatformViewNotifyCreated(shell.get());
1946
1947 auto configuration = RunConfiguration::InferFromSettings(settings);
1948 configuration.SetEntrypoint("testSkiaResourceCacheSendsResponse");
1949
1950 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1951 static_cast<size_t>(3840000U));
1952
1954 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
1955 latch.Signal();
1956 }));
1957
1958 RunEngine(shell.get(), std::move(configuration));
1959 PumpOneFrame(shell.get());
1960
1961 latch.Wait();
1962
1963 EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1964 static_cast<size_t>(10000U));
1965 DestroyShell(std::move(shell), task_runners);
1966}
1967
1968TEST_F(ShellTest, CanCreateImagefromDecompressedBytes) {
1969 Settings settings = CreateSettingsForFixture();
1970 auto task_runner = CreateNewThread();
1971
1972 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1973 task_runner);
1974
1975 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
1976
1977 // Create the surface needed by rasterizer
1978 PlatformViewNotifyCreated(shell.get());
1979
1980 auto configuration = RunConfiguration::InferFromSettings(settings);
1981 configuration.SetEntrypoint("canCreateImageFromDecompressedData");
1982
1984 AddNativeCallback("NotifyWidthHeight",
1985 CREATE_NATIVE_ENTRY([&latch](auto args) {
1987 Dart_GetNativeArgument(args, 0));
1989 Dart_GetNativeArgument(args, 1));
1990 ASSERT_EQ(width, 10);
1991 ASSERT_EQ(height, 10);
1992 latch.Signal();
1993 }));
1994
1995 RunEngine(shell.get(), std::move(configuration));
1996
1997 latch.Wait();
1998 DestroyShell(std::move(shell), task_runners);
1999}
2000
2001class MockTexture : public Texture {
2002 public:
2003 MockTexture(int64_t textureId,
2004 std::shared_ptr<fml::AutoResetWaitableEvent> latch)
2005 : Texture(textureId), latch_(std::move(latch)) {}
2006
2007 ~MockTexture() override = default;
2008
2009 // Called from raster thread.
2010 void Paint(PaintContext& context,
2011 const DlRect& bounds,
2012 bool freeze,
2013 const DlImageSampling) override {}
2014
2015 void OnGrContextCreated() override {}
2016
2017 void OnGrContextDestroyed() override {}
2018
2019 void MarkNewFrameAvailable() override {
2020 frames_available_++;
2021 latch_->Signal();
2022 }
2023
2024 void OnTextureUnregistered() override {
2025 unregistered_ = true;
2026 latch_->Signal();
2027 }
2028
2029 bool unregistered() { return unregistered_; }
2030 int frames_available() { return frames_available_; }
2031
2032 private:
2033 bool unregistered_ = false;
2034 int frames_available_ = 0;
2035 std::shared_ptr<fml::AutoResetWaitableEvent> latch_;
2036};
2037
2038TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) {
2039 Settings settings = CreateSettingsForFixture();
2040 auto configuration = RunConfiguration::InferFromSettings(settings);
2041 auto task_runner = CreateNewThread();
2042 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2043 task_runner);
2044 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2045
2046 ASSERT_TRUE(ValidateShell(shell.get()));
2047 PlatformViewNotifyCreated(shell.get());
2048
2049 RunEngine(shell.get(), std::move(configuration));
2050
2051 std::shared_ptr<fml::AutoResetWaitableEvent> latch =
2052 std::make_shared<fml::AutoResetWaitableEvent>();
2053
2054 std::shared_ptr<MockTexture> mockTexture =
2055 std::make_shared<MockTexture>(0, latch);
2056
2058 shell->GetTaskRunners().GetRasterTaskRunner(), [&]() {
2059 shell->GetPlatformView()->RegisterTexture(mockTexture);
2060 shell->GetPlatformView()->MarkTextureFrameAvailable(0);
2061 });
2062 latch->Wait();
2063
2064 EXPECT_EQ(mockTexture->frames_available(), 1);
2065
2067 shell->GetTaskRunners().GetRasterTaskRunner(),
2068 [&]() { shell->GetPlatformView()->UnregisterTexture(0); });
2069 latch->Wait();
2070
2071 EXPECT_EQ(mockTexture->unregistered(), true);
2072 DestroyShell(std::move(shell), task_runners);
2073}
2074
2075TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) {
2076 const std::string message = "dummy isolate launch data.";
2077
2078 Settings settings = CreateSettingsForFixture();
2079 settings.persistent_isolate_data =
2080 std::make_shared<fml::DataMapping>(message);
2081 TaskRunners task_runners("test", // label
2082 GetCurrentTaskRunner(), // platform
2083 CreateNewThread(), // raster
2084 CreateNewThread(), // ui
2085 CreateNewThread() // io
2086 );
2087
2088 fml::AutoResetWaitableEvent message_latch;
2089 AddNativeCallback("NotifyMessage",
2090 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2091 const auto message_from_dart =
2093 Dart_GetNativeArgument(args, 0));
2094 ASSERT_EQ(message, message_from_dart);
2095 message_latch.Signal();
2096 }));
2097
2098 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2099
2100 ASSERT_TRUE(shell->IsSetup());
2101 auto configuration = RunConfiguration::InferFromSettings(settings);
2102 configuration.SetEntrypoint("canAccessIsolateLaunchData");
2103
2105 shell->RunEngine(std::move(configuration), [&](auto result) {
2106 ASSERT_EQ(result, Engine::RunStatus::Success);
2107 });
2108
2109 message_latch.Wait();
2110 DestroyShell(std::move(shell), task_runners);
2111}
2112
2113TEST_F(ShellTest, CanScheduleFrameFromPlatform) {
2114 Settings settings = CreateSettingsForFixture();
2115 TaskRunners task_runners = GetTaskRunnersForFixture();
2117 AddNativeCallback(
2118 "NotifyNative",
2119 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
2120 fml::AutoResetWaitableEvent check_latch;
2121 AddNativeCallback("NativeOnBeginFrame",
2122 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2123 check_latch.Signal();
2124 }));
2125 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2126 ASSERT_TRUE(shell->IsSetup());
2127
2128 auto configuration = RunConfiguration::InferFromSettings(settings);
2129 configuration.SetEntrypoint("onBeginFrameWithNotifyNativeMain");
2130 RunEngine(shell.get(), std::move(configuration));
2131
2132 // Wait for the application to attach the listener.
2133 latch.Wait();
2134
2136 shell->GetTaskRunners().GetPlatformTaskRunner(),
2137 [&shell]() { shell->GetPlatformView()->ScheduleFrame(); });
2138 check_latch.Wait();
2139 DestroyShell(std::move(shell), task_runners);
2140}
2141
2142TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) {
2143 bool is_on_begin_frame_called = false;
2144 bool is_secondary_callback_called = false;
2145 bool test_started = false;
2146 Settings settings = CreateSettingsForFixture();
2147 TaskRunners task_runners = GetTaskRunnersForFixture();
2149 AddNativeCallback(
2150 "NotifyNative",
2151 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
2152 fml::CountDownLatch count_down_latch(2);
2153 AddNativeCallback("NativeOnBeginFrame",
2154 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2155 if (!test_started) {
2156 return;
2157 }
2158 EXPECT_FALSE(is_on_begin_frame_called);
2159 EXPECT_FALSE(is_secondary_callback_called);
2160 is_on_begin_frame_called = true;
2161 count_down_latch.CountDown();
2162 }));
2163 std::unique_ptr<Shell> shell = CreateShell({
2164 .settings = settings,
2165 .task_runners = task_runners,
2166 });
2167 ASSERT_TRUE(shell->IsSetup());
2168
2169 auto configuration = RunConfiguration::InferFromSettings(settings);
2170 configuration.SetEntrypoint("onBeginFrameWithNotifyNativeMain");
2171 RunEngine(shell.get(), std::move(configuration));
2172
2173 // Wait for the application to attach the listener.
2174 latch.Wait();
2175
2177 shell->GetTaskRunners().GetUITaskRunner(), [&]() {
2178 shell->GetEngine()->ScheduleSecondaryVsyncCallback(0, [&]() {
2179 if (!test_started) {
2180 return;
2181 }
2182 EXPECT_TRUE(is_on_begin_frame_called);
2183 EXPECT_FALSE(is_secondary_callback_called);
2184 is_secondary_callback_called = true;
2185 count_down_latch.CountDown();
2186 });
2187 shell->GetEngine()->ScheduleFrame();
2188 test_started = true;
2189 });
2190 count_down_latch.Wait();
2191 EXPECT_TRUE(is_on_begin_frame_called);
2192 EXPECT_TRUE(is_secondary_callback_called);
2193 DestroyShell(std::move(shell), task_runners);
2194}
2195
2196static void LogSkData(const sk_sp<SkData>& data, const char* title) {
2197 FML_LOG(ERROR) << "---------- " << title;
2198 std::ostringstream ostr;
2199 for (size_t i = 0; i < data->size();) {
2200 ostr << std::hex << std::setfill('0') << std::setw(2)
2201 << static_cast<int>(data->bytes()[i]) << " ";
2202 i++;
2203 if (i % 16 == 0 || i == data->size()) {
2204 FML_LOG(ERROR) << ostr.str();
2205 ostr.str("");
2206 ostr.clear();
2207 }
2208 }
2209}
2210
2211TEST_F(ShellTest, Screenshot) {
2212 auto settings = CreateSettingsForFixture();
2213 fml::AutoResetWaitableEvent firstFrameLatch;
2214 settings.frame_rasterized_callback =
2215 [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); };
2216
2217 std::unique_ptr<Shell> shell = CreateShell(settings);
2218
2219 // Create the surface needed by rasterizer
2220 PlatformViewNotifyCreated(shell.get());
2221
2222 auto configuration = RunConfiguration::InferFromSettings(settings);
2223 configuration.SetEntrypoint("emptyMain");
2224
2225 RunEngine(shell.get(), std::move(configuration));
2226
2227 LayerTreeBuilder builder = [&](const std::shared_ptr<ContainerLayer>& root) {
2228 auto display_list_layer = std::make_shared<DisplayListLayer>(
2229 DlPoint(10, 10), MakeSizedDisplayList(80, 80), false, false);
2230 root->Add(display_list_layer);
2231 };
2232
2233 PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
2234 firstFrameLatch.Wait();
2235
2236 std::promise<Rasterizer::Screenshot> screenshot_promise;
2237 auto screenshot_future = screenshot_promise.get_future();
2238
2240 shell->GetTaskRunners().GetRasterTaskRunner(),
2241 [&screenshot_promise, &shell]() {
2242 auto rasterizer = shell->GetRasterizer();
2243 screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree(
2244 Rasterizer::ScreenshotType::CompressedImage, false));
2245 });
2246
2247 auto fixtures_dir =
2249
2250 auto reference_png = fml::FileMapping::CreateReadOnly(
2251 fixtures_dir, "shelltest_screenshot.png");
2252
2253 // Use MakeWithoutCopy instead of MakeWithCString because we don't want to
2254 // encode the null sentinel
2255 sk_sp<SkData> reference_data = SkData::MakeWithoutCopy(
2256 reference_png->GetMapping(), reference_png->GetSize());
2257
2258 sk_sp<SkData> screenshot_data = screenshot_future.get().data;
2259 if (!reference_data->equals(screenshot_data.get())) {
2260 LogSkData(reference_data, "reference");
2261 LogSkData(screenshot_data, "screenshot");
2262 ASSERT_TRUE(false);
2263 }
2264
2265 DestroyShell(std::move(shell));
2266}
2267
2268// Compares local times as seen by the dart isolate and as seen by this test
2269// fixture, to a resolution of 1 hour.
2270//
2271// This verifies that (1) the isolate is able to get a timezone (doesn't lock
2272// up for example), and (2) that the host and the isolate agree on what the
2273// timezone is.
2274TEST_F(ShellTest, LocaltimesMatch) {
2276 std::string dart_isolate_time_str;
2277
2278 // See fixtures/shell_test.dart, the callback NotifyLocalTime is declared
2279 // there.
2280 AddNativeCallback("NotifyLocalTime", CREATE_NATIVE_ENTRY([&](auto args) {
2281 dart_isolate_time_str =
2283 Dart_GetNativeArgument(args, 0));
2284 latch.Signal();
2285 }));
2286
2287 auto settings = CreateSettingsForFixture();
2288 auto configuration = RunConfiguration::InferFromSettings(settings);
2289 configuration.SetEntrypoint("localtimesMatch");
2290 std::unique_ptr<Shell> shell = CreateShell(settings);
2291 ASSERT_NE(shell.get(), nullptr);
2292 RunEngine(shell.get(), std::move(configuration));
2293 latch.Wait();
2294
2295 char timestr[200];
2296 const time_t timestamp = time(nullptr);
2297 const struct tm* local_time = localtime(&timestamp);
2298 ASSERT_NE(local_time, nullptr)
2299 << "Could not get local time: errno=" << errno << ": " << strerror(errno);
2300 // Example: "2020-02-26 14" for 2pm on February 26, 2020.
2301 const size_t format_size =
2302 strftime(timestr, sizeof(timestr), "%Y-%m-%d %H", local_time);
2303 ASSERT_NE(format_size, 0UL)
2304 << "strftime failed: host time: " << std::string(timestr)
2305 << " dart isolate time: " << dart_isolate_time_str;
2306
2307 const std::string host_local_time_str = timestr;
2308
2309 ASSERT_EQ(dart_isolate_time_str, host_local_time_str)
2310 << "Local times in the dart isolate and the local time seen by the test "
2311 << "differ by more than 1 hour, but are expected to be about equal";
2312
2313 DestroyShell(std::move(shell));
2314}
2315
2316/// An image generator that always creates a 1x1 single-frame green image.
2318 public:
2320 : info_(SkImageInfo::MakeN32(1, 1, SkAlphaType::kOpaque_SkAlphaType)) {};
2322 const SkImageInfo& GetInfo() { return info_; }
2323
2324 unsigned int GetFrameCount() const { return 1; }
2325
2326 unsigned int GetPlayCount() const { return 1; }
2327
2328 const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) {
2329 return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep};
2330 }
2331
2332 SkISize GetScaledDimensions(float scale) {
2333 return SkISize::Make(info_.width(), info_.height());
2334 }
2335
2336 bool GetPixels(const SkImageInfo& info,
2337 void* pixels,
2338 size_t row_bytes,
2339 unsigned int frame_index,
2340 std::optional<unsigned int> prior_frame) {
2341 assert(info.width() == 1);
2342 assert(info.height() == 1);
2343 assert(row_bytes == 4);
2344
2345 reinterpret_cast<uint32_t*>(pixels)[0] = 0x00ff00ff;
2346 return true;
2347 };
2348
2349 private:
2350 SkImageInfo info_;
2351};
2352
2353TEST_F(ShellTest, CanRegisterImageDecoders) {
2355 AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
2357 Dart_GetNativeArgument(args, 0));
2359 Dart_GetNativeArgument(args, 1));
2360 ASSERT_EQ(width, 1);
2361 ASSERT_EQ(height, 1);
2362 latch.Signal();
2363 }));
2364
2365 auto settings = CreateSettingsForFixture();
2366 auto configuration = RunConfiguration::InferFromSettings(settings);
2367 configuration.SetEntrypoint("canRegisterImageDecoders");
2368 std::unique_ptr<Shell> shell = CreateShell(settings);
2369 ASSERT_NE(shell.get(), nullptr);
2370
2372 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
2373 shell->RegisterImageDecoder(
2374 [](const sk_sp<SkData>& buffer) {
2375 return std::make_unique<SinglePixelImageGenerator>();
2376 },
2377 100);
2378 });
2379
2380 RunEngine(shell.get(), std::move(configuration));
2381 latch.Wait();
2382 DestroyShell(std::move(shell));
2383}
2384
2385TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) {
2387 ASSERT_TRUE(base_dir.fd().is_valid());
2388 PersistentCache::SetCacheDirectoryPath(base_dir.path());
2389 PersistentCache::ResetCacheForProcess();
2390
2391 // Create 2 dummy SkSL cache file IE (base32 encoding of A), II (base32
2392 // encoding of B) with content x and y.
2393 std::vector<std::string> components = {
2394 "flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion(),
2395 PersistentCache::kSkSLSubdirName};
2396 auto sksl_dir = fml::CreateDirectory(base_dir.fd(), components,
2398 const std::string x_key_str = "A";
2399 const std::string x_value_str = "x";
2400 sk_sp<SkData> x_key =
2401 SkData::MakeWithCopy(x_key_str.data(), x_key_str.size());
2402 sk_sp<SkData> x_value =
2403 SkData::MakeWithCopy(x_value_str.data(), x_value_str.size());
2404 auto x_data = PersistentCache::BuildCacheObject(*x_key, *x_value);
2405
2406 const std::string y_key_str = "B";
2407 const std::string y_value_str = "y";
2408 sk_sp<SkData> y_key =
2409 SkData::MakeWithCopy(y_key_str.data(), y_key_str.size());
2410 sk_sp<SkData> y_value =
2411 SkData::MakeWithCopy(y_value_str.data(), y_value_str.size());
2412 auto y_data = PersistentCache::BuildCacheObject(*y_key, *y_value);
2413
2414 ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "x_cache", *x_data));
2415 ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "y_cache", *y_data));
2416
2417 Settings settings = CreateSettingsForFixture();
2418 std::unique_ptr<Shell> shell = CreateShell(settings);
2420 rapidjson::Document document;
2421 OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs,
2422 shell->GetTaskRunners().GetIOTaskRunner(), empty_params,
2423 &document);
2424 rapidjson::StringBuffer buffer;
2425 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2426 document.Accept(writer);
2427 DestroyShell(std::move(shell));
2428
2429 const std::string expected_json1 =
2430 "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}";
2431 const std::string expected_json2 =
2432 "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"IE\":\"eA==\",\"II\":\"eQ==\"}}";
2433 bool json_is_expected = (expected_json1 == buffer.GetString()) ||
2434 (expected_json2 == buffer.GetString());
2435 ASSERT_TRUE(json_is_expected) << buffer.GetString() << " is not equal to "
2436 << expected_json1 << " or " << expected_json2;
2437}
2438
2439TEST_F(ShellTest, RasterizerScreenshot) {
2440 Settings settings = CreateSettingsForFixture();
2441 auto configuration = RunConfiguration::InferFromSettings(settings);
2442 auto task_runner = CreateNewThread();
2443 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2444 task_runner);
2445 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2446
2447 ASSERT_TRUE(ValidateShell(shell.get()));
2448 PlatformViewNotifyCreated(shell.get());
2449
2450 RunEngine(shell.get(), std::move(configuration));
2451
2452 auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
2453
2454 PumpOneFrame(shell.get());
2455
2457 shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
2458 Rasterizer::Screenshot screenshot =
2459 shell->GetRasterizer()->ScreenshotLastLayerTree(
2460 Rasterizer::ScreenshotType::CompressedImage, true);
2461 EXPECT_NE(screenshot.data, nullptr);
2462
2463 latch->Signal();
2464 });
2465 latch->Wait();
2466 DestroyShell(std::move(shell), task_runners);
2467}
2468
2469TEST_F(ShellTest, RasterizerMakeRasterSnapshot) {
2470 Settings settings = CreateSettingsForFixture();
2471 auto configuration = RunConfiguration::InferFromSettings(settings);
2472 auto task_runner = CreateNewThread();
2473 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2474 task_runner);
2475 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2476
2477 ASSERT_TRUE(ValidateShell(shell.get()));
2478 PlatformViewNotifyCreated(shell.get());
2479
2480 RunEngine(shell.get(), std::move(configuration));
2481
2482 auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
2483
2484 PumpOneFrame(shell.get());
2485
2487 shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
2488 SnapshotDelegate* delegate =
2489 reinterpret_cast<Rasterizer*>(shell->GetRasterizer().get());
2490 sk_sp<DlImage> image = delegate->MakeRasterSnapshotSync(
2491 MakeSizedDisplayList(50, 50), DlISize(50, 50));
2492 EXPECT_NE(image, nullptr);
2493
2494 latch->Signal();
2495 });
2496 latch->Wait();
2497 DestroyShell(std::move(shell), task_runners);
2498}
2499
2500TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
2501 Settings settings = CreateSettingsForFixture();
2502 std::unique_ptr<Shell> shell = CreateShell(settings);
2503
2504 // 1. Construct a picture and a picture layer to be raster cached.
2505 sk_sp<DisplayList> display_list = MakeSizedDisplayList(10, 10);
2506 auto display_list_layer = std::make_shared<DisplayListLayer>(
2507 DlPoint(0, 0), MakeSizedDisplayList(100, 100), false, false);
2508 display_list_layer->set_paint_bounds(DlRect::MakeWH(100, 100));
2509
2510 // 2. Rasterize the picture and the picture layer in the raster cache.
2511 std::promise<bool> rasterized;
2512
2513 shell->GetTaskRunners().GetRasterTaskRunner()->PostTask(
2514 [&shell, &rasterized, &display_list, &display_list_layer] {
2515 std::vector<RasterCacheItem*> raster_cache_items;
2516 auto* compositor_context = shell->GetRasterizer()->compositor_context();
2517 auto& raster_cache = compositor_context->raster_cache();
2518
2519 LayerStateStack state_stack;
2520 FixedRefreshRateStopwatch raster_time;
2522 PaintContext paint_context = {
2523 // clang-format off
2524 .state_stack = state_stack,
2525 .canvas = nullptr,
2526 .gr_context = nullptr,
2527 .dst_color_space = nullptr,
2528 .view_embedder = nullptr,
2529 .raster_time = raster_time,
2530 .ui_time = ui_time,
2531 .texture_registry = nullptr,
2532 .raster_cache = &raster_cache,
2533 // clang-format on
2534 };
2535
2536 PrerollContext preroll_context = {
2537 // clang-format off
2538 .raster_cache = &raster_cache,
2539 .gr_context = nullptr,
2540 .view_embedder = nullptr,
2541 .state_stack = state_stack,
2542 .dst_color_space = nullptr,
2543 .surface_needs_readback = false,
2544 .raster_time = raster_time,
2545 .ui_time = ui_time,
2546 .texture_registry = nullptr,
2547 .has_platform_view = false,
2548 .has_texture_layer = false,
2549 .raster_cached_entries = &raster_cache_items,
2550 // clang-format on
2551 };
2552
2553 // 2.1. Rasterize the picture. Call Draw multiple times to pass the
2554 // access threshold (default to 3) so a cache can be generated.
2555 DisplayListBuilder dummy_canvas;
2556 DlPaint paint;
2557 bool picture_cache_generated;
2558 DisplayListRasterCacheItem display_list_raster_cache_item(
2559 display_list, SkPoint(), true, false);
2560 for (int i = 0; i < 4; i += 1) {
2561 DlMatrix matrix;
2562 state_stack.set_preroll_delegate(matrix);
2563 display_list_raster_cache_item.PrerollSetup(&preroll_context, matrix);
2564 display_list_raster_cache_item.PrerollFinalize(&preroll_context,
2565 matrix);
2566 picture_cache_generated =
2567 display_list_raster_cache_item.need_caching();
2568 state_stack.set_delegate(&dummy_canvas);
2569 display_list_raster_cache_item.TryToPrepareRasterCache(paint_context);
2570 display_list_raster_cache_item.Draw(paint_context, &dummy_canvas,
2571 &paint);
2572 }
2573 ASSERT_TRUE(picture_cache_generated);
2574
2575 // 2.2. Rasterize the picture layer.
2576 LayerRasterCacheItem layer_raster_cache_item(display_list_layer.get());
2577 state_stack.set_preroll_delegate(DlMatrix());
2578 layer_raster_cache_item.PrerollSetup(&preroll_context, DlMatrix());
2579 layer_raster_cache_item.PrerollFinalize(&preroll_context, DlMatrix());
2580 state_stack.set_delegate(&dummy_canvas);
2581 layer_raster_cache_item.TryToPrepareRasterCache(paint_context);
2582 layer_raster_cache_item.Draw(paint_context, &dummy_canvas, &paint);
2583 rasterized.set_value(true);
2584 });
2585 rasterized.get_future().wait();
2586
2587 // 3. Call the service protocol and check its output.
2589 rapidjson::Document document;
2590 OnServiceProtocol(
2591 shell.get(), ServiceProtocolEnum::kEstimateRasterCacheMemory,
2592 shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2593 rapidjson::StringBuffer buffer;
2594 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2595 document.Accept(writer);
2596 std::string expected_json =
2597 "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40024,\"picture"
2598 "Bytes\":424}";
2599 std::string actual_json = buffer.GetString();
2600 ASSERT_EQ(actual_json, expected_json);
2601
2602 DestroyShell(std::move(shell));
2603}
2604
2605// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
2606// flakiness.
2607// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
2608// re-enabling.
2609TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) {
2610 auto settings = CreateSettingsForFixture();
2611
2612 DlISize wrong_size = DlISize(400, 100);
2613 DlISize expected_size = DlISize(400, 200);
2614
2615 fml::AutoResetWaitableEvent end_frame_latch;
2616 auto end_frame_callback =
2617 [&](bool should_merge_thread,
2618 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
2619 end_frame_latch.Signal();
2620 };
2621 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2622 std::move(end_frame_callback), PostPrerollResult::kSuccess, false);
2623 std::unique_ptr<Shell> shell = CreateShell({
2624 .settings = settings,
2625 .platform_view_create_callback = ShellTestPlatformViewBuilder({
2626 .shell_test_external_view_embedder = external_view_embedder,
2627 }),
2628 });
2629
2630 // Create the surface needed by rasterizer
2631 PlatformViewNotifyCreated(shell.get());
2632
2634 shell->GetTaskRunners().GetPlatformTaskRunner(),
2635 [&shell, &expected_size]() {
2636 shell->GetPlatformView()->SetViewportMetrics(
2637 kImplicitViewId,
2638 {1.0, static_cast<double>(expected_size.width),
2639 static_cast<double>(expected_size.height), 22, 0});
2640 });
2641
2642 auto configuration = RunConfiguration::InferFromSettings(settings);
2643 configuration.SetEntrypoint("emptyMain");
2644
2645 RunEngine(shell.get(), std::move(configuration));
2646
2647 PumpOneFrame(shell.get(),
2648 ViewContent::DummyView(static_cast<double>(wrong_size.width),
2649 static_cast<double>(wrong_size.height)));
2650 end_frame_latch.Wait();
2651 // Wrong size, no frames are submitted.
2652 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2653
2654 PumpOneFrame(shell.get(), ViewContent::DummyView(
2655 static_cast<double>(expected_size.width),
2656 static_cast<double>(expected_size.height)));
2657 end_frame_latch.Wait();
2658 // Expected size, 1 frame submitted.
2659 ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2660 ASSERT_EQ(expected_size, external_view_embedder->GetLastSubmittedFrameSize());
2661
2662 PlatformViewNotifyDestroyed(shell.get());
2663 DestroyShell(std::move(shell));
2664}
2665
2666// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
2667// flakiness.
2668// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
2669// re-enabling.
2670TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) {
2671 auto settings = CreateSettingsForFixture();
2672
2673 DlISize origin_size = DlISize(400, 100);
2674 DlISize new_size = DlISize(400, 200);
2675
2676 fml::AutoResetWaitableEvent end_frame_latch;
2677
2678 fml::AutoResetWaitableEvent resize_latch;
2679
2680 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
2681 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
2682 auto end_frame_callback =
2683 [&](bool should_merge_thread,
2684 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
2685 if (!raster_thread_merger_ref) {
2686 raster_thread_merger_ref = raster_thread_merger;
2687 }
2688 if (should_merge_thread) {
2689 raster_thread_merger->MergeWithLease(10);
2690 external_view_embedder->UpdatePostPrerollResult(
2691 PostPrerollResult::kSuccess);
2692 }
2693 end_frame_latch.Signal();
2694
2695 if (should_merge_thread) {
2696 resize_latch.Wait();
2697 }
2698 };
2699
2700 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2701 std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true);
2702
2703 std::unique_ptr<Shell> shell = CreateShell({
2704 .settings = settings,
2705 .platform_view_create_callback = ShellTestPlatformViewBuilder({
2706 .shell_test_external_view_embedder = external_view_embedder,
2707 }),
2708 });
2709
2710 // Create the surface needed by rasterizer
2711 PlatformViewNotifyCreated(shell.get());
2712
2714 shell->GetTaskRunners().GetPlatformTaskRunner(),
2715 [&shell, &origin_size]() {
2716 shell->GetPlatformView()->SetViewportMetrics(
2717 kImplicitViewId, {1.0, static_cast<double>(origin_size.width),
2718 static_cast<double>(origin_size.height), 22, 0});
2719 });
2720
2721 auto configuration = RunConfiguration::InferFromSettings(settings);
2722 configuration.SetEntrypoint("emptyMain");
2723
2724 RunEngine(shell.get(), std::move(configuration));
2725
2726 PumpOneFrame(shell.get(),
2727 ViewContent::DummyView(static_cast<double>(origin_size.width),
2728 static_cast<double>(origin_size.height)));
2729
2730 end_frame_latch.Wait();
2731 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2732
2734 shell->GetTaskRunners().GetPlatformTaskRunner(),
2735 [&shell, &new_size, &resize_latch]() {
2736 shell->GetPlatformView()->SetViewportMetrics(
2737 kImplicitViewId, {1.0, static_cast<double>(new_size.width),
2738 static_cast<double>(new_size.height), 22, 0});
2739 resize_latch.Signal();
2740 });
2741
2742 end_frame_latch.Wait();
2743
2744 // The frame resubmitted with origin size should be discarded after the
2745 // viewport metrics changed.
2746 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2747
2748 // Threads will be merged at the end of this frame.
2749 PumpOneFrame(shell.get(),
2750 ViewContent::DummyView(static_cast<double>(new_size.width),
2751 static_cast<double>(new_size.height)));
2752
2753 end_frame_latch.Wait();
2754 ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
2755 ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2756 ASSERT_EQ(new_size, external_view_embedder->GetLastSubmittedFrameSize());
2757
2758 PlatformViewNotifyDestroyed(shell.get());
2759 DestroyShell(std::move(shell));
2760}
2761
2762TEST_F(ShellTest, IgnoresInvalidMetrics) {
2764 double last_device_pixel_ratio;
2765 double last_width;
2766 double last_height;
2767 auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2768 auto dpr_handle = Dart_GetNativeArgument(args, 0);
2769 ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2770 Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2771 ASSERT_FALSE(last_device_pixel_ratio == 0.0);
2772
2773 auto width_handle = Dart_GetNativeArgument(args, 1);
2774 ASSERT_TRUE(Dart_IsDouble(width_handle));
2775 Dart_DoubleValue(width_handle, &last_width);
2776 ASSERT_FALSE(last_width == 0.0);
2777
2778 auto height_handle = Dart_GetNativeArgument(args, 2);
2779 ASSERT_TRUE(Dart_IsDouble(height_handle));
2780 Dart_DoubleValue(height_handle, &last_height);
2781 ASSERT_FALSE(last_height == 0.0);
2782
2783 latch.Signal();
2784 };
2785
2786 Settings settings = CreateSettingsForFixture();
2787 auto task_runner = CreateNewThread();
2788 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2789 task_runner);
2790
2791 AddNativeCallback("ReportMetrics",
2792 CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2793
2794 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2795
2796 auto configuration = RunConfiguration::InferFromSettings(settings);
2797 configuration.SetEntrypoint("reportMetrics");
2798
2799 RunEngine(shell.get(), std::move(configuration));
2800
2801 task_runner->PostTask([&]() {
2802 // This one is invalid for having 0 pixel ratio.
2803 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2804 {0.0, 400, 200, 22, 0});
2805 task_runner->PostTask([&]() {
2806 // This one is invalid for having 0 width.
2807 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2808 {0.8, 0.0, 200, 22, 0});
2809 task_runner->PostTask([&]() {
2810 // This one is invalid for having 0 height.
2811 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2812 {0.8, 400, 0.0, 22, 0});
2813 task_runner->PostTask([&]() {
2814 // This one makes it through.
2815 shell->GetPlatformView()->SetViewportMetrics(
2816 kImplicitViewId, {0.8, 400, 200.0, 22, 0});
2817 });
2818 });
2819 });
2820 });
2821 latch.Wait();
2822 ASSERT_EQ(last_device_pixel_ratio, 0.8);
2823 ASSERT_EQ(last_width, 400.0);
2824 ASSERT_EQ(last_height, 200.0);
2825 latch.Reset();
2826
2827 task_runner->PostTask([&]() {
2828 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2829 {1.2, 600, 300, 22, 0});
2830 });
2831 latch.Wait();
2832 ASSERT_EQ(last_device_pixel_ratio, 1.2);
2833 ASSERT_EQ(last_width, 600.0);
2834 ASSERT_EQ(last_height, 300.0);
2835
2836 DestroyShell(std::move(shell), task_runners);
2837}
2838
2839TEST_F(ShellTest, IgnoresMetricsUpdateToInvalidView) {
2841 double last_device_pixel_ratio;
2842 // This callback will be called whenever any view's metrics change.
2843 auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2844 // The correct call will have a DPR of 3.
2845 auto dpr_handle = Dart_GetNativeArgument(args, 0);
2846 ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2847 Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2848 ASSERT_TRUE(last_device_pixel_ratio > 2.5);
2849
2850 latch.Signal();
2851 };
2852
2853 Settings settings = CreateSettingsForFixture();
2854 auto task_runner = CreateNewThread();
2855 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2856 task_runner);
2857
2858 AddNativeCallback("ReportMetrics",
2859 CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2860
2861 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2862
2863 auto configuration = RunConfiguration::InferFromSettings(settings);
2864 configuration.SetEntrypoint("reportMetrics");
2865
2866 RunEngine(shell.get(), std::move(configuration));
2867
2868 task_runner->PostTask([&]() {
2869 // This one is invalid for having an nonexistent view ID.
2870 // Also, it has a DPR of 2.0 for detection.
2871 shell->GetPlatformView()->SetViewportMetrics(2, {2.0, 400, 200, 22, 0});
2872 task_runner->PostTask([&]() {
2873 // This one is valid with DPR 3.0.
2874 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2875 {3.0, 400, 200, 22, 0});
2876 });
2877 });
2878 latch.Wait();
2879 ASSERT_EQ(last_device_pixel_ratio, 3.0);
2880 latch.Reset();
2881
2882 DestroyShell(std::move(shell), task_runners);
2883}
2884
2885TEST_F(ShellTest, OnServiceProtocolSetAssetBundlePathWorks) {
2886 Settings settings = CreateSettingsForFixture();
2887 std::unique_ptr<Shell> shell = CreateShell(settings);
2888 RunConfiguration configuration =
2889 RunConfiguration::InferFromSettings(settings);
2890 configuration.SetEntrypoint("canAccessResourceFromAssetDir");
2891
2892 // Verify isolate can load a known resource with the
2893 // default asset directory - kernel_blob.bin
2895
2896 // Callback used to signal whether the resource was loaded successfully.
2897 bool can_access_resource = false;
2898 auto native_can_access_resource = [&can_access_resource,
2899 &latch](Dart_NativeArguments args) {
2900 Dart_Handle exception = nullptr;
2901 can_access_resource =
2903 latch.Signal();
2904 };
2905 AddNativeCallback("NotifyCanAccessResource",
2906 CREATE_NATIVE_ENTRY(native_can_access_resource));
2907
2908 // Callback used to delay the asset load until after the service
2909 // protocol method has finished.
2910 auto native_notify_set_asset_bundle_path =
2911 [&shell](Dart_NativeArguments args) {
2912 // Update the asset directory to a bonus path.
2914 params["assetDirectory"] = "assetDirectory";
2915 rapidjson::Document document;
2916 OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath,
2917 shell->GetTaskRunners().GetUITaskRunner(), params,
2918 &document);
2919 rapidjson::StringBuffer buffer;
2920 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2921 document.Accept(writer);
2922 };
2923 AddNativeCallback("NotifySetAssetBundlePath",
2924 CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path));
2925
2926 RunEngine(shell.get(), std::move(configuration));
2927
2928 latch.Wait();
2929 ASSERT_TRUE(can_access_resource);
2930
2931 DestroyShell(std::move(shell));
2932}
2933
2934TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) {
2935 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2936 // Make sure the shell launch does not kick off the creation of the VM
2937 // instance by already creating one upfront.
2938 auto vm_settings = CreateSettingsForFixture();
2939 auto vm_ref = DartVMRef::Create(vm_settings);
2940 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2941
2942 auto settings = vm_settings;
2943 fml::AutoResetWaitableEvent isolate_create_latch;
2944 settings.root_isolate_create_callback = [&](const auto& isolate) {
2945 isolate_create_latch.Signal();
2946 };
2947 auto shell = CreateShell(settings);
2948 ASSERT_TRUE(ValidateShell(shell.get()));
2949 auto configuration = RunConfiguration::InferFromSettings(settings);
2950 ASSERT_TRUE(configuration.IsValid());
2951 RunEngine(shell.get(), std::move(configuration));
2952 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2953 DestroyShell(std::move(shell));
2954 isolate_create_latch.Wait();
2955}
2956
2957TEST_F(ShellTest, AssetManagerSingle) {
2959 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2960 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2961
2962 std::string filename = "test_name";
2963 std::string content = "test_content";
2964
2965 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2967 ASSERT_TRUE(success);
2968
2969 AssetManager asset_manager;
2970 asset_manager.PushBack(
2971 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
2972
2973 auto mapping = asset_manager.GetAsMapping(filename);
2974 ASSERT_TRUE(mapping != nullptr);
2975
2976 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
2977 mapping->GetSize());
2978
2979 ASSERT_TRUE(result == content);
2980}
2981
2982TEST_F(ShellTest, AssetManagerMulti) {
2984 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2985 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2986
2987 std::vector<std::string> filenames = {
2988 "good0",
2989 "bad0",
2990 "good1",
2991 "bad1",
2992 };
2993
2994 for (const auto& filename : filenames) {
2995 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2996 fml::DataMapping(filename));
2997 ASSERT_TRUE(success);
2998 }
2999
3000 AssetManager asset_manager;
3001 asset_manager.PushBack(
3002 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
3003
3004 auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
3005 EXPECT_EQ(mappings.size(), 4u);
3006
3007 std::vector<std::string> expected_results = {
3008 "good0",
3009 "good1",
3010 };
3011
3012 mappings = asset_manager.GetAsMappings("(.*)good(.*)", std::nullopt);
3013 ASSERT_EQ(mappings.size(), expected_results.size());
3014
3015 for (auto& mapping : mappings) {
3016 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
3017 mapping->GetSize());
3018 EXPECT_NE(
3019 std::find(expected_results.begin(), expected_results.end(), result),
3020 expected_results.end());
3021 }
3022}
3023
3024#if defined(OS_FUCHSIA)
3025TEST_F(ShellTest, AssetManagerMultiSubdir) {
3026 std::string subdir_path = "subdir";
3027
3029 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
3030 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
3031 fml::UniqueFD subdir_fd =
3032 fml::OpenDirectory((asset_dir.path() + "/" + subdir_path).c_str(), true,
3034
3035 std::vector<std::string> filenames = {
3036 "bad0",
3037 "notgood", // this is to make sure the pattern (.*)good(.*) only
3038 // matches things in the subdirectory
3039 };
3040
3041 std::vector<std::string> subdir_filenames = {
3042 "good0",
3043 "good1",
3044 "bad1",
3045 };
3046
3047 for (auto filename : filenames) {
3048 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
3049 fml::DataMapping(filename));
3050 ASSERT_TRUE(success);
3051 }
3052
3053 for (auto filename : subdir_filenames) {
3054 bool success = fml::WriteAtomically(subdir_fd, filename.c_str(),
3055 fml::DataMapping(filename));
3056 ASSERT_TRUE(success);
3057 }
3058
3059 AssetManager asset_manager;
3060 asset_manager.PushBack(
3061 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
3062
3063 auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
3064 EXPECT_EQ(mappings.size(), 5u);
3065
3066 mappings = asset_manager.GetAsMappings("(.*)", subdir_path);
3067 EXPECT_EQ(mappings.size(), 3u);
3068
3069 std::vector<std::string> expected_results = {
3070 "good0",
3071 "good1",
3072 };
3073
3074 mappings = asset_manager.GetAsMappings("(.*)good(.*)", subdir_path);
3075 ASSERT_EQ(mappings.size(), expected_results.size());
3076
3077 for (auto& mapping : mappings) {
3078 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
3079 mapping->GetSize());
3080 ASSERT_NE(
3081 std::find(expected_results.begin(), expected_results.end(), result),
3082 expected_results.end());
3083 }
3084}
3085#endif // OS_FUCHSIA
3086
3088 auto settings = CreateSettingsForFixture();
3089 auto shell = CreateShell(settings);
3090 ASSERT_TRUE(ValidateShell(shell.get()));
3091
3092 auto configuration = RunConfiguration::InferFromSettings(settings);
3093 ASSERT_TRUE(configuration.IsValid());
3094 configuration.SetEntrypoint("fixturesAreFunctionalMain");
3095
3096 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3097 ASSERT_TRUE(second_configuration.IsValid());
3098 second_configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
3099
3100 const std::string initial_route("/foo");
3101
3102 fml::AutoResetWaitableEvent main_latch;
3103 std::string last_entry_point;
3104 // Fulfill native function for the first Shell's entrypoint.
3105 AddNativeCallback(
3106 "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
3107 last_entry_point = shell->GetEngine()->GetLastEntrypoint();
3108 main_latch.Signal();
3109 }));
3110 // Fulfill native function for the second Shell's entrypoint.
3111 fml::CountDownLatch second_latch(2);
3112 AddNativeCallback(
3113 // The Dart native function names aren't very consistent but this is
3114 // just the native function name of the second vm entrypoint in the
3115 // fixture.
3116 "NotifyNative",
3117 CREATE_NATIVE_ENTRY([&](auto args) { second_latch.CountDown(); }));
3118
3119 RunEngine(shell.get(), std::move(configuration));
3120 main_latch.Wait();
3121 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3122 // Check first Shell ran the first entrypoint.
3123 ASSERT_EQ("fixturesAreFunctionalMain", last_entry_point);
3124
3125 PostSync(
3126 shell->GetTaskRunners().GetPlatformTaskRunner(),
3127 [this, &spawner = shell, &second_configuration, &second_latch,
3128 initial_route]() {
3129 MockPlatformViewDelegate platform_view_delegate;
3130 auto spawn = spawner->Spawn(
3131 std::move(second_configuration), initial_route,
3132 [&platform_view_delegate](Shell& shell) {
3133 auto result = std::make_unique<MockPlatformView>(
3134 platform_view_delegate, shell.GetTaskRunners());
3135 ON_CALL(*result, CreateRenderingSurface())
3136 .WillByDefault(::testing::Invoke(
3137 [] { return std::make_unique<MockSurface>(); }));
3138 return result;
3139 },
3140 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3141 ASSERT_NE(nullptr, spawn.get());
3142 ASSERT_TRUE(ValidateShell(spawn.get()));
3143
3144 PostSync(spawner->GetTaskRunners().GetUITaskRunner(), [&spawn, &spawner,
3145 initial_route] {
3146 // Check second shell ran the second entrypoint.
3147 ASSERT_EQ("testCanLaunchSecondaryIsolate",
3148 spawn->GetEngine()->GetLastEntrypoint());
3149 ASSERT_EQ(initial_route, spawn->GetEngine()->InitialRoute());
3150
3151 ASSERT_NE(spawner->GetEngine()
3152 ->GetRuntimeController()
3153 ->GetRootIsolateGroup(),
3154 0u);
3155 ASSERT_EQ(spawner->GetEngine()
3156 ->GetRuntimeController()
3157 ->GetRootIsolateGroup(),
3158 spawn->GetEngine()
3159 ->GetRuntimeController()
3160 ->GetRootIsolateGroup());
3161 auto spawner_snapshot_delegate = spawner->GetEngine()
3162 ->GetRuntimeController()
3163 ->GetSnapshotDelegate();
3164 auto spawn_snapshot_delegate =
3165 spawn->GetEngine()->GetRuntimeController()->GetSnapshotDelegate();
3166 PostSync(spawner->GetTaskRunners().GetRasterTaskRunner(),
3167 [spawner_snapshot_delegate, spawn_snapshot_delegate] {
3168 ASSERT_NE(spawner_snapshot_delegate.get(),
3169 spawn_snapshot_delegate.get());
3170 });
3171 });
3172 PostSync(
3173 spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3174 ASSERT_EQ(spawner->GetIOManager()->GetResourceContext().get(),
3175 spawn->GetIOManager()->GetResourceContext().get());
3176 });
3177
3178 // Before destroying the shell, wait for expectations of the spawned
3179 // isolate to be met.
3180 second_latch.Wait();
3181
3182 DestroyShell(std::move(spawn));
3183 });
3184
3185 DestroyShell(std::move(shell));
3186 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3187}
3188
3189TEST_F(ShellTest, SpawnWithDartEntrypointArgs) {
3190 auto settings = CreateSettingsForFixture();
3191 auto shell = CreateShell(settings);
3192 ASSERT_TRUE(ValidateShell(shell.get()));
3193
3194 auto configuration = RunConfiguration::InferFromSettings(settings);
3195 ASSERT_TRUE(configuration.IsValid());
3196 configuration.SetEntrypoint("canReceiveArgumentsWhenEngineRun");
3197 const std::vector<std::string> entrypoint_args{"foo", "bar"};
3198 configuration.SetEntrypointArgs(entrypoint_args);
3199
3200 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3201 ASSERT_TRUE(second_configuration.IsValid());
3202 second_configuration.SetEntrypoint("canReceiveArgumentsWhenEngineSpawn");
3203 const std::vector<std::string> second_entrypoint_args{"arg1", "arg2"};
3204 second_configuration.SetEntrypointArgs(second_entrypoint_args);
3205
3206 const std::string initial_route("/foo");
3207
3208 fml::AutoResetWaitableEvent main_latch;
3209 std::string last_entry_point;
3210 // Fulfill native function for the first Shell's entrypoint.
3211 AddNativeCallback("NotifyNativeWhenEngineRun",
3212 CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) {
3214 Dart_GetNativeArgument(args, 0)));
3215 last_entry_point =
3216 shell->GetEngine()->GetLastEntrypoint();
3217 main_latch.Signal();
3218 })));
3219
3220 fml::AutoResetWaitableEvent second_latch;
3221 // Fulfill native function for the second Shell's entrypoint.
3222 AddNativeCallback("NotifyNativeWhenEngineSpawn",
3223 CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) {
3225 Dart_GetNativeArgument(args, 0)));
3226 last_entry_point =
3227 shell->GetEngine()->GetLastEntrypoint();
3228 second_latch.Signal();
3229 })));
3230
3231 RunEngine(shell.get(), std::move(configuration));
3232 main_latch.Wait();
3233 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3234 // Check first Shell ran the first entrypoint.
3235 ASSERT_EQ("canReceiveArgumentsWhenEngineRun", last_entry_point);
3236
3237 PostSync(
3238 shell->GetTaskRunners().GetPlatformTaskRunner(),
3239 [this, &spawner = shell, &second_configuration, &second_latch,
3240 initial_route]() {
3241 MockPlatformViewDelegate platform_view_delegate;
3242 auto spawn = spawner->Spawn(
3243 std::move(second_configuration), initial_route,
3244 [&platform_view_delegate](Shell& shell) {
3245 auto result = std::make_unique<MockPlatformView>(
3246 platform_view_delegate, shell.GetTaskRunners());
3247 ON_CALL(*result, CreateRenderingSurface())
3248 .WillByDefault(::testing::Invoke(
3249 [] { return std::make_unique<MockSurface>(); }));
3250 return result;
3251 },
3252 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3253 ASSERT_NE(nullptr, spawn.get());
3254 ASSERT_TRUE(ValidateShell(spawn.get()));
3255
3256 PostSync(spawner->GetTaskRunners().GetUITaskRunner(),
3257 [&spawn, &spawner, initial_route] {
3258 // Check second shell ran the second entrypoint.
3259 ASSERT_EQ("canReceiveArgumentsWhenEngineSpawn",
3260 spawn->GetEngine()->GetLastEntrypoint());
3261 ASSERT_EQ(initial_route, spawn->GetEngine()->InitialRoute());
3262
3263 ASSERT_NE(spawner->GetEngine()
3264 ->GetRuntimeController()
3265 ->GetRootIsolateGroup(),
3266 0u);
3267 ASSERT_EQ(spawner->GetEngine()
3268 ->GetRuntimeController()
3269 ->GetRootIsolateGroup(),
3270 spawn->GetEngine()
3271 ->GetRuntimeController()
3272 ->GetRootIsolateGroup());
3273 });
3274
3275 PostSync(
3276 spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3277 ASSERT_EQ(spawner->GetIOManager()->GetResourceContext().get(),
3278 spawn->GetIOManager()->GetResourceContext().get());
3279 });
3280
3281 // Before destroying the shell, wait for expectations of the spawned
3282 // isolate to be met.
3283 second_latch.Wait();
3284
3285 DestroyShell(std::move(spawn));
3286 });
3287
3288 DestroyShell(std::move(shell));
3289 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3290}
3291
3292TEST_F(ShellTest, IOManagerIsSharedBetweenParentAndSpawnedShell) {
3293 auto settings = CreateSettingsForFixture();
3294 auto shell = CreateShell(settings);
3295 ASSERT_TRUE(ValidateShell(shell.get()));
3296
3297 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [this,
3298 &spawner = shell,
3299 &settings] {
3300 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3301 ASSERT_TRUE(second_configuration.IsValid());
3302 second_configuration.SetEntrypoint("emptyMain");
3303 const std::string initial_route("/foo");
3304 MockPlatformViewDelegate platform_view_delegate;
3305 auto spawn = spawner->Spawn(
3306 std::move(second_configuration), initial_route,
3307 [&platform_view_delegate](Shell& shell) {
3308 auto result = std::make_unique<MockPlatformView>(
3309 platform_view_delegate, shell.GetTaskRunners());
3310 ON_CALL(*result, CreateRenderingSurface())
3311 .WillByDefault(::testing::Invoke(
3312 [] { return std::make_unique<MockSurface>(); }));
3313 return result;
3314 },
3315 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3316 ASSERT_TRUE(ValidateShell(spawn.get()));
3317
3318 PostSync(spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3319 ASSERT_NE(spawner->GetIOManager().get(), nullptr);
3320 ASSERT_EQ(spawner->GetIOManager().get(), spawn->GetIOManager().get());
3321 });
3322
3323 // Destroy the child shell.
3324 DestroyShell(std::move(spawn));
3325 });
3326 // Destroy the parent shell.
3327 DestroyShell(std::move(shell));
3328}
3329
3330TEST_F(ShellTest, IOManagerInSpawnedShellIsNotNullAfterParentShellDestroyed) {
3331 auto settings = CreateSettingsForFixture();
3332 auto shell = CreateShell(settings);
3333 ASSERT_TRUE(ValidateShell(shell.get()));
3334
3335 PostSync(shell->GetTaskRunners().GetUITaskRunner(), [&shell] {
3336 // We must get engine on UI thread.
3337 auto runtime_controller = shell->GetEngine()->GetRuntimeController();
3338 PostSync(shell->GetTaskRunners().GetIOTaskRunner(),
3339 [&shell, &runtime_controller] {
3340 // We must get io_manager on IO thread.
3341 auto io_manager = runtime_controller->GetIOManager();
3342 // Check io_manager existence.
3343 ASSERT_NE(io_manager.get(), nullptr);
3344 ASSERT_NE(io_manager->GetSkiaUnrefQueue().get(), nullptr);
3345 // Get io_manager directly from shell and check its existence.
3346 ASSERT_NE(shell->GetIOManager().get(), nullptr);
3347 });
3348 });
3349
3350 std::unique_ptr<Shell> spawn;
3351
3352 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &settings,
3353 &spawn] {
3354 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3355 ASSERT_TRUE(second_configuration.IsValid());
3356 second_configuration.SetEntrypoint("emptyMain");
3357 const std::string initial_route("/foo");
3358 MockPlatformViewDelegate platform_view_delegate;
3359 auto child = shell->Spawn(
3360 std::move(second_configuration), initial_route,
3361 [&platform_view_delegate](Shell& shell) {
3362 auto result = std::make_unique<MockPlatformView>(
3363 platform_view_delegate, shell.GetTaskRunners());
3364 ON_CALL(*result, CreateRenderingSurface())
3365 .WillByDefault(::testing::Invoke(
3366 [] { return std::make_unique<MockSurface>(); }));
3367 return result;
3368 },
3369 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3370 spawn = std::move(child);
3371 });
3372 // Destroy the parent shell.
3373 DestroyShell(std::move(shell));
3374
3375 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3376 // We must get engine on UI thread.
3377 auto runtime_controller = spawn->GetEngine()->GetRuntimeController();
3378 PostSync(spawn->GetTaskRunners().GetIOTaskRunner(),
3379 [&spawn, &runtime_controller] {
3380 // We must get io_manager on IO thread.
3381 auto io_manager = runtime_controller->GetIOManager();
3382 // Check io_manager existence here.
3383 ASSERT_NE(io_manager.get(), nullptr);
3384 ASSERT_NE(io_manager->GetSkiaUnrefQueue().get(), nullptr);
3385 // Get io_manager directly from shell and check its existence.
3386 ASSERT_NE(spawn->GetIOManager().get(), nullptr);
3387 });
3388 });
3389 // Destroy the child shell.
3390 DestroyShell(std::move(spawn));
3391}
3392
3393TEST_F(ShellTest, ImageGeneratorRegistryNotNullAfterParentShellDestroyed) {
3394 auto settings = CreateSettingsForFixture();
3395 auto shell = CreateShell(settings);
3396 ASSERT_TRUE(ValidateShell(shell.get()));
3397
3398 std::unique_ptr<Shell> spawn;
3399
3400 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &settings,
3401 &spawn] {
3402 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3403 ASSERT_TRUE(second_configuration.IsValid());
3404 second_configuration.SetEntrypoint("emptyMain");
3405 const std::string initial_route("/foo");
3406 MockPlatformViewDelegate platform_view_delegate;
3407 auto child = shell->Spawn(
3408 std::move(second_configuration), initial_route,
3409 [&platform_view_delegate](Shell& shell) {
3410 auto result = std::make_unique<MockPlatformView>(
3411 platform_view_delegate, shell.GetTaskRunners());
3412 ON_CALL(*result, CreateRenderingSurface())
3413 .WillByDefault(::testing::Invoke(
3414 [] { return std::make_unique<MockSurface>(); }));
3415 return result;
3416 },
3417 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3418 spawn = std::move(child);
3419 });
3420
3421 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3422 std::shared_ptr<const DartIsolate> isolate =
3423 spawn->GetEngine()->GetRuntimeController()->GetRootIsolate().lock();
3424 ASSERT_TRUE(isolate);
3425 ASSERT_TRUE(isolate->GetImageGeneratorRegistry());
3426 });
3427
3428 // Destroy the parent shell.
3429 DestroyShell(std::move(shell));
3430
3431 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3432 std::shared_ptr<const DartIsolate> isolate =
3433 spawn->GetEngine()->GetRuntimeController()->GetRootIsolate().lock();
3434 ASSERT_TRUE(isolate);
3435 ASSERT_TRUE(isolate->GetImageGeneratorRegistry());
3436 });
3437 // Destroy the child shell.
3438 DestroyShell(std::move(spawn));
3439}
3440
3441TEST_F(ShellTest, UpdateAssetResolverByTypeReplaces) {
3442 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3443 Settings settings = CreateSettingsForFixture();
3444
3446 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3447 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3448 task_runner);
3449 auto shell = CreateShell(settings, task_runners);
3450 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3451 ASSERT_TRUE(ValidateShell(shell.get()));
3452
3453 auto configuration = RunConfiguration::InferFromSettings(settings);
3454 configuration.SetEntrypoint("emptyMain");
3455 auto asset_manager = configuration.GetAssetManager();
3456
3457 shell->RunEngine(std::move(configuration), [&](auto result) {
3458 ASSERT_EQ(result, Engine::RunStatus::Success);
3459 });
3460
3461 auto platform_view =
3462 std::make_unique<PlatformView>(*shell.get(), task_runners);
3463
3464 auto old_resolver = std::make_unique<TestAssetResolver>(
3465 true, AssetResolver::AssetResolverType::kApkAssetProvider);
3466 ASSERT_TRUE(old_resolver->IsValid());
3467 asset_manager->PushBack(std::move(old_resolver));
3468
3469 auto updated_resolver = std::make_unique<TestAssetResolver>(
3470 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3471 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3472 platform_view->UpdateAssetResolverByType(
3473 std::move(updated_resolver),
3474 AssetResolver::AssetResolverType::kApkAssetProvider);
3475
3476 auto resolvers = asset_manager->TakeResolvers();
3477 ASSERT_EQ(resolvers.size(), 2ull);
3478 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3479
3480 ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
3481
3482 DestroyShell(std::move(shell), task_runners);
3483 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3484}
3485
3486TEST_F(ShellTest, UpdateAssetResolverByTypeAppends) {
3487 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3488 Settings settings = CreateSettingsForFixture();
3489
3491 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3492 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3493 task_runner);
3494 auto shell = CreateShell(settings, task_runners);
3495 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3496 ASSERT_TRUE(ValidateShell(shell.get()));
3497
3498 auto configuration = RunConfiguration::InferFromSettings(settings);
3499 configuration.SetEntrypoint("emptyMain");
3500 auto asset_manager = configuration.GetAssetManager();
3501
3502 shell->RunEngine(std::move(configuration), [&](auto result) {
3503 ASSERT_EQ(result, Engine::RunStatus::Success);
3504 });
3505
3506 auto platform_view =
3507 std::make_unique<PlatformView>(*shell.get(), task_runners);
3508
3509 auto updated_resolver = std::make_unique<TestAssetResolver>(
3510 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3511 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3512 platform_view->UpdateAssetResolverByType(
3513 std::move(updated_resolver),
3514 AssetResolver::AssetResolverType::kApkAssetProvider);
3515
3516 auto resolvers = asset_manager->TakeResolvers();
3517 ASSERT_EQ(resolvers.size(), 2ull);
3518 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3519
3520 ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
3521
3522 DestroyShell(std::move(shell), task_runners);
3523 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3524}
3525
3526TEST_F(ShellTest, UpdateAssetResolverByTypeNull) {
3527 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3528 Settings settings = CreateSettingsForFixture();
3530 "io.flutter.test." + GetCurrentTestName() + ".",
3531 ThreadHost::Type::kPlatform));
3532 auto task_runner = thread_host.platform_thread->GetTaskRunner();
3533 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3534 task_runner);
3535 auto shell = CreateShell(settings, task_runners);
3536 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3537 ASSERT_TRUE(ValidateShell(shell.get()));
3538
3539 auto configuration = RunConfiguration::InferFromSettings(settings);
3540 configuration.SetEntrypoint("emptyMain");
3541 auto asset_manager = configuration.GetAssetManager();
3542 RunEngine(shell.get(), std::move(configuration));
3543
3544 auto platform_view =
3545 std::make_unique<PlatformView>(*shell.get(), task_runners);
3546
3547 auto old_resolver = std::make_unique<TestAssetResolver>(
3548 true, AssetResolver::AssetResolverType::kApkAssetProvider);
3549 ASSERT_TRUE(old_resolver->IsValid());
3550 asset_manager->PushBack(std::move(old_resolver));
3551
3552 platform_view->UpdateAssetResolverByType(
3553 nullptr, AssetResolver::AssetResolverType::kApkAssetProvider);
3554
3555 auto resolvers = asset_manager->TakeResolvers();
3556 ASSERT_EQ(resolvers.size(), 2ull);
3557 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3558 ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
3559
3560 DestroyShell(std::move(shell), task_runners);
3561 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3562}
3563
3564TEST_F(ShellTest, UpdateAssetResolverByTypeDoesNotReplaceMismatchType) {
3565 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3566 Settings settings = CreateSettingsForFixture();
3567
3569 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3570 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3571 task_runner);
3572 auto shell = CreateShell(settings, task_runners);
3573 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3574 ASSERT_TRUE(ValidateShell(shell.get()));
3575
3576 auto configuration = RunConfiguration::InferFromSettings(settings);
3577 configuration.SetEntrypoint("emptyMain");
3578 auto asset_manager = configuration.GetAssetManager();
3579
3580 shell->RunEngine(std::move(configuration), [&](auto result) {
3581 ASSERT_EQ(result, Engine::RunStatus::Success);
3582 });
3583
3584 auto platform_view =
3585 std::make_unique<PlatformView>(*shell.get(), task_runners);
3586
3587 auto old_resolver = std::make_unique<TestAssetResolver>(
3588 true, AssetResolver::AssetResolverType::kAssetManager);
3589 ASSERT_TRUE(old_resolver->IsValid());
3590 asset_manager->PushBack(std::move(old_resolver));
3591
3592 auto updated_resolver = std::make_unique<TestAssetResolver>(
3593 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3594 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3595 platform_view->UpdateAssetResolverByType(
3596 std::move(updated_resolver),
3597 AssetResolver::AssetResolverType::kApkAssetProvider);
3598
3599 auto resolvers = asset_manager->TakeResolvers();
3600 ASSERT_EQ(resolvers.size(), 3ull);
3601 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3602
3603 ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
3604
3605 ASSERT_FALSE(resolvers[2]->IsValidAfterAssetManagerChange());
3606
3607 DestroyShell(std::move(shell), task_runners);
3608 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3609}
3610
3611TEST_F(ShellTest, CanCreateShellsWithGLBackend) {
3612#if !SHELL_ENABLE_GL
3613 // GL emulation does not exist on Fuchsia.
3614 GTEST_SKIP();
3615#else
3616 auto settings = CreateSettingsForFixture();
3617 std::unique_ptr<Shell> shell = CreateShell({
3618 .settings = settings,
3619 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3620 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
3621 }),
3622 });
3623 ASSERT_NE(shell, nullptr);
3624 ASSERT_TRUE(shell->IsSetup());
3625 auto configuration = RunConfiguration::InferFromSettings(settings);
3626 PlatformViewNotifyCreated(shell.get());
3627 configuration.SetEntrypoint("emptyMain");
3628 RunEngine(shell.get(), std::move(configuration));
3629 PumpOneFrame(shell.get());
3630 PlatformViewNotifyDestroyed(shell.get());
3631 DestroyShell(std::move(shell));
3632#endif // !SHELL_ENABLE_GL
3633}
3634
3635TEST_F(ShellTest, CanCreateShellsWithVulkanBackend) {
3636#if !SHELL_ENABLE_VULKAN
3637 GTEST_SKIP();
3638#else
3639 auto settings = CreateSettingsForFixture();
3640 std::unique_ptr<Shell> shell = CreateShell({
3641 .settings = settings,
3642 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3643 .rendering_backend =
3644 ShellTestPlatformView::BackendType::kVulkanBackend,
3645 }),
3646 });
3647 ASSERT_NE(shell, nullptr);
3648 ASSERT_TRUE(shell->IsSetup());
3649 auto configuration = RunConfiguration::InferFromSettings(settings);
3650 PlatformViewNotifyCreated(shell.get());
3651 configuration.SetEntrypoint("emptyMain");
3652 RunEngine(shell.get(), std::move(configuration));
3653 PumpOneFrame(shell.get());
3654 PlatformViewNotifyDestroyed(shell.get());
3655 DestroyShell(std::move(shell));
3656#endif // !SHELL_ENABLE_VULKAN
3657}
3658
3659TEST_F(ShellTest, CanCreateShellsWithMetalBackend) {
3660#if !SHELL_ENABLE_METAL
3661 GTEST_SKIP();
3662#else
3663 auto settings = CreateSettingsForFixture();
3664 std::unique_ptr<Shell> shell = CreateShell({
3665 .settings = settings,
3666 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3667 .rendering_backend =
3668 ShellTestPlatformView::BackendType::kMetalBackend,
3669 }),
3670 });
3671 ASSERT_NE(shell, nullptr);
3672 ASSERT_TRUE(shell->IsSetup());
3673 auto configuration = RunConfiguration::InferFromSettings(settings);
3674 PlatformViewNotifyCreated(shell.get());
3675 configuration.SetEntrypoint("emptyMain");
3676 RunEngine(shell.get(), std::move(configuration));
3677 PumpOneFrame(shell.get());
3678 PlatformViewNotifyDestroyed(shell.get());
3679 DestroyShell(std::move(shell));
3680#endif // !SHELL_ENABLE_METAL
3681}
3682
3683TEST_F(ShellTest, UserTagSetOnStartup) {
3684 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3685 // Make sure the shell launch does not kick off the creation of the VM
3686 // instance by already creating one upfront.
3687 auto vm_settings = CreateSettingsForFixture();
3688 auto vm_ref = DartVMRef::Create(vm_settings);
3689 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3690
3691 auto settings = vm_settings;
3692 fml::AutoResetWaitableEvent isolate_create_latch;
3693
3694 // ensure that "AppStartUpTag" is set during isolate creation.
3695 settings.root_isolate_create_callback = [&](const DartIsolate& isolate) {
3696 Dart_Handle current_tag = Dart_GetCurrentUserTag();
3697 Dart_Handle startup_tag = Dart_NewUserTag("AppStartUp");
3698 EXPECT_TRUE(Dart_IdentityEquals(current_tag, startup_tag));
3699
3700 isolate_create_latch.Signal();
3701 };
3702
3703 auto shell = CreateShell(settings);
3704 ASSERT_TRUE(ValidateShell(shell.get()));
3705
3706 auto configuration = RunConfiguration::InferFromSettings(settings);
3707 ASSERT_TRUE(configuration.IsValid());
3708
3709 RunEngine(shell.get(), std::move(configuration));
3710 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3711
3712 DestroyShell(std::move(shell));
3713 isolate_create_latch.Wait();
3714}
3715
3716TEST_F(ShellTest, PrefetchDefaultFontManager) {
3717 auto settings = CreateSettingsForFixture();
3718 settings.prefetched_default_font_manager = true;
3719 std::unique_ptr<Shell> shell;
3720
3721 auto get_font_manager_count = [&] {
3723 size_t font_manager_count;
3725 shell->GetTaskRunners().GetUITaskRunner(),
3726 [this, &shell, &latch, &font_manager_count]() {
3727 font_manager_count =
3728 GetFontCollection(shell.get())->GetFontManagersCount();
3729 latch.Signal();
3730 });
3731 latch.Wait();
3732 return font_manager_count;
3733 };
3734 size_t initial_font_manager_count = 0;
3735 settings.root_isolate_create_callback = [&](const auto& isolate) {
3736 ASSERT_GT(initial_font_manager_count, 0ul);
3737 // Should not have fetched the default font manager yet, since the root
3738 // isolate was only just created.
3739 ASSERT_EQ(get_font_manager_count(), initial_font_manager_count);
3740 };
3741
3742 shell = CreateShell(settings);
3743
3744 initial_font_manager_count = get_font_manager_count();
3745
3746 auto configuration = RunConfiguration::InferFromSettings(settings);
3747 configuration.SetEntrypoint("emptyMain");
3748 RunEngine(shell.get(), std::move(configuration));
3749
3750 // If the prefetched_default_font_manager flag is set, then the default font
3751 // manager will not be added until the engine starts running.
3752 ASSERT_EQ(get_font_manager_count(), initial_font_manager_count + 1);
3753
3754 DestroyShell(std::move(shell));
3755}
3756
3757TEST_F(ShellTest, OnPlatformViewCreatedWhenUIThreadIsBusy) {
3758 // This test will deadlock if the threading logic in
3759 // Shell::OnCreatePlatformView is wrong.
3760 auto settings = CreateSettingsForFixture();
3761 auto shell = CreateShell(settings);
3762
3764 fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetUITaskRunner(),
3765 [&latch]() { latch.Wait(); });
3766
3767 ShellTest::PlatformViewNotifyCreated(shell.get());
3768 latch.Signal();
3769
3770 DestroyShell(std::move(shell));
3771}
3772
3773TEST_F(ShellTest, UIWorkAfterOnPlatformViewDestroyed) {
3774 auto settings = CreateSettingsForFixture();
3775 auto shell = CreateShell(settings);
3776 auto configuration = RunConfiguration::InferFromSettings(settings);
3777 configuration.SetEntrypoint("drawFrames");
3778
3780 fml::AutoResetWaitableEvent notify_native_latch;
3781 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
3782 notify_native_latch.Signal();
3783 latch.Wait();
3784 }));
3785
3786 RunEngine(shell.get(), std::move(configuration));
3787 // Wait to make sure we get called back from Dart and thus have latched
3788 // the UI thread before we create/destroy the platform view.
3789 notify_native_latch.Wait();
3790
3791 ShellTest::PlatformViewNotifyCreated(shell.get());
3792
3793 fml::AutoResetWaitableEvent destroy_latch;
3795 shell->GetTaskRunners().GetPlatformTaskRunner(),
3796 [&shell, &destroy_latch]() {
3797 shell->GetPlatformView()->NotifyDestroyed();
3798 destroy_latch.Signal();
3799 });
3800
3801 destroy_latch.Wait();
3802
3803 // Unlatch the UI thread and let it send us a scene to render.
3804 latch.Signal();
3805
3806 // Flush the UI task runner to make sure we process the render/scheduleFrame
3807 // request.
3808 fml::AutoResetWaitableEvent ui_flush_latch;
3810 shell->GetTaskRunners().GetUITaskRunner(),
3811 [&ui_flush_latch]() { ui_flush_latch.Signal(); });
3812 ui_flush_latch.Wait();
3813 DestroyShell(std::move(shell));
3814}
3815
3816TEST_F(ShellTest, UsesPlatformMessageHandler) {
3817 TaskRunners task_runners = GetTaskRunnersForFixture();
3818 auto settings = CreateSettingsForFixture();
3819 MockPlatformViewDelegate platform_view_delegate;
3820 auto platform_message_handler =
3821 std::make_shared<MockPlatformMessageHandler>();
3822 int message_id = 1;
3823 EXPECT_CALL(*platform_message_handler, HandlePlatformMessage(_));
3824 EXPECT_CALL(*platform_message_handler,
3825 InvokePlatformMessageEmptyResponseCallback(message_id));
3826 Shell::CreateCallback<PlatformView> platform_view_create_callback =
3827 [&platform_view_delegate, task_runners,
3828 platform_message_handler](flutter::Shell& shell) {
3829 auto result = std::make_unique<MockPlatformView>(platform_view_delegate,
3830 task_runners);
3831 EXPECT_CALL(*result, GetPlatformMessageHandler())
3832 .WillOnce(Return(platform_message_handler));
3833 return result;
3834 };
3835 auto shell = CreateShell({
3836 .settings = settings,
3837 .task_runners = task_runners,
3838 .platform_view_create_callback = platform_view_create_callback,
3839 });
3840
3841 EXPECT_EQ(platform_message_handler, shell->GetPlatformMessageHandler());
3842 PostSync(task_runners.GetUITaskRunner(), [&shell]() {
3843 size_t data_size = 4;
3844 fml::MallocMapping bytes =
3845 fml::MallocMapping(static_cast<uint8_t*>(malloc(data_size)), data_size);
3846 fml::RefPtr<MockPlatformMessageResponse> response =
3847 MockPlatformMessageResponse::Create();
3848 auto message = std::make_unique<PlatformMessage>(
3849 /*channel=*/"foo", /*data=*/std::move(bytes), /*response=*/response);
3850 (static_cast<Engine::Delegate*>(shell.get()))
3851 ->OnEngineHandlePlatformMessage(std::move(message));
3852 });
3853 shell->GetPlatformMessageHandler()
3854 ->InvokePlatformMessageEmptyResponseCallback(message_id);
3855 DestroyShell(std::move(shell));
3856}
3857
3858TEST_F(ShellTest, SpawnWorksWithOnError) {
3859 auto settings = CreateSettingsForFixture();
3860 auto shell = CreateShell(settings);
3861 ASSERT_TRUE(ValidateShell(shell.get()));
3862
3863 auto configuration = RunConfiguration::InferFromSettings(settings);
3864 ASSERT_TRUE(configuration.IsValid());
3865 configuration.SetEntrypoint("onErrorA");
3866
3867 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3868 ASSERT_TRUE(second_configuration.IsValid());
3869 second_configuration.SetEntrypoint("onErrorB");
3870
3871 fml::CountDownLatch latch(2);
3872
3873 AddNativeCallback(
3874 "NotifyErrorA", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3875 auto string_handle = Dart_GetNativeArgument(args, 0);
3876 const char* c_str;
3877 Dart_StringToCString(string_handle, &c_str);
3878 EXPECT_STREQ(c_str, "Exception: I should be coming from A");
3879 latch.CountDown();
3880 }));
3881
3882 AddNativeCallback(
3883 "NotifyErrorB", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3884 auto string_handle = Dart_GetNativeArgument(args, 0);
3885 const char* c_str;
3886 Dart_StringToCString(string_handle, &c_str);
3887 EXPECT_STREQ(c_str, "Exception: I should be coming from B");
3888 latch.CountDown();
3889 }));
3890
3891 RunEngine(shell.get(), std::move(configuration));
3892
3893 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3894
3895 PostSync(
3896 shell->GetTaskRunners().GetPlatformTaskRunner(),
3897 [this, &spawner = shell, &second_configuration, &latch]() {
3898 ::testing::NiceMock<MockPlatformViewDelegate> platform_view_delegate;
3899 auto spawn = spawner->Spawn(
3900 std::move(second_configuration), "",
3901 [&platform_view_delegate](Shell& shell) {
3902 auto result =
3903 std::make_unique<::testing::NiceMock<MockPlatformView>>(
3904 platform_view_delegate, shell.GetTaskRunners());
3905 ON_CALL(*result, CreateRenderingSurface())
3906 .WillByDefault(::testing::Invoke([] {
3907 return std::make_unique<::testing::NiceMock<MockSurface>>();
3908 }));
3909 return result;
3910 },
3911 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3912 ASSERT_NE(nullptr, spawn.get());
3913 ASSERT_TRUE(ValidateShell(spawn.get()));
3914
3915 // Before destroying the shell, wait for expectations of the spawned
3916 // isolate to be met.
3917 latch.Wait();
3918
3919 DestroyShell(std::move(spawn));
3920 });
3921
3922 DestroyShell(std::move(shell));
3923 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3924}
3925
3926TEST_F(ShellTest, ImmutableBufferLoadsAssetOnBackgroundThread) {
3927 Settings settings = CreateSettingsForFixture();
3928 auto task_runner = CreateNewThread();
3929 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3930 task_runner);
3931 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
3932
3933 fml::CountDownLatch latch(1);
3934 AddNativeCallback("NotifyNative",
3935 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
3936
3937 // Create the surface needed by rasterizer
3938 PlatformViewNotifyCreated(shell.get());
3939
3940 auto configuration = RunConfiguration::InferFromSettings(settings);
3941 configuration.SetEntrypoint("testThatAssetLoadingHappensOnWorkerThread");
3942 auto asset_manager = configuration.GetAssetManager();
3943 auto test_resolver = std::make_unique<ThreadCheckingAssetResolver>(
3944 shell->GetDartVM()->GetConcurrentMessageLoop());
3945 auto leaked_resolver = test_resolver.get();
3946 asset_manager->PushBack(std::move(test_resolver));
3947
3948 RunEngine(shell.get(), std::move(configuration));
3949 PumpOneFrame(shell.get());
3950
3951 latch.Wait();
3952
3953 EXPECT_EQ(leaked_resolver->mapping_requests[0], "DoesNotExist");
3954
3955 PlatformViewNotifyDestroyed(shell.get());
3956 DestroyShell(std::move(shell), task_runners);
3957}
3958
3959TEST_F(ShellTest, PictureToImageSync) {
3960#if !SHELL_ENABLE_GL
3961 // This test uses the GL backend.
3962 GTEST_SKIP();
3963#else
3964 auto settings = CreateSettingsForFixture();
3965 std::unique_ptr<Shell> shell = CreateShell({
3966 .settings = settings,
3967 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3968 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
3969 }),
3970 });
3971
3972 AddNativeCallback("NativeOnBeforeToImageSync",
3973 CREATE_NATIVE_ENTRY([&](auto args) {
3974 // nop
3975 }));
3976
3977 fml::CountDownLatch latch(2);
3978 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
3979 // Teardown and set up rasterizer again.
3980 PlatformViewNotifyDestroyed(shell.get());
3981 PlatformViewNotifyCreated(shell.get());
3982 latch.CountDown();
3983 }));
3984
3985 ASSERT_NE(shell, nullptr);
3986 ASSERT_TRUE(shell->IsSetup());
3987 auto configuration = RunConfiguration::InferFromSettings(settings);
3988 PlatformViewNotifyCreated(shell.get());
3989 configuration.SetEntrypoint("toImageSync");
3990 RunEngine(shell.get(), std::move(configuration));
3991 PumpOneFrame(shell.get());
3992
3993 latch.Wait();
3994
3995 PlatformViewNotifyDestroyed(shell.get());
3996 DestroyShell(std::move(shell));
3997#endif // !SHELL_ENABLE_GL
3998}
3999
4000TEST_F(ShellTest, PictureToImageSyncImpellerNoSurface) {
4001#if !SHELL_ENABLE_METAL
4002 // This test uses the Metal backend.
4003 GTEST_SKIP();
4004#else
4005 auto settings = CreateSettingsForFixture();
4006 settings.enable_impeller = true;
4007 std::unique_ptr<Shell> shell = CreateShell({
4008 .settings = settings,
4009 .platform_view_create_callback = ShellTestPlatformViewBuilder({
4010 .rendering_backend =
4011 ShellTestPlatformView::BackendType::kMetalBackend,
4012 }),
4013 });
4014
4015 AddNativeCallback("NativeOnBeforeToImageSync",
4016 CREATE_NATIVE_ENTRY([&](auto args) {
4017 // nop
4018 }));
4019
4020 fml::CountDownLatch latch(2);
4021 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4022 // Teardown and set up rasterizer again.
4023 PlatformViewNotifyDestroyed(shell.get());
4024 PlatformViewNotifyCreated(shell.get());
4025 latch.CountDown();
4026 }));
4027
4028 ASSERT_NE(shell, nullptr);
4029 ASSERT_TRUE(shell->IsSetup());
4030 auto configuration = RunConfiguration::InferFromSettings(settings);
4031
4032 // Important: Do not create the platform view yet!
4033 // This test is making sure that the rasterizer can create the texture
4034 // as expected without a surface.
4035
4036 configuration.SetEntrypoint("toImageSync");
4037 RunEngine(shell.get(), std::move(configuration));
4038 PumpOneFrame(shell.get());
4039
4040 latch.Wait();
4041
4042 PlatformViewNotifyDestroyed(shell.get());
4043 DestroyShell(std::move(shell));
4044#endif // !SHELL_ENABLE_METAL
4045}
4046
4047#if SHELL_ENABLE_GL
4048// This test uses the GL backend and refers to symbols in egl.h
4049TEST_F(ShellTest, PictureToImageSyncWithTrampledContext) {
4050 // make it easier to trample the GL context by running on a single task
4051 // runner.
4052 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4053 ThreadHost::Type::kPlatform);
4054 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4055 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4056 task_runner);
4057
4058 auto settings = CreateSettingsForFixture();
4059 std::unique_ptr<Shell> shell = CreateShell({
4060 .settings = settings,
4061 .task_runners = task_runners,
4062 .platform_view_create_callback = ShellTestPlatformViewBuilder({
4063 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
4064 }),
4065 });
4066
4067 AddNativeCallback(
4068 "NativeOnBeforeToImageSync", CREATE_NATIVE_ENTRY([&](auto args) {
4069 // Trample the GL context. If the rasterizer fails
4070 // to make the right one current again, test will
4071 // fail.
4072 ::eglMakeCurrent(::eglGetCurrentDisplay(), NULL, NULL, NULL);
4073 }));
4074
4075 fml::CountDownLatch latch(2);
4076 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4077 // Teardown and set up rasterizer again.
4078 PlatformViewNotifyDestroyed(shell.get());
4079 PlatformViewNotifyCreated(shell.get());
4080 latch.CountDown();
4081 }));
4082
4083 ASSERT_NE(shell, nullptr);
4084 ASSERT_TRUE(shell->IsSetup());
4085 auto configuration = RunConfiguration::InferFromSettings(settings);
4086 PlatformViewNotifyCreated(shell.get());
4087 configuration.SetEntrypoint("toImageSync");
4088 RunEngine(shell.get(), std::move(configuration));
4089 PumpOneFrame(shell.get());
4090
4091 latch.Wait();
4092
4093 PlatformViewNotifyDestroyed(shell.get());
4094 DestroyShell(std::move(shell), task_runners);
4095}
4096#endif // SHELL_ENABLE_GL
4097
4098TEST_F(ShellTest, PluginUtilitiesCallbackHandleErrorHandling) {
4099 auto settings = CreateSettingsForFixture();
4100 std::unique_ptr<Shell> shell = CreateShell(settings);
4101
4103 bool test_passed;
4104 AddNativeCallback("NotifyNativeBool", CREATE_NATIVE_ENTRY([&](auto args) {
4105 Dart_Handle exception = nullptr;
4107 args, 0, exception);
4108 latch.Signal();
4109 }));
4110
4111 ASSERT_NE(shell, nullptr);
4112 ASSERT_TRUE(shell->IsSetup());
4113 auto configuration = RunConfiguration::InferFromSettings(settings);
4114 PlatformViewNotifyCreated(shell.get());
4115 configuration.SetEntrypoint("testPluginUtilitiesCallbackHandle");
4116 RunEngine(shell.get(), std::move(configuration));
4117 PumpOneFrame(shell.get());
4118
4119 latch.Wait();
4120
4121 ASSERT_TRUE(test_passed);
4122
4123 PlatformViewNotifyDestroyed(shell.get());
4124 DestroyShell(std::move(shell));
4125}
4126
4127TEST_F(ShellTest, NotifyIdleRejectsPastAndNearFuture) {
4128 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4129 Settings settings = CreateSettingsForFixture();
4130 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4131 ThreadHost::Type::kPlatform | ThreadHost::kUi |
4132 ThreadHost::kIo | ThreadHost::kRaster);
4133 auto platform_task_runner = thread_host.platform_thread->GetTaskRunner();
4134 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4135 thread_host.raster_thread->GetTaskRunner(),
4136 thread_host.ui_thread->GetTaskRunner(),
4137 thread_host.io_thread->GetTaskRunner());
4138 auto shell = CreateShell(settings, task_runners);
4139 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
4140 ASSERT_TRUE(ValidateShell(shell.get()));
4141
4143
4144 auto configuration = RunConfiguration::InferFromSettings(settings);
4145 configuration.SetEntrypoint("emptyMain");
4146 RunEngine(shell.get(), std::move(configuration));
4147
4149 task_runners.GetUITaskRunner(), [&latch, &shell]() {
4150 auto runtime_controller = const_cast<RuntimeController*>(
4151 shell->GetEngine()->GetRuntimeController());
4152
4153 auto now = fml::TimeDelta::FromMicroseconds(Dart_TimelineGetMicros());
4154
4155 EXPECT_FALSE(runtime_controller->NotifyIdle(
4156 now - fml::TimeDelta::FromMilliseconds(10)));
4157 EXPECT_FALSE(runtime_controller->NotifyIdle(now));
4158 EXPECT_FALSE(runtime_controller->NotifyIdle(
4159 now + fml::TimeDelta::FromNanoseconds(100)));
4160
4161 EXPECT_TRUE(runtime_controller->NotifyIdle(
4162 now + fml::TimeDelta::FromMilliseconds(100)));
4163 latch.Signal();
4164 });
4165
4166 latch.Wait();
4167
4168 DestroyShell(std::move(shell), task_runners);
4169 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4170}
4171
4172TEST_F(ShellTest, NotifyIdleNotCalledInLatencyMode) {
4173 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4174 Settings settings = CreateSettingsForFixture();
4175 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4176 ThreadHost::Type::kPlatform | ThreadHost::kUi |
4177 ThreadHost::kIo | ThreadHost::kRaster);
4178 auto platform_task_runner = thread_host.platform_thread->GetTaskRunner();
4179 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4180 thread_host.raster_thread->GetTaskRunner(),
4181 thread_host.ui_thread->GetTaskRunner(),
4182 thread_host.io_thread->GetTaskRunner());
4183 auto shell = CreateShell(settings, task_runners);
4184 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
4185 ASSERT_TRUE(ValidateShell(shell.get()));
4186
4187 // we start off in balanced mode, where we expect idle notifications to
4188 // succeed. After the first `NotifyNativeBool` we expect to be in latency
4189 // mode, where we expect idle notifications to fail.
4190 fml::CountDownLatch latch(2);
4191 AddNativeCallback(
4192 "NotifyNativeBool", CREATE_NATIVE_ENTRY([&](auto args) {
4193 Dart_Handle exception = nullptr;
4194 bool is_in_latency_mode =
4196 auto runtime_controller = const_cast<RuntimeController*>(
4197 shell->GetEngine()->GetRuntimeController());
4198 bool success =
4199 runtime_controller->NotifyIdle(fml::TimeDelta::FromMicroseconds(
4200 Dart_TimelineGetMicros() + 100000));
4201 EXPECT_EQ(success, !is_in_latency_mode);
4202 latch.CountDown();
4203 }));
4204
4205 auto configuration = RunConfiguration::InferFromSettings(settings);
4206 configuration.SetEntrypoint("performanceModeImpactsNotifyIdle");
4207 RunEngine(shell.get(), std::move(configuration));
4208
4209 latch.Wait();
4210
4211 DestroyShell(std::move(shell), task_runners);
4212 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4213}
4214
4215TEST_F(ShellTest, PrintsErrorWhenPlatformMessageSentFromWrongThread) {
4216#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DEBUG || OS_FUCHSIA
4217 GTEST_SKIP() << "Test is for debug mode only on non-fuchsia targets.";
4218#else
4219 Settings settings = CreateSettingsForFixture();
4220 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4221 ThreadHost::Type::kPlatform);
4222 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4223 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4224 task_runner);
4225 auto shell = CreateShell(settings, task_runners);
4226
4227 {
4228 fml::testing::LogCapture log_capture;
4229
4230 // The next call will result in a thread checker violation.
4232 SendPlatformMessage(shell.get(), std::make_unique<PlatformMessage>(
4233 "com.test.plugin", nullptr));
4234
4235 EXPECT_THAT(
4236 log_capture.str(),
4237 ::testing::EndsWith(
4238 "The 'com.test.plugin' channel sent a message from native to "
4239 "Flutter on a non-platform thread. Platform channel messages "
4240 "must be sent on the platform thread. Failure to do so may "
4241 "result in data loss or crashes, and must be fixed in the "
4242 "plugin or application code creating that channel.\nSee "
4243 "https://docs.flutter.dev/platform-integration/"
4244 "platform-channels#channels-and-platform-threading for more "
4245 "information.\n"));
4246 }
4247
4248 {
4249 fml::testing::LogCapture log_capture;
4250
4251 // The next call will result in a thread checker violation.
4253 SendPlatformMessage(shell.get(), std::make_unique<PlatformMessage>(
4254 "com.test.plugin", nullptr));
4255
4256 EXPECT_EQ(log_capture.str(), "");
4257 }
4258
4259 DestroyShell(std::move(shell), task_runners);
4260 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4261#endif
4262}
4263
4264TEST_F(ShellTest, NavigationMessageDispachedImmediately) {
4265 Settings settings = CreateSettingsForFixture();
4266 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4267 ThreadHost::Type::kPlatform);
4268 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4269 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4270 task_runner);
4271 auto shell = CreateShell(settings, task_runners);
4272
4273 auto latch = std::make_shared<fml::CountDownLatch>(1u);
4274 task_runner->PostTask([&]() {
4275 auto message = MakePlatformMessage(
4276 "flutter/navigation",
4277 {{"method", "setInitialRoute"}, {"args", "/testo"}}, nullptr);
4278 SendPlatformMessage(shell.get(), std::move(message));
4279 EXPECT_EQ(shell->GetEngine()->InitialRoute(), "/testo");
4280
4281 latch->CountDown();
4282 });
4283 latch->Wait();
4284
4285 DestroyShell(std::move(shell), task_runners);
4286 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4287}
4288
4289// Verifies a semantics Action will flush the dart event loop.
4290TEST_F(ShellTest, SemanticsActionsFlushMessageLoop) {
4291 Settings settings = CreateSettingsForFixture();
4292 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4293 ThreadHost::Type::kPlatform);
4294 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4295 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4296 task_runner);
4297
4298 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4299 task_runners.GetUITaskRunner());
4300 auto shell = CreateShell(settings, task_runners);
4301 auto configuration = RunConfiguration::InferFromSettings(settings);
4302 configuration.SetEntrypoint("testSemanticsActions");
4303
4304 RunEngine(shell.get(), std::move(configuration));
4305 fml::CountDownLatch latch(1);
4306 AddNativeCallback(
4307 // The Dart native function names aren't very consistent but this is
4308 // just the native function name of the second vm entrypoint in the
4309 // fixture.
4310 "NotifyNative",
4311 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
4312
4313 task_runners.GetPlatformTaskRunner()->PostTask([&] {
4314 SendSemanticsAction(shell.get(), 456, 0, SemanticsAction::kTap,
4315 fml::MallocMapping(nullptr, 0));
4316 });
4317 latch.Wait();
4318
4319 DestroyShell(std::move(shell), task_runners);
4320 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4321}
4322
4323// Verifies a pointer event will flush the dart event loop.
4324TEST_F(ShellTest, PointerPacketFlushMessageLoop) {
4325 Settings settings = CreateSettingsForFixture();
4326 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4327 ThreadHost::Type::kPlatform);
4328 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4329 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4330 task_runner);
4331
4332 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4333 task_runners.GetUITaskRunner());
4334 auto shell = CreateShell(settings, task_runners);
4335 auto configuration = RunConfiguration::InferFromSettings(settings);
4336 configuration.SetEntrypoint("testPointerActions");
4337
4338 RunEngine(shell.get(), std::move(configuration));
4339 fml::CountDownLatch latch(1);
4340 AddNativeCallback(
4341 // The Dart native function names aren't very consistent but this is
4342 // just the native function name of the second vm entrypoint in the
4343 // fixture.
4344 "NotifyNative",
4345 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
4346
4347 DispatchFakePointerData(shell.get(), 23);
4348 latch.Wait();
4349
4350 DestroyShell(std::move(shell), task_runners);
4351 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4352}
4353
4354// Verifies a pointer event will flush the dart event loop.
4355TEST_F(ShellTest, DISABLED_PointerPacketsAreDispatchedWithTask) {
4356 Settings settings = CreateSettingsForFixture();
4357 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4358 ThreadHost::Type::kPlatform);
4359 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4360 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4361 task_runner);
4362
4363 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4364 task_runners.GetUITaskRunner());
4365 auto shell = CreateShell(settings, task_runners);
4366 auto configuration = RunConfiguration::InferFromSettings(settings);
4367 configuration.SetEntrypoint("testDispatchEvents");
4368
4369 RunEngine(shell.get(), std::move(configuration));
4370 fml::CountDownLatch latch(1);
4371 bool did_invoke_callback = false;
4372 AddNativeCallback(
4373 // The Dart native function names aren't very consistent but this is
4374 // just the native function name of the second vm entrypoint in the
4375 // fixture.
4376 "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4377 did_invoke_callback = true;
4378 latch.CountDown();
4379 }));
4380
4381 DispatchFakePointerData(shell.get(), 23);
4382 EXPECT_FALSE(did_invoke_callback);
4383 latch.Wait();
4384 EXPECT_TRUE(did_invoke_callback);
4385
4386 DestroyShell(std::move(shell), task_runners);
4387 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4388}
4389
4390TEST_F(ShellTest, DiesIfSoftwareRenderingAndImpellerAreEnabledDeathTest) {
4391#if defined(OS_FUCHSIA)
4392 GTEST_SKIP() << "Fuchsia";
4393#else
4394 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
4395 Settings settings = CreateSettingsForFixture();
4396 settings.enable_impeller = true;
4397 settings.enable_software_rendering = true;
4398 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4399 ThreadHost::Type::kPlatform);
4400 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4401 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4402 task_runner);
4403 EXPECT_DEATH_IF_SUPPORTED(
4404 CreateShell(settings, task_runners),
4405 "Software rendering is incompatible with Impeller.");
4406#endif // OS_FUCHSIA
4407}
4408
4409// Parse the arguments of NativeReportViewIdsCallback and
4410// store them in hasImplicitView and viewIds.
4411static void ParseViewIdsCallback(const Dart_NativeArguments& args,
4412 bool* hasImplicitView,
4413 std::vector<int64_t>* viewIds) {
4414 Dart_Handle exception = nullptr;
4415 viewIds->clear();
4416 *hasImplicitView =
4418 ASSERT_EQ(exception, nullptr);
4419 *viewIds = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
4420 args, 1, exception);
4421 ASSERT_EQ(exception, nullptr);
4422}
4423
4424TEST_F(ShellTest, ShellStartsWithImplicitView) {
4425 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4426 Settings settings = CreateSettingsForFixture();
4427 auto task_runner = CreateNewThread();
4428 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4429 task_runner);
4430 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4431 ASSERT_TRUE(shell);
4432
4433 bool hasImplicitView;
4434 std::vector<int64_t> viewIds;
4435 fml::AutoResetWaitableEvent reportLatch;
4436 auto nativeViewIdsCallback = [&reportLatch, &hasImplicitView,
4437 &viewIds](Dart_NativeArguments args) {
4438 ParseViewIdsCallback(args, &hasImplicitView, &viewIds);
4439 reportLatch.Signal();
4440 };
4441 AddNativeCallback("NativeReportViewIdsCallback",
4442 CREATE_NATIVE_ENTRY(nativeViewIdsCallback));
4443
4444 PlatformViewNotifyCreated(shell.get());
4445 auto configuration = RunConfiguration::InferFromSettings(settings);
4446 configuration.SetEntrypoint("testReportViewIds");
4447 RunEngine(shell.get(), std::move(configuration));
4448 reportLatch.Wait();
4449
4450 ASSERT_TRUE(hasImplicitView);
4451 ASSERT_EQ(viewIds.size(), 1u);
4452 ASSERT_EQ(viewIds[0], 0ll);
4453
4454 PlatformViewNotifyDestroyed(shell.get());
4455 DestroyShell(std::move(shell), task_runners);
4456}
4457
4458// Tests that Shell::AddView and Shell::RemoveView works.
4459TEST_F(ShellTest, ShellCanAddViewOrRemoveView) {
4460 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4461 Settings settings = CreateSettingsForFixture();
4463 "io.flutter.test." + GetCurrentTestName() + ".",
4464 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4465 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4466 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4467 thread_host.raster_thread->GetTaskRunner(),
4468 thread_host.ui_thread->GetTaskRunner(),
4469 thread_host.io_thread->GetTaskRunner());
4470 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4471 ASSERT_TRUE(shell);
4472
4473 bool hasImplicitView;
4474 std::vector<int64_t> viewIds;
4475 fml::AutoResetWaitableEvent reportLatch;
4476 auto nativeViewIdsCallback = [&reportLatch, &hasImplicitView,
4477 &viewIds](Dart_NativeArguments args) {
4478 ParseViewIdsCallback(args, &hasImplicitView, &viewIds);
4479 reportLatch.Signal();
4480 };
4481 AddNativeCallback("NativeReportViewIdsCallback",
4482 CREATE_NATIVE_ENTRY(nativeViewIdsCallback));
4483
4484 PlatformViewNotifyCreated(shell.get());
4485 auto configuration = RunConfiguration::InferFromSettings(settings);
4486 configuration.SetEntrypoint("testReportViewIds");
4487 RunEngine(shell.get(), std::move(configuration));
4488
4489 reportLatch.Wait();
4490 ASSERT_TRUE(hasImplicitView);
4491 ASSERT_EQ(viewIds.size(), 1u);
4492 ASSERT_EQ(viewIds[0], 0ll);
4493
4494 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4495 shell->GetPlatformView()->AddView(2, ViewportMetrics{},
4496 [](bool added) { EXPECT_TRUE(added); });
4497 });
4498 reportLatch.Wait();
4499 ASSERT_TRUE(hasImplicitView);
4500 ASSERT_EQ(viewIds.size(), 2u);
4501 ASSERT_EQ(viewIds[1], 2ll);
4502
4503 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4504 shell->GetPlatformView()->RemoveView(
4505 2, [](bool removed) { ASSERT_TRUE(removed); });
4506 });
4507 reportLatch.Wait();
4508 ASSERT_TRUE(hasImplicitView);
4509 ASSERT_EQ(viewIds.size(), 1u);
4510 ASSERT_EQ(viewIds[0], 0ll);
4511
4512 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4513 shell->GetPlatformView()->AddView(4, ViewportMetrics{},
4514 [](bool added) { EXPECT_TRUE(added); });
4515 });
4516 reportLatch.Wait();
4517 ASSERT_TRUE(hasImplicitView);
4518 ASSERT_EQ(viewIds.size(), 2u);
4519 ASSERT_EQ(viewIds[1], 4ll);
4520
4521 PlatformViewNotifyDestroyed(shell.get());
4522 DestroyShell(std::move(shell), task_runners);
4523}
4524
4525// Test that add view fails if the view ID already exists.
4526TEST_F(ShellTest, ShellCannotAddDuplicateViewId) {
4527 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4528 Settings settings = CreateSettingsForFixture();
4530 "io.flutter.test." + GetCurrentTestName() + ".",
4531 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4532 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4533 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4534 thread_host.raster_thread->GetTaskRunner(),
4535 thread_host.ui_thread->GetTaskRunner(),
4536 thread_host.io_thread->GetTaskRunner());
4537 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4538 ASSERT_TRUE(shell);
4539
4540 bool has_implicit_view;
4541 std::vector<int64_t> view_ids;
4542 fml::AutoResetWaitableEvent report_latch;
4543 AddNativeCallback("NativeReportViewIdsCallback",
4544 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4545 ParseViewIdsCallback(args, &has_implicit_view, &view_ids);
4546 report_latch.Signal();
4547 }));
4548
4549 PlatformViewNotifyCreated(shell.get());
4550 auto configuration = RunConfiguration::InferFromSettings(settings);
4551 configuration.SetEntrypoint("testReportViewIds");
4552 RunEngine(shell.get(), std::move(configuration));
4553
4554 report_latch.Wait();
4555 ASSERT_TRUE(has_implicit_view);
4556 ASSERT_EQ(view_ids.size(), 1u);
4557 ASSERT_EQ(view_ids[0], kImplicitViewId);
4558
4559 // Add view 123.
4561 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell,
4562 &add_latch] {
4563 shell->GetPlatformView()->AddView(123, ViewportMetrics{}, [&](bool added) {
4564 EXPECT_TRUE(added);
4565 add_latch.Signal();
4566 });
4567 });
4568
4569 add_latch.Wait();
4570
4571 report_latch.Wait();
4572 ASSERT_EQ(view_ids.size(), 2u);
4573 ASSERT_EQ(view_ids[0], kImplicitViewId);
4574 ASSERT_EQ(view_ids[1], 123);
4575
4576 // Attempt to add duplicate view ID 123. This should fail.
4577 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell,
4578 &add_latch] {
4579 shell->GetPlatformView()->AddView(123, ViewportMetrics{}, [&](bool added) {
4580 EXPECT_FALSE(added);
4581 add_latch.Signal();
4582 });
4583 });
4584
4585 add_latch.Wait();
4586
4587 PlatformViewNotifyDestroyed(shell.get());
4588 DestroyShell(std::move(shell), task_runners);
4589}
4590
4591// Test that remove view fails if the view ID does not exist.
4592TEST_F(ShellTest, ShellCannotRemoveNonexistentId) {
4593 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign)
4594 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4595 Settings settings = CreateSettingsForFixture();
4597 "io.flutter.test." + GetCurrentTestName() + ".",
4598 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4599 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4600 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4601 thread_host.raster_thread->GetTaskRunner(),
4602 thread_host.ui_thread->GetTaskRunner(),
4603 thread_host.io_thread->GetTaskRunner());
4604 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4605 ASSERT_TRUE(shell);
4606
4607 bool has_implicit_view;
4608 std::vector<int64_t> view_ids;
4609 fml::AutoResetWaitableEvent report_latch;
4610 AddNativeCallback("NativeReportViewIdsCallback",
4611 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4612 ParseViewIdsCallback(args, &has_implicit_view, &view_ids);
4613 report_latch.Signal();
4614 }));
4615
4616 PlatformViewNotifyCreated(shell.get());
4617 auto configuration = RunConfiguration::InferFromSettings(settings);
4618 configuration.SetEntrypoint("testReportViewIds");
4619 RunEngine(shell.get(), std::move(configuration));
4620
4621 report_latch.Wait();
4622 ASSERT_TRUE(has_implicit_view);
4623 ASSERT_EQ(view_ids.size(), 1u);
4624 ASSERT_EQ(view_ids[0], kImplicitViewId);
4625
4626 // Remove view 123. This should fail as this view doesn't exist.
4627 fml::AutoResetWaitableEvent remove_latch;
4628 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(),
4629 [&shell, &remove_latch] {
4630 shell->GetPlatformView()->RemoveView(123, [&](bool removed) {
4631 EXPECT_FALSE(removed);
4632 remove_latch.Signal();
4633 });
4634 });
4635
4636 remove_latch.Wait();
4637
4638 PlatformViewNotifyDestroyed(shell.get());
4639 DestroyShell(std::move(shell), task_runners);
4640}
4641
4642// Parse the arguments of NativeReportViewWidthsCallback and
4643// store them in viewWidths.
4644static void ParseViewWidthsCallback(const Dart_NativeArguments& args,
4645 std::map<int64_t, int64_t>* viewWidths) {
4646 Dart_Handle exception = nullptr;
4647 viewWidths->clear();
4648 std::vector<int64_t> viewWidthPacket =
4650 exception);
4651 ASSERT_EQ(exception, nullptr);
4652 ASSERT_EQ(viewWidthPacket.size() % 2, 0ul);
4653 for (size_t packetIndex = 0; packetIndex < viewWidthPacket.size();
4654 packetIndex += 2) {
4655 (*viewWidths)[viewWidthPacket[packetIndex]] =
4656 viewWidthPacket[packetIndex + 1];
4657 }
4658}
4659
4660// Ensure that PlatformView::SetViewportMetrics and Shell::AddView that were
4661// dispatched before the isolate is run have been flushed to the Dart VM when
4662// the main function starts.
4663TEST_F(ShellTest, ShellFlushesPlatformStatesByMain) {
4664 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4665 Settings settings = CreateSettingsForFixture();
4667 "io.flutter.test." + GetCurrentTestName() + ".",
4668 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4669 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4670 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4671 thread_host.raster_thread->GetTaskRunner(),
4672 thread_host.ui_thread->GetTaskRunner(),
4673 thread_host.io_thread->GetTaskRunner());
4674 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4675 ASSERT_TRUE(shell);
4676
4677 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4678 auto platform_view = shell->GetPlatformView();
4679 // The construtor for ViewportMetrics{_, width, _, _, _} (only the 2nd
4680 // argument matters in this test).
4681 platform_view->SetViewportMetrics(0, ViewportMetrics{1, 10, 1, 0, 0});
4682 shell->GetPlatformView()->AddView(1, ViewportMetrics{1, 30, 1, 0, 0},
4683 [](bool added) { ASSERT_TRUE(added); });
4684 platform_view->SetViewportMetrics(0, ViewportMetrics{1, 20, 1, 0, 0});
4685 });
4686
4687 bool first_report = true;
4688 std::map<int64_t, int64_t> viewWidths;
4689 fml::AutoResetWaitableEvent reportLatch;
4690 auto nativeViewWidthsCallback = [&reportLatch, &viewWidths,
4691 &first_report](Dart_NativeArguments args) {
4692 EXPECT_TRUE(first_report);
4693 first_report = false;
4694 ParseViewWidthsCallback(args, &viewWidths);
4695 reportLatch.Signal();
4696 };
4697 AddNativeCallback("NativeReportViewWidthsCallback",
4698 CREATE_NATIVE_ENTRY(nativeViewWidthsCallback));
4699
4700 PlatformViewNotifyCreated(shell.get());
4701 auto configuration = RunConfiguration::InferFromSettings(settings);
4702 configuration.SetEntrypoint("testReportViewWidths");
4703 RunEngine(shell.get(), std::move(configuration));
4704
4705 reportLatch.Wait();
4706 EXPECT_EQ(viewWidths.size(), 2u);
4707 EXPECT_EQ(viewWidths[0], 20ll);
4708 EXPECT_EQ(viewWidths[1], 30ll);
4709
4710 PlatformViewNotifyDestroyed(shell.get());
4711 DestroyShell(std::move(shell), task_runners);
4712}
4713
4714// A view can be added and removed before the Dart isolate is launched.
4715TEST_F(ShellTest, CanRemoveViewBeforeLaunchingIsolate) {
4716 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4717 Settings settings = CreateSettingsForFixture();
4719 "io.flutter.test." + GetCurrentTestName() + ".",
4720 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4721 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4722 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4723 thread_host.raster_thread->GetTaskRunner(),
4724 thread_host.ui_thread->GetTaskRunner(),
4725 thread_host.io_thread->GetTaskRunner());
4726 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4727 ASSERT_TRUE(shell);
4728
4729 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4730 auto platform_view = shell->GetPlatformView();
4731
4732 // A view can be added and removed all before the isolate launches.
4733 // The pending add view operation is cancelled, the view is never
4734 // added to the Dart isolate.
4735 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 30, 1, 0, 0},
4736 [](bool added) { ASSERT_FALSE(added); });
4737 shell->GetPlatformView()->RemoveView(
4738 123, [](bool removed) { ASSERT_FALSE(removed); });
4739 });
4740
4741 bool first_report = true;
4742 std::map<int64_t, int64_t> view_widths;
4743 fml::AutoResetWaitableEvent report_latch;
4744 AddNativeCallback("NativeReportViewWidthsCallback",
4745 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4746 EXPECT_TRUE(first_report);
4747 first_report = false;
4748 ParseViewWidthsCallback(args, &view_widths);
4749 report_latch.Signal();
4750 }));
4751
4752 PlatformViewNotifyCreated(shell.get());
4753 auto configuration = RunConfiguration::InferFromSettings(settings);
4754 configuration.SetEntrypoint("testReportViewWidths");
4755 RunEngine(shell.get(), std::move(configuration));
4756
4757 report_latch.Wait();
4758 EXPECT_EQ(view_widths.size(), 1u);
4759
4760 PlatformViewNotifyDestroyed(shell.get());
4761 DestroyShell(std::move(shell), task_runners);
4762}
4763
4764// Ensure pending "add views" failures are properly flushed when the Dart
4765// isolate is launched.
4766TEST_F(ShellTest, IgnoresBadAddViewsBeforeLaunchingIsolate) {
4767 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4768 Settings settings = CreateSettingsForFixture();
4770 "io.flutter.test." + GetCurrentTestName() + ".",
4771 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4772 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4773 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4774 thread_host.raster_thread->GetTaskRunner(),
4775 thread_host.ui_thread->GetTaskRunner(),
4776 thread_host.io_thread->GetTaskRunner());
4777 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4778 ASSERT_TRUE(shell);
4779
4780 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4781 auto platform_view = shell->GetPlatformView();
4782
4783 // Add the same view twice. The second time should fail.
4784 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 100, 1, 0, 0},
4785 [](bool added) { ASSERT_TRUE(added); });
4786
4787 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 200, 1, 0, 0},
4788 [](bool added) { ASSERT_FALSE(added); });
4789
4790 // Add another view. Previous failures should not affect this.
4791 shell->GetPlatformView()->AddView(456, ViewportMetrics{1, 300, 1, 0, 0},
4792 [](bool added) { ASSERT_TRUE(added); });
4793 });
4794
4795 bool first_report = true;
4796 std::map<int64_t, int64_t> view_widths;
4797 fml::AutoResetWaitableEvent report_latch;
4798 AddNativeCallback("NativeReportViewWidthsCallback",
4799 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4800 EXPECT_TRUE(first_report);
4801 first_report = false;
4802 ParseViewWidthsCallback(args, &view_widths);
4803 report_latch.Signal();
4804 }));
4805
4806 PlatformViewNotifyCreated(shell.get());
4807 auto configuration = RunConfiguration::InferFromSettings(settings);
4808 configuration.SetEntrypoint("testReportViewWidths");
4809 RunEngine(shell.get(), std::move(configuration));
4810
4811 report_latch.Wait();
4812 EXPECT_EQ(view_widths.size(), 3u);
4813 EXPECT_EQ(view_widths[0], 0);
4814 EXPECT_EQ(view_widths[123], 100);
4815 EXPECT_EQ(view_widths[456], 300);
4816
4817 PlatformViewNotifyDestroyed(shell.get());
4818 DestroyShell(std::move(shell), task_runners);
4819}
4820
4821TEST_F(ShellTest, WillLogWarningWhenImpellerIsOptedOut) {
4822#if !IMPELLER_SUPPORTS_RENDERING
4823 GTEST_SKIP() << "This platform doesn't support Impeller.";
4824#endif
4825 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4826 Settings settings = CreateSettingsForFixture();
4827 settings.enable_impeller = false;
4828 settings.warn_on_impeller_opt_out = true;
4829 // Log captures are thread specific. Just put the shell in single threaded
4830 // configuration.
4831 const auto& runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
4832 TaskRunners task_runners("test", runner, runner, runner, runner);
4833 std::ostringstream stream;
4835 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4836 ASSERT_TRUE(
4837 stream.str().find("[Action Required]: Impeller opt-out deprecated.") !=
4838 std::string::npos);
4839 ASSERT_TRUE(shell);
4840 DestroyShell(std::move(shell), task_runners);
4841}
4842
4843TEST_F(ShellTest, SendViewFocusEvent) {
4844 Settings settings = CreateSettingsForFixture();
4845 TaskRunners task_runners = GetTaskRunnersForFixture();
4847 std::string last_event;
4848
4849 AddNativeCallback(
4850 "NotifyNative",
4851 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4852
4853 AddNativeCallback("NotifyMessage",
4854 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4855 const auto message_from_dart =
4857 Dart_GetNativeArgument(args, 0));
4858 last_event = message_from_dart;
4859 latch.Signal();
4860 }));
4861 fml::AutoResetWaitableEvent check_latch;
4862
4863 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4864 ASSERT_TRUE(shell->IsSetup());
4865
4866 auto configuration = RunConfiguration::InferFromSettings(settings);
4867
4868 configuration.SetEntrypoint("testSendViewFocusEvent");
4869 RunEngine(shell.get(), std::move(configuration));
4870 latch.Wait();
4871 latch.Reset();
4872
4873 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
4874 shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
4875 1, ViewFocusState::kFocused, ViewFocusDirection::kUndefined));
4876 });
4877 latch.Wait();
4878 ASSERT_EQ(last_event,
4879 "1 ViewFocusState.focused ViewFocusDirection.undefined");
4880
4881 latch.Reset();
4882 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
4883 shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
4884 2, ViewFocusState::kUnfocused, ViewFocusDirection::kBackward));
4885 });
4886 latch.Wait();
4887 ASSERT_EQ(last_event,
4888 "2 ViewFocusState.unfocused ViewFocusDirection.backward");
4889 DestroyShell(std::move(shell), task_runners);
4890}
4891
4892TEST_F(ShellTest, ProvidesEngineId) {
4893 Settings settings = CreateSettingsForFixture();
4894 TaskRunners task_runners = GetTaskRunnersForFixture();
4896
4897 std::optional<int> reported_handle = std::nullopt;
4898
4899 AddNativeCallback(
4900 "ReportEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4901 Dart_Handle arg = Dart_GetNativeArgument(args, 0);
4902 if (Dart_IsNull(arg)) {
4903 reported_handle = std::nullopt;
4904 } else {
4905 reported_handle = tonic::DartConverter<int64_t>::FromDart(arg);
4906 }
4907 latch.Signal();
4908 }));
4909 fml::AutoResetWaitableEvent check_latch;
4910
4911 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4912 ASSERT_TRUE(shell->IsSetup());
4913
4914 auto configuration = RunConfiguration::InferFromSettings(settings);
4915 configuration.SetEngineId(99);
4916 configuration.SetEntrypoint("providesEngineId");
4917 RunEngine(shell.get(), std::move(configuration));
4918
4919 latch.Wait();
4920 ASSERT_EQ(reported_handle, 99);
4921
4922 latch.Reset();
4923
4925 shell->GetTaskRunners().GetUITaskRunner(), [&]() {
4926 ASSERT_EQ(shell->GetEngine()->GetLastEngineId(), 99);
4927 latch.Signal();
4928 });
4929 latch.Wait();
4930 DestroyShell(std::move(shell), task_runners);
4931}
4932
4933TEST_F(ShellTest, ProvidesNullEngineId) {
4934 Settings settings = CreateSettingsForFixture();
4935 TaskRunners task_runners = GetTaskRunnersForFixture();
4937
4938 std::optional<int> reported_handle = std::nullopt;
4939
4940 AddNativeCallback(
4941 "ReportEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4942 Dart_Handle arg = Dart_GetNativeArgument(args, 0);
4943 if (Dart_IsNull(arg)) {
4944 reported_handle = std::nullopt;
4945 } else {
4946 reported_handle = tonic::DartConverter<int64_t>::FromDart(arg);
4947 }
4948 latch.Signal();
4949 }));
4950 fml::AutoResetWaitableEvent check_latch;
4951
4952 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4953 ASSERT_TRUE(shell->IsSetup());
4954
4955 auto configuration = RunConfiguration::InferFromSettings(settings);
4956 configuration.SetEntrypoint("providesEngineId");
4957 RunEngine(shell.get(), std::move(configuration));
4958
4959 latch.Wait();
4960 ASSERT_EQ(reported_handle, std::nullopt);
4961 DestroyShell(std::move(shell), task_runners);
4962}
4963
4964TEST_F(ShellTest, MergeUIAndPlatformThreadsAfterLaunch) {
4965 Settings settings = CreateSettingsForFixture();
4966 settings.merged_platform_ui_thread =
4967 Settings::MergedPlatformUIThread::kMergeAfterLaunch;
4969 "io.flutter.test." + GetCurrentTestName() + ".",
4970 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4971 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4972 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4973 thread_host.raster_thread->GetTaskRunner(),
4974 thread_host.ui_thread->GetTaskRunner(),
4975 thread_host.io_thread->GetTaskRunner());
4976
4977 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4978 ASSERT_TRUE(shell);
4979
4981 task_runners.GetUITaskRunner()->GetTaskQueueId(),
4982 task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
4983
4985 AddNativeCallback(
4986 "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4987 ASSERT_TRUE(
4989 latch.Signal();
4990 }));
4991
4992 auto configuration = RunConfiguration::InferFromSettings(settings);
4993 configuration.SetEntrypoint("mainNotifyNative");
4994 RunEngine(shell.get(), std::move(configuration));
4995
4996 latch.Wait();
4997
4999 task_runners.GetUITaskRunner()->GetTaskQueueId(),
5000 task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
5001
5002 DestroyShell(std::move(shell), task_runners);
5003}
5004
5005TEST_F(ShellTest, ReleaseResourceContextWhenIOManagerIsDeleted) {
5006 TaskRunners task_runners = GetTaskRunnersForFixture();
5007 auto settings = CreateSettingsForFixture();
5008 bool called_release_resource_context = false;
5009 Shell::CreateCallback<PlatformView> platform_view_create_callback =
5010 [task_runners, &called_release_resource_context](flutter::Shell& shell) {
5011 auto result = std::make_unique<::testing::NiceMock<TestPlatformView>>(
5012 shell, task_runners);
5013 ON_CALL(*result, ReleaseResourceContext())
5014 .WillByDefault(
5015 ::testing::Assign(&called_release_resource_context, true));
5016 return result;
5017 };
5018
5019 auto parent_shell = CreateShell({
5020 .settings = settings,
5021 .task_runners = task_runners,
5022 .platform_view_create_callback = platform_view_create_callback,
5023 });
5024
5025 std::unique_ptr<Shell> child_shell;
5026 PostSync(
5027 parent_shell->GetTaskRunners().GetPlatformTaskRunner(),
5028 [&parent_shell, &settings, &child_shell, platform_view_create_callback] {
5029 auto configuration = RunConfiguration::InferFromSettings(settings);
5030 configuration.SetEntrypoint("emptyMain");
5031 auto child = parent_shell->Spawn(
5032 std::move(configuration), "", platform_view_create_callback,
5033 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
5034 child_shell = std::move(child);
5035 });
5036
5037 DestroyShell(std::move(parent_shell), task_runners);
5038 ASSERT_FALSE(called_release_resource_context);
5039
5040 DestroyShell(std::move(child_shell), task_runners);
5041 ASSERT_TRUE(called_release_resource_context);
5042}
5043
5044TEST_F(ShellTest, ShoulDiscardLayerTreeIfFrameIsSizedIncorrectly) {
5045 Settings settings = CreateSettingsForFixture();
5046 auto task_runner = CreateNewThread();
5047 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
5048 task_runner);
5049 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
5050
5052 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
5053 shell->GetPlatformView()->SetViewportMetrics(
5054 kImplicitViewId,
5055 {
5056 1.0, // p_device_pixel_ratio
5057 500, // p_physical_width
5058 800, // p_physical_height
5059 1, // p_min_width_constraint,
5060 1000, // p_max_width_constraint,
5061 1, // p_min_height_constraint,
5062 1000, // p_max_height_constraint,
5063 0, // p_physical_padding_top
5064 0, // p_physical_padding_right
5065 0, // p_physical_padding_bottom
5066 0, // p_physical_padding_left
5067 0, // p_physical_view_inset_top,
5068 0, // p_physical_view_inset_right,
5069 0, // p_physical_view_inset_bottom,
5070 0, // p_physical_view_inset_left,
5071 0, // p_physical_system_gesture_inset_top,
5072 0, // p_physical_system_gesture_inset_right,
5073 0, // p_physical_system_gesture_inset_bottom,
5074 0, // p_physical_system_gesture_inset_left,
5075 22, // p_physical_touch_slop,
5076 {}, // p_physical_display_features_bounds,
5077 {}, // p_physical_display_features_type,
5078 {}, // p_physical_display_features_state,
5079 0 // p_display_id
5080 });
5081 });
5082 PumpOneFrame(shell.get());
5083
5084 auto layer_tree =
5085 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5086 /*frame_size=*/DlISize(100, 100));
5087 ASSERT_FALSE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5088 *layer_tree));
5089 auto over_width =
5090 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5091 /*frame_size=*/DlISize(1010, 100));
5092 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5093 *over_width));
5094 auto over_height =
5095 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5096 /*frame_size=*/DlISize(100, 1010));
5097 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5098 *over_height));
5099 auto min_width = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5100 /*frame_size=*/DlISize(0, 100));
5101 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5102 *min_width));
5103 auto min_height = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5104 /*frame_size=*/DlISize(100, 0));
5105 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5106 *min_height));
5107 DestroyShell(std::move(shell), task_runners);
5108}
5109
5110} // namespace testing
5111} // namespace flutter
5112
5113// NOLINTEND(clang-analyzer-core.StackAddressEscape)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
GLenum type
std::vector< std::unique_ptr< fml::Mapping > > GetAsMappings(const std::string &asset_pattern, const std::optional< std::string > &subdir) const override
Same as GetAsMapping() but returns mappings for all files who's name matches a given pattern....
std::unique_ptr< fml::Mapping > GetAsMapping(const std::string &asset_name) const override
bool PushBack(std::unique_ptr< AssetResolver > resolver)
Adds an asset resolver to the end of the resolver queue. Assets would be loaded from this resolver af...
AssetResolverType
Identifies the type of AssetResolver an instance is.
Represents an instance of a live isolate. An isolate is a separate Dart execution context....
static bool IsRunningPrecompiledCode()
Checks if VM instances in the process can run precompiled code. This call can be made at any time and...
Definition dart_vm.cc:176
static bool IsInstanceRunning()
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawRect(const DlRect &rect, const DlPaint &paint) override
void PrerollSetup(PrerollContext *context, const DlMatrix &matrix) override
void PrerollFinalize(PrerollContext *context, const DlMatrix &matrix) override
bool TryToPrepareRasterCache(const PaintContext &context, bool parent_cached=false) const override
bool Draw(const PaintContext &context, const DlPaint *paint) const override
static std::shared_ptr< DlImageFilter > MakeBlur(DlScalar sigma_x, DlScalar sigma_y, DlTileMode tile_mode)
Used for fixed refresh rate cases.
Definition stopwatch.h:80
static constexpr Phase kPhases[kCount]
Definition settings.h:37
fml::TimePoint Set(Phase phase, fml::TimePoint value)
Definition settings.h:44
fml::TimePoint Get(Phase phase) const
Definition settings.h:43
The minimal interface necessary for defining a decoder that can be used for both single and multi-fra...
void PrerollSetup(PrerollContext *context, const DlMatrix &matrix) override
void PrerollFinalize(PrerollContext *context, const DlMatrix &matrix) override
bool Draw(const PaintContext &context, const DlPaint *paint) const override
bool TryToPrepareRasterCache(const PaintContext &context, bool parent_cached=false) const override
void set_delegate(DlCanvas *canvas)
void set_preroll_delegate(const DlRect &cull_rect, const DlMatrix &matrix)
const std::vector< std::shared_ptr< Mutator > >::const_iterator Begin() const
Specifies all the configuration required by the runtime library to launch the root isolate....
void SetEntrypoint(std::string entrypoint)
Updates the main application entrypoint. If this is not set, the "main" method is used as the entrypo...
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
virtual bool NotifyIdle(fml::TimeDelta deadline)
Notify the Dart VM that no frame workloads are expected on the UI task runner till the specified dead...
std::map< std::string_view, std::string_view > ServiceProtocolMap
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, const TaskRunners &task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer, bool is_gpu_disabled=false)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition shell.cc:221
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition shell.cc:911
std::shared_ptr< const fml::SyncSwitch > GetIsGpuDisabledSyncSwitch() const override
Accessor for the disable GPU SyncSwitch.
Definition shell.cc:2351
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition shell.h:121
bool IsSetup() const
Used by embedders to check if all shell subcomponents are initialized. It is the embedder's responsib...
Definition shell.cc:834
fml::RefPtr< fml::TaskRunner > GetRasterTaskRunner() const
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
~MockTexture() override=default
MockTexture(int64_t textureId, std::shared_ptr< fml::AutoResetWaitableEvent > latch)
void Paint(PaintContext &context, const DlRect &bounds, bool freeze, const DlImageSampling) override
static void PlatformViewNotifyDestroyed(Shell *shell)
static void PlatformViewNotifyCreated(Shell *shell)
Definition shell_test.cc:93
static std::unique_ptr< ShellTestPlatformView > Create(BackendType backend, PlatformView::Delegate &delegate, const TaskRunners &task_runners, const std::shared_ptr< ShellTestVsyncClock > &vsync_clock, const CreateVsyncWaiter &create_vsync_waiter, const std::shared_ptr< ShellTestExternalViewEmbedder > &shell_test_external_view_embedder, const std::shared_ptr< const fml::SyncSwitch > &is_gpu_disabled_sync_switch)
An image generator that always creates a 1x1 single-frame green image.
unsigned int GetFrameCount() const
Get the number of frames that the encoded image stores. This method is always expected to be called b...
const SkImageInfo & GetInfo()
Returns basic information about the contents of the encoded image. This information can almost always...
const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index)
Get information about a single frame in the context of a multi-frame image, useful for animation and ...
bool GetPixels(const SkImageInfo &info, void *pixels, size_t row_bytes, unsigned int frame_index, std::optional< unsigned int > prior_frame)
Decode the image into a given buffer. This method is currently always used for sub-pixel image decodi...
SkISize GetScaledDimensions(float scale)
Given a scale value, find the closest image size that can be used for efficiently decoding the image....
unsigned int GetPlayCount() const
The number of times an animated image should play through before playback stops.
TestAssetResolver(bool valid, AssetResolver::AssetResolverType type)
bool IsValidAfterAssetManagerChange() const override
Certain asset resolvers are still valid after the asset manager is replaced before a hot reload,...
std::unique_ptr< fml::Mapping > GetAsMapping(const std::string &asset_name) const override
bool operator==(const AssetResolver &other) const override
AssetResolver::AssetResolverType GetType() const override
Gets the type of AssetResolver this is. Types are defined in AssetResolverType.
std::vector< std::unique_ptr< fml::Mapping > > GetAsMappings(const std::string &asset_pattern, const std::optional< std::string > &subdir) const override
Same as GetAsMapping() but returns mappings for all files who's name matches a given pattern....
std::unique_ptr< fml::Mapping > GetAsMapping(const std::string &asset_name) const override
bool operator==(const AssetResolver &other) const override
bool IsValidAfterAssetManagerChange() const override
Certain asset resolvers are still valid after the asset manager is replaced before a hot reload,...
ThreadCheckingAssetResolver(std::shared_ptr< fml::ConcurrentMessageLoop > concurrent_loop)
AssetResolverType GetType() const
Gets the type of AssetResolver this is. Types are defined in AssetResolverType.
bool WaitWithTimeout(TimeDelta timeout)
static std::unique_ptr< FileMapping > CreateReadOnly(const std::string &path)
Definition mapping.cc:20
static void CaptureNextLog(std::ostringstream *stream)
Definition logging.cc:129
A Mapping like NonOwnedMapping, but uses Free as its release proc.
Definition mapping.h:144
static MallocMapping Copy(const T *begin, const T *end)
Definition mapping.h:162
static void EnsureInitializedForCurrentThread()
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
const UniqueFD & fd()
Definition file.h:147
const std::string & path() const
Definition file.h:146
bool ok() const
Definition status.h:71
fml::StatusCode code() const
Definition status.h:63
std::string_view message() const
Definition status.h:75
static bool RunsOnTheSameThread(TaskQueueId queue_a, TaskQueueId queue_b)
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
virtual void PostTask(const fml::closure &task) override
virtual bool RunsTasksOnCurrentThread()
virtual TaskQueueId GetTaskQueueId()
static void DisableNextThreadCheckFailure()
constexpr int64_t ToMicroseconds() const
Definition time_delta.h:62
static constexpr TimeDelta Max()
Definition time_delta.h:37
static constexpr TimeDelta FromMicroseconds(int64_t micros)
Definition time_delta.h:43
static constexpr TimeDelta Zero()
Definition time_delta.h:33
constexpr TimeDelta ToEpochDelta() const
Definition time_point.h:52
static TimePoint Now()
Definition time_point.cc:49
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition time_point.h:43
bool is_valid() const
int32_t value
const EmbeddedViewParams * params
VkSurfaceKHR surface
Definition main.cc:65
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
G_BEGIN_DECLS GBytes * message
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
static constexpr FlutterViewId kImplicitViewId
union flutter::testing::@2824::KeyboardChange::@78 content
FlTexture * texture
static void LogSkData(const sk_sp< SkData > &data, const char *title)
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition testing.cc:14
TEST_F(DisplayListTest, Defaults)
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
static void ParseViewIdsCallback(const Dart_NativeArguments &args, bool *hasImplicitView, std::vector< int64_t > *viewIds)
static void TestDartVmFlags(std::vector< const char * > &flags)
static sk_sp< DisplayList > MakeSizedDisplayList(int width, int height)
std::function< void(std::shared_ptr< ContainerLayer > root)> LayerTreeBuilder
Definition shell_test.h:33
static void PostSync(const fml::RefPtr< fml::TaskRunner > &task_runner, const fml::closure &task)
static void ParseViewWidthsCallback(const Dart_NativeArguments &args, std::map< int64_t, int64_t > *viewWidths)
static bool ValidateShell(Shell *shell)
static std::string CreateFlagsString(std::vector< const char * > &flags)
TEST(NativeAssetsManagerTest, NoAvailableAssets)
static void CheckFrameTimings(const std::vector< FrameTiming > &timings, fml::TimePoint start, fml::TimePoint finish)
static size_t GetRasterizerResourceCacheBytesSync(const Shell &shell)
static void ValidateDestroyPlatformView(Shell *shell)
static bool RasterizerIsTornDown(Shell *shell)
constexpr int64_t kImplicitViewId
const char * GetSkiaVersion()
Definition version.cc:19
static void InvokePlatformMessageResponseCallback(JNIEnv *env, jobject jcaller, jlong shell_holder, jint responseId, jobject message, jint position)
@ kHardEdge
Definition layer.h:43
static void InvokePlatformMessageEmptyResponseCallback(JNIEnv *env, jobject jcaller, jlong shell_holder, jint responseId)
impeller::Matrix DlMatrix
impeller::ISize32 DlISize
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
impeller::Size DlSize
const char * GetFlutterEngineVersion()
Definition version.cc:11
static void LoadDartDeferredLibrary(JNIEnv *env, jobject obj, jlong shell_holder, jint jLoadingUnitId, jobjectArray jSearchPaths)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
Settings SettingsFromCommandLine(const fml::CommandLine &command_line, bool require_merged_platform_ui_thread)
Definition switches.cc:230
TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders)
impeller::Point DlPoint
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
std::string BacktraceHere(size_t offset)
Definition backtrace.cc:43
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition file_posix.cc:97
RefPtr< T > AdoptRef(T *ptr)
Definition ref_ptr.h:227
static fml::UniqueFD CreateDirectory(const fml::UniqueFD &base_directory, const std::vector< std::string > &components, FilePermission permission, size_t index)
Definition file.cc:12
std::function< void()> closure
Definition closure.h:14
Definition ref_ptr.h:261
static const size_t kGrCacheMaxByteSize
int32_t height
int32_t width
static constexpr DlColor kRed()
Definition dl_color.h:71
Info about a single frame in the context of a multi-frame image, useful for animation and blending.
LayerStateStack & state_stack
Definition layer.h:91
GrDirectContext * gr_context
Definition layer.h:47
bool enable_software_rendering
Definition settings.h:317
MergedPlatformUIThread merged_platform_ui_thread
Definition settings.h:379
std::shared_ptr< const fml::Mapping > persistent_isolate_data
Definition settings.h:345
std::vector< std::string > dart_flags
Definition settings.h:145
bool warn_on_impeller_opt_out
Definition settings.h:245
The collection of all the threads used by the engine.
Definition thread_host.h:21
std::unique_ptr< fml::Thread > io_thread
Definition thread_host.h:86
std::unique_ptr< fml::Thread > platform_thread
Definition thread_host.h:83
std::string name_prefix
Definition thread_host.h:82
std::unique_ptr< fml::Thread > raster_thread
Definition thread_host.h:85
std::unique_ptr< fml::Thread > ui_thread
Definition thread_host.h:84
static FrameContent ImplicitView(double width, double height, LayerTreeBuilder builder)
Definition shell_test.cc:47
static FrameContent DummyView(double width=1, double height=1)
Definition shell_test.cc:29
Represents the 2 code paths available when calling |SyncSwitchExecute|.
Definition sync_switch.h:35
std::string str() const
Definition logging.cc:122
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
const size_t start
#define CREATE_NATIVE_ENTRY(native_entry)
std::shared_ptr< const fml::Mapping > data
int64_t texture_id