Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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 SnapshotPixelFormat::kDontCare);
2493 EXPECT_NE(image, nullptr);
2494
2495 latch->Signal();
2496 });
2497 latch->Wait();
2498 DestroyShell(std::move(shell), task_runners);
2499}
2500
2501TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
2502 Settings settings = CreateSettingsForFixture();
2503 std::unique_ptr<Shell> shell = CreateShell(settings);
2504
2505 // 1. Construct a picture and a picture layer to be raster cached.
2506 sk_sp<DisplayList> display_list = MakeSizedDisplayList(10, 10);
2507 auto display_list_layer = std::make_shared<DisplayListLayer>(
2508 DlPoint(0, 0), MakeSizedDisplayList(100, 100), false, false);
2509 display_list_layer->set_paint_bounds(DlRect::MakeWH(100, 100));
2510
2511 // 2. Rasterize the picture and the picture layer in the raster cache.
2512 std::promise<bool> rasterized;
2513
2514 shell->GetTaskRunners().GetRasterTaskRunner()->PostTask(
2515 [&shell, &rasterized, &display_list, &display_list_layer] {
2516 std::vector<RasterCacheItem*> raster_cache_items;
2517 auto* compositor_context = shell->GetRasterizer()->compositor_context();
2518 auto& raster_cache = compositor_context->raster_cache();
2519
2520 LayerStateStack state_stack;
2521 FixedRefreshRateStopwatch raster_time;
2523 PaintContext paint_context = {
2524 // clang-format off
2525 .state_stack = state_stack,
2526 .canvas = nullptr,
2527 .gr_context = nullptr,
2528 .dst_color_space = nullptr,
2529 .view_embedder = nullptr,
2530 .raster_time = raster_time,
2531 .ui_time = ui_time,
2532 .texture_registry = nullptr,
2533 .raster_cache = &raster_cache,
2534 // clang-format on
2535 };
2536
2537 PrerollContext preroll_context = {
2538 // clang-format off
2539 .raster_cache = &raster_cache,
2540 .gr_context = nullptr,
2541 .view_embedder = nullptr,
2542 .state_stack = state_stack,
2543 .dst_color_space = nullptr,
2544 .surface_needs_readback = false,
2545 .raster_time = raster_time,
2546 .ui_time = ui_time,
2547 .texture_registry = nullptr,
2548 .has_platform_view = false,
2549 .has_texture_layer = false,
2550 .raster_cached_entries = &raster_cache_items,
2551 // clang-format on
2552 };
2553
2554 // 2.1. Rasterize the picture. Call Draw multiple times to pass the
2555 // access threshold (default to 3) so a cache can be generated.
2556 DisplayListBuilder dummy_canvas;
2557 DlPaint paint;
2558 bool picture_cache_generated;
2559 DisplayListRasterCacheItem display_list_raster_cache_item(
2560 display_list, SkPoint(), true, false);
2561 for (int i = 0; i < 4; i += 1) {
2562 DlMatrix matrix;
2563 state_stack.set_preroll_delegate(matrix);
2564 display_list_raster_cache_item.PrerollSetup(&preroll_context, matrix);
2565 display_list_raster_cache_item.PrerollFinalize(&preroll_context,
2566 matrix);
2567 picture_cache_generated =
2568 display_list_raster_cache_item.need_caching();
2569 state_stack.set_delegate(&dummy_canvas);
2570 display_list_raster_cache_item.TryToPrepareRasterCache(paint_context);
2571 display_list_raster_cache_item.Draw(paint_context, &dummy_canvas,
2572 &paint);
2573 }
2574 ASSERT_TRUE(picture_cache_generated);
2575
2576 // 2.2. Rasterize the picture layer.
2577 LayerRasterCacheItem layer_raster_cache_item(display_list_layer.get());
2578 state_stack.set_preroll_delegate(DlMatrix());
2579 layer_raster_cache_item.PrerollSetup(&preroll_context, DlMatrix());
2580 layer_raster_cache_item.PrerollFinalize(&preroll_context, DlMatrix());
2581 state_stack.set_delegate(&dummy_canvas);
2582 layer_raster_cache_item.TryToPrepareRasterCache(paint_context);
2583 layer_raster_cache_item.Draw(paint_context, &dummy_canvas, &paint);
2584 rasterized.set_value(true);
2585 });
2586 rasterized.get_future().wait();
2587
2588 // 3. Call the service protocol and check its output.
2590 rapidjson::Document document;
2591 OnServiceProtocol(
2592 shell.get(), ServiceProtocolEnum::kEstimateRasterCacheMemory,
2593 shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2594 rapidjson::StringBuffer buffer;
2595 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2596 document.Accept(writer);
2597 std::string expected_json =
2598 "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40024,\"picture"
2599 "Bytes\":424}";
2600 std::string actual_json = buffer.GetString();
2601 ASSERT_EQ(actual_json, expected_json);
2602
2603 DestroyShell(std::move(shell));
2604}
2605
2606// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
2607// flakiness.
2608// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
2609// re-enabling.
2610TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) {
2611 auto settings = CreateSettingsForFixture();
2612
2613 DlISize wrong_size = DlISize(400, 100);
2614 DlISize expected_size = DlISize(400, 200);
2615
2616 fml::AutoResetWaitableEvent end_frame_latch;
2617 auto end_frame_callback =
2618 [&](bool should_merge_thread,
2619 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
2620 end_frame_latch.Signal();
2621 };
2622 auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2623 std::move(end_frame_callback), PostPrerollResult::kSuccess, false);
2624 std::unique_ptr<Shell> shell = CreateShell({
2625 .settings = settings,
2626 .platform_view_create_callback = ShellTestPlatformViewBuilder({
2627 .shell_test_external_view_embedder = external_view_embedder,
2628 }),
2629 });
2630
2631 // Create the surface needed by rasterizer
2632 PlatformViewNotifyCreated(shell.get());
2633
2635 shell->GetTaskRunners().GetPlatformTaskRunner(),
2636 [&shell, &expected_size]() {
2637 shell->GetPlatformView()->SetViewportMetrics(
2638 kImplicitViewId,
2639 {1.0, static_cast<double>(expected_size.width),
2640 static_cast<double>(expected_size.height), 22, 0});
2641 });
2642
2643 auto configuration = RunConfiguration::InferFromSettings(settings);
2644 configuration.SetEntrypoint("emptyMain");
2645
2646 RunEngine(shell.get(), std::move(configuration));
2647
2648 PumpOneFrame(shell.get(),
2649 ViewContent::DummyView(static_cast<double>(wrong_size.width),
2650 static_cast<double>(wrong_size.height)));
2651 end_frame_latch.Wait();
2652 // Wrong size, no frames are submitted.
2653 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2654
2655 PumpOneFrame(shell.get(), ViewContent::DummyView(
2656 static_cast<double>(expected_size.width),
2657 static_cast<double>(expected_size.height)));
2658 end_frame_latch.Wait();
2659 // Expected size, 1 frame submitted.
2660 ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2661 ASSERT_EQ(expected_size, external_view_embedder->GetLastSubmittedFrameSize());
2662
2663 PlatformViewNotifyDestroyed(shell.get());
2664 DestroyShell(std::move(shell));
2665}
2666
2667// TODO(https://github.com/flutter/flutter/issues/100273): Disabled due to
2668// flakiness.
2669// TODO(https://github.com/flutter/flutter/issues/100299): Fix it when
2670// re-enabling.
2671TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) {
2672 auto settings = CreateSettingsForFixture();
2673
2674 DlISize origin_size = DlISize(400, 100);
2675 DlISize new_size = DlISize(400, 200);
2676
2677 fml::AutoResetWaitableEvent end_frame_latch;
2678
2679 fml::AutoResetWaitableEvent resize_latch;
2680
2681 std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
2682 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
2683 auto end_frame_callback =
2684 [&](bool should_merge_thread,
2685 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
2686 if (!raster_thread_merger_ref) {
2687 raster_thread_merger_ref = raster_thread_merger;
2688 }
2689 if (should_merge_thread) {
2690 raster_thread_merger->MergeWithLease(10);
2691 external_view_embedder->UpdatePostPrerollResult(
2692 PostPrerollResult::kSuccess);
2693 }
2694 end_frame_latch.Signal();
2695
2696 if (should_merge_thread) {
2697 resize_latch.Wait();
2698 }
2699 };
2700
2701 external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2702 std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true);
2703
2704 std::unique_ptr<Shell> shell = CreateShell({
2705 .settings = settings,
2706 .platform_view_create_callback = ShellTestPlatformViewBuilder({
2707 .shell_test_external_view_embedder = external_view_embedder,
2708 }),
2709 });
2710
2711 // Create the surface needed by rasterizer
2712 PlatformViewNotifyCreated(shell.get());
2713
2715 shell->GetTaskRunners().GetPlatformTaskRunner(),
2716 [&shell, &origin_size]() {
2717 shell->GetPlatformView()->SetViewportMetrics(
2718 kImplicitViewId, {1.0, static_cast<double>(origin_size.width),
2719 static_cast<double>(origin_size.height), 22, 0});
2720 });
2721
2722 auto configuration = RunConfiguration::InferFromSettings(settings);
2723 configuration.SetEntrypoint("emptyMain");
2724
2725 RunEngine(shell.get(), std::move(configuration));
2726
2727 PumpOneFrame(shell.get(),
2728 ViewContent::DummyView(static_cast<double>(origin_size.width),
2729 static_cast<double>(origin_size.height)));
2730
2731 end_frame_latch.Wait();
2732 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2733
2735 shell->GetTaskRunners().GetPlatformTaskRunner(),
2736 [&shell, &new_size, &resize_latch]() {
2737 shell->GetPlatformView()->SetViewportMetrics(
2738 kImplicitViewId, {1.0, static_cast<double>(new_size.width),
2739 static_cast<double>(new_size.height), 22, 0});
2740 resize_latch.Signal();
2741 });
2742
2743 end_frame_latch.Wait();
2744
2745 // The frame resubmitted with origin size should be discarded after the
2746 // viewport metrics changed.
2747 ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2748
2749 // Threads will be merged at the end of this frame.
2750 PumpOneFrame(shell.get(),
2751 ViewContent::DummyView(static_cast<double>(new_size.width),
2752 static_cast<double>(new_size.height)));
2753
2754 end_frame_latch.Wait();
2755 ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
2756 ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2757 ASSERT_EQ(new_size, external_view_embedder->GetLastSubmittedFrameSize());
2758
2759 PlatformViewNotifyDestroyed(shell.get());
2760 DestroyShell(std::move(shell));
2761}
2762
2763TEST_F(ShellTest, IgnoresInvalidMetrics) {
2765 double last_device_pixel_ratio;
2766 double last_width;
2767 double last_height;
2768 auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2769 auto dpr_handle = Dart_GetNativeArgument(args, 0);
2770 ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2771 Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2772 ASSERT_FALSE(last_device_pixel_ratio == 0.0);
2773
2774 auto width_handle = Dart_GetNativeArgument(args, 1);
2775 ASSERT_TRUE(Dart_IsDouble(width_handle));
2776 Dart_DoubleValue(width_handle, &last_width);
2777 ASSERT_FALSE(last_width == 0.0);
2778
2779 auto height_handle = Dart_GetNativeArgument(args, 2);
2780 ASSERT_TRUE(Dart_IsDouble(height_handle));
2781 Dart_DoubleValue(height_handle, &last_height);
2782 ASSERT_FALSE(last_height == 0.0);
2783
2784 latch.Signal();
2785 };
2786
2787 Settings settings = CreateSettingsForFixture();
2788 auto task_runner = CreateNewThread();
2789 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2790 task_runner);
2791
2792 AddNativeCallback("ReportMetrics",
2793 CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2794
2795 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2796
2797 auto configuration = RunConfiguration::InferFromSettings(settings);
2798 configuration.SetEntrypoint("reportMetrics");
2799
2800 RunEngine(shell.get(), std::move(configuration));
2801
2802 task_runner->PostTask([&]() {
2803 // This one is invalid for having 0 pixel ratio.
2804 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2805 {0.0, 400, 200, 22, 0});
2806 task_runner->PostTask([&]() {
2807 // This one is invalid for having 0 width.
2808 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2809 {0.8, 0.0, 200, 22, 0});
2810 task_runner->PostTask([&]() {
2811 // This one is invalid for having 0 height.
2812 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2813 {0.8, 400, 0.0, 22, 0});
2814 task_runner->PostTask([&]() {
2815 // This one makes it through.
2816 shell->GetPlatformView()->SetViewportMetrics(
2817 kImplicitViewId, {0.8, 400, 200.0, 22, 0});
2818 });
2819 });
2820 });
2821 });
2822 latch.Wait();
2823 ASSERT_EQ(last_device_pixel_ratio, 0.8);
2824 ASSERT_EQ(last_width, 400.0);
2825 ASSERT_EQ(last_height, 200.0);
2826 latch.Reset();
2827
2828 task_runner->PostTask([&]() {
2829 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2830 {1.2, 600, 300, 22, 0});
2831 });
2832 latch.Wait();
2833 ASSERT_EQ(last_device_pixel_ratio, 1.2);
2834 ASSERT_EQ(last_width, 600.0);
2835 ASSERT_EQ(last_height, 300.0);
2836
2837 DestroyShell(std::move(shell), task_runners);
2838}
2839
2840TEST_F(ShellTest, IgnoresMetricsUpdateToInvalidView) {
2842 double last_device_pixel_ratio;
2843 // This callback will be called whenever any view's metrics change.
2844 auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2845 // The correct call will have a DPR of 3.
2846 auto dpr_handle = Dart_GetNativeArgument(args, 0);
2847 ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2848 Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2849 ASSERT_TRUE(last_device_pixel_ratio > 2.5);
2850
2851 latch.Signal();
2852 };
2853
2854 Settings settings = CreateSettingsForFixture();
2855 auto task_runner = CreateNewThread();
2856 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2857 task_runner);
2858
2859 AddNativeCallback("ReportMetrics",
2860 CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2861
2862 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
2863
2864 auto configuration = RunConfiguration::InferFromSettings(settings);
2865 configuration.SetEntrypoint("reportMetrics");
2866
2867 RunEngine(shell.get(), std::move(configuration));
2868
2869 task_runner->PostTask([&]() {
2870 // This one is invalid for having an nonexistent view ID.
2871 // Also, it has a DPR of 2.0 for detection.
2872 shell->GetPlatformView()->SetViewportMetrics(2, {2.0, 400, 200, 22, 0});
2873 task_runner->PostTask([&]() {
2874 // This one is valid with DPR 3.0.
2875 shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId,
2876 {3.0, 400, 200, 22, 0});
2877 });
2878 });
2879 latch.Wait();
2880 ASSERT_EQ(last_device_pixel_ratio, 3.0);
2881 latch.Reset();
2882
2883 DestroyShell(std::move(shell), task_runners);
2884}
2885
2886TEST_F(ShellTest, OnServiceProtocolSetAssetBundlePathWorks) {
2887 Settings settings = CreateSettingsForFixture();
2888 std::unique_ptr<Shell> shell = CreateShell(settings);
2889 RunConfiguration configuration =
2890 RunConfiguration::InferFromSettings(settings);
2891 configuration.SetEntrypoint("canAccessResourceFromAssetDir");
2892
2893 // Verify isolate can load a known resource with the
2894 // default asset directory - kernel_blob.bin
2896
2897 // Callback used to signal whether the resource was loaded successfully.
2898 bool can_access_resource = false;
2899 auto native_can_access_resource = [&can_access_resource,
2900 &latch](Dart_NativeArguments args) {
2901 Dart_Handle exception = nullptr;
2902 can_access_resource =
2904 latch.Signal();
2905 };
2906 AddNativeCallback("NotifyCanAccessResource",
2907 CREATE_NATIVE_ENTRY(native_can_access_resource));
2908
2909 // Callback used to delay the asset load until after the service
2910 // protocol method has finished.
2911 auto native_notify_set_asset_bundle_path =
2912 [&shell](Dart_NativeArguments args) {
2913 // Update the asset directory to a bonus path.
2915 params["assetDirectory"] = "assetDirectory";
2916 rapidjson::Document document;
2917 OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath,
2918 shell->GetTaskRunners().GetUITaskRunner(), params,
2919 &document);
2920 rapidjson::StringBuffer buffer;
2921 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2922 document.Accept(writer);
2923 };
2924 AddNativeCallback("NotifySetAssetBundlePath",
2925 CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path));
2926
2927 RunEngine(shell.get(), std::move(configuration));
2928
2929 latch.Wait();
2930 ASSERT_TRUE(can_access_resource);
2931
2932 DestroyShell(std::move(shell));
2933}
2934
2935TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) {
2936 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2937 // Make sure the shell launch does not kick off the creation of the VM
2938 // instance by already creating one upfront.
2939 auto vm_settings = CreateSettingsForFixture();
2940 auto vm_ref = DartVMRef::Create(vm_settings);
2941 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2942
2943 auto settings = vm_settings;
2944 fml::AutoResetWaitableEvent isolate_create_latch;
2945 settings.root_isolate_create_callback = [&](const auto& isolate) {
2946 isolate_create_latch.Signal();
2947 };
2948 auto shell = CreateShell(settings);
2949 ASSERT_TRUE(ValidateShell(shell.get()));
2950 auto configuration = RunConfiguration::InferFromSettings(settings);
2951 ASSERT_TRUE(configuration.IsValid());
2952 RunEngine(shell.get(), std::move(configuration));
2953 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2954 DestroyShell(std::move(shell));
2955 isolate_create_latch.Wait();
2956}
2957
2958TEST_F(ShellTest, AssetManagerSingle) {
2960 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2961 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2962
2963 std::string filename = "test_name";
2964 std::string content = "test_content";
2965
2966 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2968 ASSERT_TRUE(success);
2969
2970 AssetManager asset_manager;
2971 asset_manager.PushBack(
2972 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
2973
2974 auto mapping = asset_manager.GetAsMapping(filename);
2975 ASSERT_TRUE(mapping != nullptr);
2976
2977 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
2978 mapping->GetSize());
2979
2980 ASSERT_TRUE(result == content);
2981}
2982
2983TEST_F(ShellTest, AssetManagerMulti) {
2985 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2986 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2987
2988 std::vector<std::string> filenames = {
2989 "good0",
2990 "bad0",
2991 "good1",
2992 "bad1",
2993 };
2994
2995 for (const auto& filename : filenames) {
2996 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2997 fml::DataMapping(filename));
2998 ASSERT_TRUE(success);
2999 }
3000
3001 AssetManager asset_manager;
3002 asset_manager.PushBack(
3003 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
3004
3005 auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
3006 EXPECT_EQ(mappings.size(), 4u);
3007
3008 std::vector<std::string> expected_results = {
3009 "good0",
3010 "good1",
3011 };
3012
3013 mappings = asset_manager.GetAsMappings("(.*)good(.*)", std::nullopt);
3014 ASSERT_EQ(mappings.size(), expected_results.size());
3015
3016 for (auto& mapping : mappings) {
3017 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
3018 mapping->GetSize());
3019 EXPECT_NE(
3020 std::find(expected_results.begin(), expected_results.end(), result),
3021 expected_results.end());
3022 }
3023}
3024
3025#if defined(OS_FUCHSIA)
3026TEST_F(ShellTest, AssetManagerMultiSubdir) {
3027 std::string subdir_path = "subdir";
3028
3030 fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
3031 asset_dir.path().c_str(), false, fml::FilePermission::kRead);
3032 fml::UniqueFD subdir_fd =
3033 fml::OpenDirectory((asset_dir.path() + "/" + subdir_path).c_str(), true,
3035
3036 std::vector<std::string> filenames = {
3037 "bad0",
3038 "notgood", // this is to make sure the pattern (.*)good(.*) only
3039 // matches things in the subdirectory
3040 };
3041
3042 std::vector<std::string> subdir_filenames = {
3043 "good0",
3044 "good1",
3045 "bad1",
3046 };
3047
3048 for (auto filename : filenames) {
3049 bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
3050 fml::DataMapping(filename));
3051 ASSERT_TRUE(success);
3052 }
3053
3054 for (auto filename : subdir_filenames) {
3055 bool success = fml::WriteAtomically(subdir_fd, filename.c_str(),
3056 fml::DataMapping(filename));
3057 ASSERT_TRUE(success);
3058 }
3059
3060 AssetManager asset_manager;
3061 asset_manager.PushBack(
3062 std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
3063
3064 auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
3065 EXPECT_EQ(mappings.size(), 5u);
3066
3067 mappings = asset_manager.GetAsMappings("(.*)", subdir_path);
3068 EXPECT_EQ(mappings.size(), 3u);
3069
3070 std::vector<std::string> expected_results = {
3071 "good0",
3072 "good1",
3073 };
3074
3075 mappings = asset_manager.GetAsMappings("(.*)good(.*)", subdir_path);
3076 ASSERT_EQ(mappings.size(), expected_results.size());
3077
3078 for (auto& mapping : mappings) {
3079 std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
3080 mapping->GetSize());
3081 ASSERT_NE(
3082 std::find(expected_results.begin(), expected_results.end(), result),
3083 expected_results.end());
3084 }
3085}
3086#endif // OS_FUCHSIA
3087
3089 auto settings = CreateSettingsForFixture();
3090 auto shell = CreateShell(settings);
3091 ASSERT_TRUE(ValidateShell(shell.get()));
3092
3093 auto configuration = RunConfiguration::InferFromSettings(settings);
3094 ASSERT_TRUE(configuration.IsValid());
3095 configuration.SetEntrypoint("fixturesAreFunctionalMain");
3096
3097 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3098 ASSERT_TRUE(second_configuration.IsValid());
3099 second_configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
3100
3101 const std::string initial_route("/foo");
3102
3103 fml::AutoResetWaitableEvent main_latch;
3104 std::string last_entry_point;
3105 // Fulfill native function for the first Shell's entrypoint.
3106 AddNativeCallback(
3107 "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
3108 last_entry_point = shell->GetEngine()->GetLastEntrypoint();
3109 main_latch.Signal();
3110 }));
3111 // Fulfill native function for the second Shell's entrypoint.
3112 fml::CountDownLatch second_latch(2);
3113 AddNativeCallback(
3114 // The Dart native function names aren't very consistent but this is
3115 // just the native function name of the second vm entrypoint in the
3116 // fixture.
3117 "NotifyNative",
3118 CREATE_NATIVE_ENTRY([&](auto args) { second_latch.CountDown(); }));
3119
3120 RunEngine(shell.get(), std::move(configuration));
3121 main_latch.Wait();
3122 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3123 // Check first Shell ran the first entrypoint.
3124 ASSERT_EQ("fixturesAreFunctionalMain", last_entry_point);
3125
3126 PostSync(
3127 shell->GetTaskRunners().GetPlatformTaskRunner(),
3128 [this, &spawner = shell, &second_configuration, &second_latch,
3129 initial_route]() {
3130 MockPlatformViewDelegate platform_view_delegate;
3131 auto spawn = spawner->Spawn(
3132 std::move(second_configuration), initial_route,
3133 [&platform_view_delegate](Shell& shell) {
3134 auto result = std::make_unique<MockPlatformView>(
3135 platform_view_delegate, shell.GetTaskRunners());
3136 ON_CALL(*result, CreateRenderingSurface())
3137 .WillByDefault(::testing::Invoke(
3138 [] { return std::make_unique<MockSurface>(); }));
3139 return result;
3140 },
3141 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3142 ASSERT_NE(nullptr, spawn.get());
3143 ASSERT_TRUE(ValidateShell(spawn.get()));
3144
3145 PostSync(spawner->GetTaskRunners().GetUITaskRunner(), [&spawn, &spawner,
3146 initial_route] {
3147 // Check second shell ran the second entrypoint.
3148 ASSERT_EQ("testCanLaunchSecondaryIsolate",
3149 spawn->GetEngine()->GetLastEntrypoint());
3150 ASSERT_EQ(initial_route, spawn->GetEngine()->InitialRoute());
3151
3152 ASSERT_NE(spawner->GetEngine()
3153 ->GetRuntimeController()
3154 ->GetRootIsolateGroup(),
3155 0u);
3156 ASSERT_EQ(spawner->GetEngine()
3157 ->GetRuntimeController()
3158 ->GetRootIsolateGroup(),
3159 spawn->GetEngine()
3160 ->GetRuntimeController()
3161 ->GetRootIsolateGroup());
3162 auto spawner_snapshot_delegate = spawner->GetEngine()
3163 ->GetRuntimeController()
3164 ->GetSnapshotDelegate();
3165 auto spawn_snapshot_delegate =
3166 spawn->GetEngine()->GetRuntimeController()->GetSnapshotDelegate();
3167 PostSync(spawner->GetTaskRunners().GetRasterTaskRunner(),
3168 [spawner_snapshot_delegate, spawn_snapshot_delegate] {
3169 ASSERT_NE(spawner_snapshot_delegate.get(),
3170 spawn_snapshot_delegate.get());
3171 });
3172 });
3173 PostSync(
3174 spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3175 ASSERT_EQ(spawner->GetIOManager()->GetResourceContext().get(),
3176 spawn->GetIOManager()->GetResourceContext().get());
3177 });
3178
3179 // Before destroying the shell, wait for expectations of the spawned
3180 // isolate to be met.
3181 second_latch.Wait();
3182
3183 DestroyShell(std::move(spawn));
3184 });
3185
3186 DestroyShell(std::move(shell));
3187 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3188}
3189
3190TEST_F(ShellTest, SpawnWithDartEntrypointArgs) {
3191 auto settings = CreateSettingsForFixture();
3192 auto shell = CreateShell(settings);
3193 ASSERT_TRUE(ValidateShell(shell.get()));
3194
3195 auto configuration = RunConfiguration::InferFromSettings(settings);
3196 ASSERT_TRUE(configuration.IsValid());
3197 configuration.SetEntrypoint("canReceiveArgumentsWhenEngineRun");
3198 const std::vector<std::string> entrypoint_args{"foo", "bar"};
3199 configuration.SetEntrypointArgs(entrypoint_args);
3200
3201 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3202 ASSERT_TRUE(second_configuration.IsValid());
3203 second_configuration.SetEntrypoint("canReceiveArgumentsWhenEngineSpawn");
3204 const std::vector<std::string> second_entrypoint_args{"arg1", "arg2"};
3205 second_configuration.SetEntrypointArgs(second_entrypoint_args);
3206
3207 const std::string initial_route("/foo");
3208
3209 fml::AutoResetWaitableEvent main_latch;
3210 std::string last_entry_point;
3211 // Fulfill native function for the first Shell's entrypoint.
3212 AddNativeCallback("NotifyNativeWhenEngineRun",
3213 CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) {
3215 Dart_GetNativeArgument(args, 0)));
3216 last_entry_point =
3217 shell->GetEngine()->GetLastEntrypoint();
3218 main_latch.Signal();
3219 })));
3220
3221 fml::AutoResetWaitableEvent second_latch;
3222 // Fulfill native function for the second Shell's entrypoint.
3223 AddNativeCallback("NotifyNativeWhenEngineSpawn",
3224 CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) {
3226 Dart_GetNativeArgument(args, 0)));
3227 last_entry_point =
3228 shell->GetEngine()->GetLastEntrypoint();
3229 second_latch.Signal();
3230 })));
3231
3232 RunEngine(shell.get(), std::move(configuration));
3233 main_latch.Wait();
3234 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3235 // Check first Shell ran the first entrypoint.
3236 ASSERT_EQ("canReceiveArgumentsWhenEngineRun", last_entry_point);
3237
3238 PostSync(
3239 shell->GetTaskRunners().GetPlatformTaskRunner(),
3240 [this, &spawner = shell, &second_configuration, &second_latch,
3241 initial_route]() {
3242 MockPlatformViewDelegate platform_view_delegate;
3243 auto spawn = spawner->Spawn(
3244 std::move(second_configuration), initial_route,
3245 [&platform_view_delegate](Shell& shell) {
3246 auto result = std::make_unique<MockPlatformView>(
3247 platform_view_delegate, shell.GetTaskRunners());
3248 ON_CALL(*result, CreateRenderingSurface())
3249 .WillByDefault(::testing::Invoke(
3250 [] { return std::make_unique<MockSurface>(); }));
3251 return result;
3252 },
3253 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3254 ASSERT_NE(nullptr, spawn.get());
3255 ASSERT_TRUE(ValidateShell(spawn.get()));
3256
3257 PostSync(spawner->GetTaskRunners().GetUITaskRunner(),
3258 [&spawn, &spawner, initial_route] {
3259 // Check second shell ran the second entrypoint.
3260 ASSERT_EQ("canReceiveArgumentsWhenEngineSpawn",
3261 spawn->GetEngine()->GetLastEntrypoint());
3262 ASSERT_EQ(initial_route, spawn->GetEngine()->InitialRoute());
3263
3264 ASSERT_NE(spawner->GetEngine()
3265 ->GetRuntimeController()
3266 ->GetRootIsolateGroup(),
3267 0u);
3268 ASSERT_EQ(spawner->GetEngine()
3269 ->GetRuntimeController()
3270 ->GetRootIsolateGroup(),
3271 spawn->GetEngine()
3272 ->GetRuntimeController()
3273 ->GetRootIsolateGroup());
3274 });
3275
3276 PostSync(
3277 spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3278 ASSERT_EQ(spawner->GetIOManager()->GetResourceContext().get(),
3279 spawn->GetIOManager()->GetResourceContext().get());
3280 });
3281
3282 // Before destroying the shell, wait for expectations of the spawned
3283 // isolate to be met.
3284 second_latch.Wait();
3285
3286 DestroyShell(std::move(spawn));
3287 });
3288
3289 DestroyShell(std::move(shell));
3290 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3291}
3292
3293TEST_F(ShellTest, IOManagerIsSharedBetweenParentAndSpawnedShell) {
3294 auto settings = CreateSettingsForFixture();
3295 auto shell = CreateShell(settings);
3296 ASSERT_TRUE(ValidateShell(shell.get()));
3297
3298 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [this,
3299 &spawner = shell,
3300 &settings] {
3301 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3302 ASSERT_TRUE(second_configuration.IsValid());
3303 second_configuration.SetEntrypoint("emptyMain");
3304 const std::string initial_route("/foo");
3305 MockPlatformViewDelegate platform_view_delegate;
3306 auto spawn = spawner->Spawn(
3307 std::move(second_configuration), initial_route,
3308 [&platform_view_delegate](Shell& shell) {
3309 auto result = std::make_unique<MockPlatformView>(
3310 platform_view_delegate, shell.GetTaskRunners());
3311 ON_CALL(*result, CreateRenderingSurface())
3312 .WillByDefault(::testing::Invoke(
3313 [] { return std::make_unique<MockSurface>(); }));
3314 return result;
3315 },
3316 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3317 ASSERT_TRUE(ValidateShell(spawn.get()));
3318
3319 PostSync(spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
3320 ASSERT_NE(spawner->GetIOManager().get(), nullptr);
3321 ASSERT_EQ(spawner->GetIOManager().get(), spawn->GetIOManager().get());
3322 });
3323
3324 // Destroy the child shell.
3325 DestroyShell(std::move(spawn));
3326 });
3327 // Destroy the parent shell.
3328 DestroyShell(std::move(shell));
3329}
3330
3331TEST_F(ShellTest, IOManagerInSpawnedShellIsNotNullAfterParentShellDestroyed) {
3332 auto settings = CreateSettingsForFixture();
3333 auto shell = CreateShell(settings);
3334 ASSERT_TRUE(ValidateShell(shell.get()));
3335
3336 PostSync(shell->GetTaskRunners().GetUITaskRunner(), [&shell] {
3337 // We must get engine on UI thread.
3338 auto runtime_controller = shell->GetEngine()->GetRuntimeController();
3339 PostSync(shell->GetTaskRunners().GetIOTaskRunner(),
3340 [&shell, &runtime_controller] {
3341 // We must get io_manager on IO thread.
3342 auto io_manager = runtime_controller->GetIOManager();
3343 // Check io_manager existence.
3344 ASSERT_NE(io_manager.get(), nullptr);
3345 ASSERT_NE(io_manager->GetSkiaUnrefQueue().get(), nullptr);
3346 // Get io_manager directly from shell and check its existence.
3347 ASSERT_NE(shell->GetIOManager().get(), nullptr);
3348 });
3349 });
3350
3351 std::unique_ptr<Shell> spawn;
3352
3353 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &settings,
3354 &spawn] {
3355 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3356 ASSERT_TRUE(second_configuration.IsValid());
3357 second_configuration.SetEntrypoint("emptyMain");
3358 const std::string initial_route("/foo");
3359 MockPlatformViewDelegate platform_view_delegate;
3360 auto child = shell->Spawn(
3361 std::move(second_configuration), initial_route,
3362 [&platform_view_delegate](Shell& shell) {
3363 auto result = std::make_unique<MockPlatformView>(
3364 platform_view_delegate, shell.GetTaskRunners());
3365 ON_CALL(*result, CreateRenderingSurface())
3366 .WillByDefault(::testing::Invoke(
3367 [] { return std::make_unique<MockSurface>(); }));
3368 return result;
3369 },
3370 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3371 spawn = std::move(child);
3372 });
3373 // Destroy the parent shell.
3374 DestroyShell(std::move(shell));
3375
3376 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3377 // We must get engine on UI thread.
3378 auto runtime_controller = spawn->GetEngine()->GetRuntimeController();
3379 PostSync(spawn->GetTaskRunners().GetIOTaskRunner(),
3380 [&spawn, &runtime_controller] {
3381 // We must get io_manager on IO thread.
3382 auto io_manager = runtime_controller->GetIOManager();
3383 // Check io_manager existence here.
3384 ASSERT_NE(io_manager.get(), nullptr);
3385 ASSERT_NE(io_manager->GetSkiaUnrefQueue().get(), nullptr);
3386 // Get io_manager directly from shell and check its existence.
3387 ASSERT_NE(spawn->GetIOManager().get(), nullptr);
3388 });
3389 });
3390 // Destroy the child shell.
3391 DestroyShell(std::move(spawn));
3392}
3393
3394TEST_F(ShellTest, ImageGeneratorRegistryNotNullAfterParentShellDestroyed) {
3395 auto settings = CreateSettingsForFixture();
3396 auto shell = CreateShell(settings);
3397 ASSERT_TRUE(ValidateShell(shell.get()));
3398
3399 std::unique_ptr<Shell> spawn;
3400
3401 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &settings,
3402 &spawn] {
3403 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3404 ASSERT_TRUE(second_configuration.IsValid());
3405 second_configuration.SetEntrypoint("emptyMain");
3406 const std::string initial_route("/foo");
3407 MockPlatformViewDelegate platform_view_delegate;
3408 auto child = shell->Spawn(
3409 std::move(second_configuration), initial_route,
3410 [&platform_view_delegate](Shell& shell) {
3411 auto result = std::make_unique<MockPlatformView>(
3412 platform_view_delegate, shell.GetTaskRunners());
3413 ON_CALL(*result, CreateRenderingSurface())
3414 .WillByDefault(::testing::Invoke(
3415 [] { return std::make_unique<MockSurface>(); }));
3416 return result;
3417 },
3418 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3419 spawn = std::move(child);
3420 });
3421
3422 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3423 std::shared_ptr<const DartIsolate> isolate =
3424 spawn->GetEngine()->GetRuntimeController()->GetRootIsolate().lock();
3425 ASSERT_TRUE(isolate);
3426 ASSERT_TRUE(isolate->GetImageGeneratorRegistry());
3427 });
3428
3429 // Destroy the parent shell.
3430 DestroyShell(std::move(shell));
3431
3432 PostSync(spawn->GetTaskRunners().GetUITaskRunner(), [&spawn] {
3433 std::shared_ptr<const DartIsolate> isolate =
3434 spawn->GetEngine()->GetRuntimeController()->GetRootIsolate().lock();
3435 ASSERT_TRUE(isolate);
3436 ASSERT_TRUE(isolate->GetImageGeneratorRegistry());
3437 });
3438 // Destroy the child shell.
3439 DestroyShell(std::move(spawn));
3440}
3441
3442TEST_F(ShellTest, UpdateAssetResolverByTypeReplaces) {
3443 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3444 Settings settings = CreateSettingsForFixture();
3445
3447 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3448 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3449 task_runner);
3450 auto shell = CreateShell(settings, task_runners);
3451 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3452 ASSERT_TRUE(ValidateShell(shell.get()));
3453
3454 auto configuration = RunConfiguration::InferFromSettings(settings);
3455 configuration.SetEntrypoint("emptyMain");
3456 auto asset_manager = configuration.GetAssetManager();
3457
3458 shell->RunEngine(std::move(configuration), [&](auto result) {
3459 ASSERT_EQ(result, Engine::RunStatus::Success);
3460 });
3461
3462 auto platform_view =
3463 std::make_unique<PlatformView>(*shell.get(), task_runners);
3464
3465 auto old_resolver = std::make_unique<TestAssetResolver>(
3466 true, AssetResolver::AssetResolverType::kApkAssetProvider);
3467 ASSERT_TRUE(old_resolver->IsValid());
3468 asset_manager->PushBack(std::move(old_resolver));
3469
3470 auto updated_resolver = std::make_unique<TestAssetResolver>(
3471 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3472 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3473 platform_view->UpdateAssetResolverByType(
3474 std::move(updated_resolver),
3475 AssetResolver::AssetResolverType::kApkAssetProvider);
3476
3477 auto resolvers = asset_manager->TakeResolvers();
3478 ASSERT_EQ(resolvers.size(), 2ull);
3479 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3480
3481 ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
3482
3483 DestroyShell(std::move(shell), task_runners);
3484 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3485}
3486
3487TEST_F(ShellTest, UpdateAssetResolverByTypeAppends) {
3488 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3489 Settings settings = CreateSettingsForFixture();
3490
3492 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3493 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3494 task_runner);
3495 auto shell = CreateShell(settings, task_runners);
3496 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3497 ASSERT_TRUE(ValidateShell(shell.get()));
3498
3499 auto configuration = RunConfiguration::InferFromSettings(settings);
3500 configuration.SetEntrypoint("emptyMain");
3501 auto asset_manager = configuration.GetAssetManager();
3502
3503 shell->RunEngine(std::move(configuration), [&](auto result) {
3504 ASSERT_EQ(result, Engine::RunStatus::Success);
3505 });
3506
3507 auto platform_view =
3508 std::make_unique<PlatformView>(*shell.get(), task_runners);
3509
3510 auto updated_resolver = std::make_unique<TestAssetResolver>(
3511 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3512 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3513 platform_view->UpdateAssetResolverByType(
3514 std::move(updated_resolver),
3515 AssetResolver::AssetResolverType::kApkAssetProvider);
3516
3517 auto resolvers = asset_manager->TakeResolvers();
3518 ASSERT_EQ(resolvers.size(), 2ull);
3519 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3520
3521 ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
3522
3523 DestroyShell(std::move(shell), task_runners);
3524 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3525}
3526
3527TEST_F(ShellTest, UpdateAssetResolverByTypeNull) {
3528 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3529 Settings settings = CreateSettingsForFixture();
3531 "io.flutter.test." + GetCurrentTestName() + ".",
3532 ThreadHost::Type::kPlatform));
3533 auto task_runner = thread_host.platform_thread->GetTaskRunner();
3534 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3535 task_runner);
3536 auto shell = CreateShell(settings, task_runners);
3537 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3538 ASSERT_TRUE(ValidateShell(shell.get()));
3539
3540 auto configuration = RunConfiguration::InferFromSettings(settings);
3541 configuration.SetEntrypoint("emptyMain");
3542 auto asset_manager = configuration.GetAssetManager();
3543 RunEngine(shell.get(), std::move(configuration));
3544
3545 auto platform_view =
3546 std::make_unique<PlatformView>(*shell.get(), task_runners);
3547
3548 auto old_resolver = std::make_unique<TestAssetResolver>(
3549 true, AssetResolver::AssetResolverType::kApkAssetProvider);
3550 ASSERT_TRUE(old_resolver->IsValid());
3551 asset_manager->PushBack(std::move(old_resolver));
3552
3553 platform_view->UpdateAssetResolverByType(
3554 nullptr, AssetResolver::AssetResolverType::kApkAssetProvider);
3555
3556 auto resolvers = asset_manager->TakeResolvers();
3557 ASSERT_EQ(resolvers.size(), 2ull);
3558 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3559 ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
3560
3561 DestroyShell(std::move(shell), task_runners);
3562 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3563}
3564
3565TEST_F(ShellTest, UpdateAssetResolverByTypeDoesNotReplaceMismatchType) {
3566 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3567 Settings settings = CreateSettingsForFixture();
3568
3570 auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
3571 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3572 task_runner);
3573 auto shell = CreateShell(settings, task_runners);
3574 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3575 ASSERT_TRUE(ValidateShell(shell.get()));
3576
3577 auto configuration = RunConfiguration::InferFromSettings(settings);
3578 configuration.SetEntrypoint("emptyMain");
3579 auto asset_manager = configuration.GetAssetManager();
3580
3581 shell->RunEngine(std::move(configuration), [&](auto result) {
3582 ASSERT_EQ(result, Engine::RunStatus::Success);
3583 });
3584
3585 auto platform_view =
3586 std::make_unique<PlatformView>(*shell.get(), task_runners);
3587
3588 auto old_resolver = std::make_unique<TestAssetResolver>(
3589 true, AssetResolver::AssetResolverType::kAssetManager);
3590 ASSERT_TRUE(old_resolver->IsValid());
3591 asset_manager->PushBack(std::move(old_resolver));
3592
3593 auto updated_resolver = std::make_unique<TestAssetResolver>(
3594 false, AssetResolver::AssetResolverType::kApkAssetProvider);
3595 ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
3596 platform_view->UpdateAssetResolverByType(
3597 std::move(updated_resolver),
3598 AssetResolver::AssetResolverType::kApkAssetProvider);
3599
3600 auto resolvers = asset_manager->TakeResolvers();
3601 ASSERT_EQ(resolvers.size(), 3ull);
3602 ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
3603
3604 ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
3605
3606 ASSERT_FALSE(resolvers[2]->IsValidAfterAssetManagerChange());
3607
3608 DestroyShell(std::move(shell), task_runners);
3609 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3610}
3611
3612TEST_F(ShellTest, CanCreateShellsWithGLBackend) {
3613#if !SHELL_ENABLE_GL
3614 // GL emulation does not exist on Fuchsia.
3615 GTEST_SKIP();
3616#else
3617 auto settings = CreateSettingsForFixture();
3618 std::unique_ptr<Shell> shell = CreateShell({
3619 .settings = settings,
3620 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3621 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
3622 }),
3623 });
3624 ASSERT_NE(shell, nullptr);
3625 ASSERT_TRUE(shell->IsSetup());
3626 auto configuration = RunConfiguration::InferFromSettings(settings);
3627 PlatformViewNotifyCreated(shell.get());
3628 configuration.SetEntrypoint("emptyMain");
3629 RunEngine(shell.get(), std::move(configuration));
3630 PumpOneFrame(shell.get());
3631 PlatformViewNotifyDestroyed(shell.get());
3632 DestroyShell(std::move(shell));
3633#endif // !SHELL_ENABLE_GL
3634}
3635
3636TEST_F(ShellTest, CanCreateShellsWithVulkanBackend) {
3637#if !SHELL_ENABLE_VULKAN
3638 GTEST_SKIP();
3639#else
3640 auto settings = CreateSettingsForFixture();
3641 std::unique_ptr<Shell> shell = CreateShell({
3642 .settings = settings,
3643 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3644 .rendering_backend =
3645 ShellTestPlatformView::BackendType::kVulkanBackend,
3646 }),
3647 });
3648 ASSERT_NE(shell, nullptr);
3649 ASSERT_TRUE(shell->IsSetup());
3650 auto configuration = RunConfiguration::InferFromSettings(settings);
3651 PlatformViewNotifyCreated(shell.get());
3652 configuration.SetEntrypoint("emptyMain");
3653 RunEngine(shell.get(), std::move(configuration));
3654 PumpOneFrame(shell.get());
3655 PlatformViewNotifyDestroyed(shell.get());
3656 DestroyShell(std::move(shell));
3657#endif // !SHELL_ENABLE_VULKAN
3658}
3659
3660TEST_F(ShellTest, CanCreateShellsWithMetalBackend) {
3661#if !SHELL_ENABLE_METAL
3662 GTEST_SKIP();
3663#else
3664 auto settings = CreateSettingsForFixture();
3665 std::unique_ptr<Shell> shell = CreateShell({
3666 .settings = settings,
3667 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3668 .rendering_backend =
3669 ShellTestPlatformView::BackendType::kMetalBackend,
3670 }),
3671 });
3672 ASSERT_NE(shell, nullptr);
3673 ASSERT_TRUE(shell->IsSetup());
3674 auto configuration = RunConfiguration::InferFromSettings(settings);
3675 PlatformViewNotifyCreated(shell.get());
3676 configuration.SetEntrypoint("emptyMain");
3677 RunEngine(shell.get(), std::move(configuration));
3678 PumpOneFrame(shell.get());
3679 PlatformViewNotifyDestroyed(shell.get());
3680 DestroyShell(std::move(shell));
3681#endif // !SHELL_ENABLE_METAL
3682}
3683
3684TEST_F(ShellTest, UserTagSetOnStartup) {
3685 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3686 // Make sure the shell launch does not kick off the creation of the VM
3687 // instance by already creating one upfront.
3688 auto vm_settings = CreateSettingsForFixture();
3689 auto vm_ref = DartVMRef::Create(vm_settings);
3690 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3691
3692 auto settings = vm_settings;
3693 fml::AutoResetWaitableEvent isolate_create_latch;
3694
3695 // ensure that "AppStartUpTag" is set during isolate creation.
3696 settings.root_isolate_create_callback = [&](const DartIsolate& isolate) {
3697 Dart_Handle current_tag = Dart_GetCurrentUserTag();
3698 Dart_Handle startup_tag = Dart_NewUserTag("AppStartUp");
3699 EXPECT_TRUE(Dart_IdentityEquals(current_tag, startup_tag));
3700
3701 isolate_create_latch.Signal();
3702 };
3703
3704 auto shell = CreateShell(settings);
3705 ASSERT_TRUE(ValidateShell(shell.get()));
3706
3707 auto configuration = RunConfiguration::InferFromSettings(settings);
3708 ASSERT_TRUE(configuration.IsValid());
3709
3710 RunEngine(shell.get(), std::move(configuration));
3711 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3712
3713 DestroyShell(std::move(shell));
3714 isolate_create_latch.Wait();
3715}
3716
3717TEST_F(ShellTest, PrefetchDefaultFontManager) {
3718 auto settings = CreateSettingsForFixture();
3719 settings.prefetched_default_font_manager = true;
3720 std::unique_ptr<Shell> shell;
3721
3722 auto get_font_manager_count = [&] {
3724 size_t font_manager_count;
3726 shell->GetTaskRunners().GetUITaskRunner(),
3727 [this, &shell, &latch, &font_manager_count]() {
3728 font_manager_count =
3729 GetFontCollection(shell.get())->GetFontManagersCount();
3730 latch.Signal();
3731 });
3732 latch.Wait();
3733 return font_manager_count;
3734 };
3735 size_t initial_font_manager_count = 0;
3736 settings.root_isolate_create_callback = [&](const auto& isolate) {
3737 ASSERT_GT(initial_font_manager_count, 0ul);
3738 // Should not have fetched the default font manager yet, since the root
3739 // isolate was only just created.
3740 ASSERT_EQ(get_font_manager_count(), initial_font_manager_count);
3741 };
3742
3743 shell = CreateShell(settings);
3744
3745 initial_font_manager_count = get_font_manager_count();
3746
3747 auto configuration = RunConfiguration::InferFromSettings(settings);
3748 configuration.SetEntrypoint("emptyMain");
3749 RunEngine(shell.get(), std::move(configuration));
3750
3751 // If the prefetched_default_font_manager flag is set, then the default font
3752 // manager will not be added until the engine starts running.
3753 ASSERT_EQ(get_font_manager_count(), initial_font_manager_count + 1);
3754
3755 DestroyShell(std::move(shell));
3756}
3757
3758TEST_F(ShellTest, OnPlatformViewCreatedWhenUIThreadIsBusy) {
3759 // This test will deadlock if the threading logic in
3760 // Shell::OnCreatePlatformView is wrong.
3761 auto settings = CreateSettingsForFixture();
3762 auto shell = CreateShell(settings);
3763
3765 fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetUITaskRunner(),
3766 [&latch]() { latch.Wait(); });
3767
3768 ShellTest::PlatformViewNotifyCreated(shell.get());
3769 latch.Signal();
3770
3771 DestroyShell(std::move(shell));
3772}
3773
3774TEST_F(ShellTest, UIWorkAfterOnPlatformViewDestroyed) {
3775 auto settings = CreateSettingsForFixture();
3776 auto shell = CreateShell(settings);
3777 auto configuration = RunConfiguration::InferFromSettings(settings);
3778 configuration.SetEntrypoint("drawFrames");
3779
3781 fml::AutoResetWaitableEvent notify_native_latch;
3782 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
3783 notify_native_latch.Signal();
3784 latch.Wait();
3785 }));
3786
3787 RunEngine(shell.get(), std::move(configuration));
3788 // Wait to make sure we get called back from Dart and thus have latched
3789 // the UI thread before we create/destroy the platform view.
3790 notify_native_latch.Wait();
3791
3792 ShellTest::PlatformViewNotifyCreated(shell.get());
3793
3794 fml::AutoResetWaitableEvent destroy_latch;
3796 shell->GetTaskRunners().GetPlatformTaskRunner(),
3797 [&shell, &destroy_latch]() {
3798 shell->GetPlatformView()->NotifyDestroyed();
3799 destroy_latch.Signal();
3800 });
3801
3802 destroy_latch.Wait();
3803
3804 // Unlatch the UI thread and let it send us a scene to render.
3805 latch.Signal();
3806
3807 // Flush the UI task runner to make sure we process the render/scheduleFrame
3808 // request.
3809 fml::AutoResetWaitableEvent ui_flush_latch;
3811 shell->GetTaskRunners().GetUITaskRunner(),
3812 [&ui_flush_latch]() { ui_flush_latch.Signal(); });
3813 ui_flush_latch.Wait();
3814 DestroyShell(std::move(shell));
3815}
3816
3817TEST_F(ShellTest, UsesPlatformMessageHandler) {
3818 TaskRunners task_runners = GetTaskRunnersForFixture();
3819 auto settings = CreateSettingsForFixture();
3820 MockPlatformViewDelegate platform_view_delegate;
3821 auto platform_message_handler =
3822 std::make_shared<MockPlatformMessageHandler>();
3823 int message_id = 1;
3824 EXPECT_CALL(*platform_message_handler, HandlePlatformMessage(_));
3825 EXPECT_CALL(*platform_message_handler,
3826 InvokePlatformMessageEmptyResponseCallback(message_id));
3827 Shell::CreateCallback<PlatformView> platform_view_create_callback =
3828 [&platform_view_delegate, task_runners,
3829 platform_message_handler](flutter::Shell& shell) {
3830 auto result = std::make_unique<MockPlatformView>(platform_view_delegate,
3831 task_runners);
3832 EXPECT_CALL(*result, GetPlatformMessageHandler())
3833 .WillOnce(Return(platform_message_handler));
3834 return result;
3835 };
3836 auto shell = CreateShell({
3837 .settings = settings,
3838 .task_runners = task_runners,
3839 .platform_view_create_callback = platform_view_create_callback,
3840 });
3841
3842 EXPECT_EQ(platform_message_handler, shell->GetPlatformMessageHandler());
3843 PostSync(task_runners.GetUITaskRunner(), [&shell]() {
3844 size_t data_size = 4;
3845 fml::MallocMapping bytes =
3846 fml::MallocMapping(static_cast<uint8_t*>(malloc(data_size)), data_size);
3847 fml::RefPtr<MockPlatformMessageResponse> response =
3848 MockPlatformMessageResponse::Create();
3849 auto message = std::make_unique<PlatformMessage>(
3850 /*channel=*/"foo", /*data=*/std::move(bytes), /*response=*/response);
3851 (static_cast<Engine::Delegate*>(shell.get()))
3852 ->OnEngineHandlePlatformMessage(std::move(message));
3853 });
3854 shell->GetPlatformMessageHandler()
3855 ->InvokePlatformMessageEmptyResponseCallback(message_id);
3856 DestroyShell(std::move(shell));
3857}
3858
3859TEST_F(ShellTest, SpawnWorksWithOnError) {
3860 auto settings = CreateSettingsForFixture();
3861 auto shell = CreateShell(settings);
3862 ASSERT_TRUE(ValidateShell(shell.get()));
3863
3864 auto configuration = RunConfiguration::InferFromSettings(settings);
3865 ASSERT_TRUE(configuration.IsValid());
3866 configuration.SetEntrypoint("onErrorA");
3867
3868 auto second_configuration = RunConfiguration::InferFromSettings(settings);
3869 ASSERT_TRUE(second_configuration.IsValid());
3870 second_configuration.SetEntrypoint("onErrorB");
3871
3872 fml::CountDownLatch latch(2);
3873
3874 AddNativeCallback(
3875 "NotifyErrorA", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3876 auto string_handle = Dart_GetNativeArgument(args, 0);
3877 const char* c_str;
3878 Dart_StringToCString(string_handle, &c_str);
3879 EXPECT_STREQ(c_str, "Exception: I should be coming from A");
3880 latch.CountDown();
3881 }));
3882
3883 AddNativeCallback(
3884 "NotifyErrorB", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3885 auto string_handle = Dart_GetNativeArgument(args, 0);
3886 const char* c_str;
3887 Dart_StringToCString(string_handle, &c_str);
3888 EXPECT_STREQ(c_str, "Exception: I should be coming from B");
3889 latch.CountDown();
3890 }));
3891
3892 RunEngine(shell.get(), std::move(configuration));
3893
3894 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3895
3896 PostSync(
3897 shell->GetTaskRunners().GetPlatformTaskRunner(),
3898 [this, &spawner = shell, &second_configuration, &latch]() {
3899 ::testing::NiceMock<MockPlatformViewDelegate> platform_view_delegate;
3900 auto spawn = spawner->Spawn(
3901 std::move(second_configuration), "",
3902 [&platform_view_delegate](Shell& shell) {
3903 auto result =
3904 std::make_unique<::testing::NiceMock<MockPlatformView>>(
3905 platform_view_delegate, shell.GetTaskRunners());
3906 ON_CALL(*result, CreateRenderingSurface())
3907 .WillByDefault(::testing::Invoke([] {
3908 return std::make_unique<::testing::NiceMock<MockSurface>>();
3909 }));
3910 return result;
3911 },
3912 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
3913 ASSERT_NE(nullptr, spawn.get());
3914 ASSERT_TRUE(ValidateShell(spawn.get()));
3915
3916 // Before destroying the shell, wait for expectations of the spawned
3917 // isolate to be met.
3918 latch.Wait();
3919
3920 DestroyShell(std::move(spawn));
3921 });
3922
3923 DestroyShell(std::move(shell));
3924 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3925}
3926
3927TEST_F(ShellTest, ImmutableBufferLoadsAssetOnBackgroundThread) {
3928 Settings settings = CreateSettingsForFixture();
3929 auto task_runner = CreateNewThread();
3930 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
3931 task_runner);
3932 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
3933
3934 fml::CountDownLatch latch(1);
3935 AddNativeCallback("NotifyNative",
3936 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
3937
3938 // Create the surface needed by rasterizer
3939 PlatformViewNotifyCreated(shell.get());
3940
3941 auto configuration = RunConfiguration::InferFromSettings(settings);
3942 configuration.SetEntrypoint("testThatAssetLoadingHappensOnWorkerThread");
3943 auto asset_manager = configuration.GetAssetManager();
3944 auto test_resolver = std::make_unique<ThreadCheckingAssetResolver>(
3945 shell->GetDartVM()->GetConcurrentMessageLoop());
3946 auto leaked_resolver = test_resolver.get();
3947 asset_manager->PushBack(std::move(test_resolver));
3948
3949 RunEngine(shell.get(), std::move(configuration));
3950 PumpOneFrame(shell.get());
3951
3952 latch.Wait();
3953
3954 EXPECT_EQ(leaked_resolver->mapping_requests[0], "DoesNotExist");
3955
3956 PlatformViewNotifyDestroyed(shell.get());
3957 DestroyShell(std::move(shell), task_runners);
3958}
3959
3960TEST_F(ShellTest, PictureToImageSync) {
3961#if !SHELL_ENABLE_GL
3962 // This test uses the GL backend.
3963 GTEST_SKIP();
3964#else
3965 auto settings = CreateSettingsForFixture();
3966 std::unique_ptr<Shell> shell = CreateShell({
3967 .settings = settings,
3968 .platform_view_create_callback = ShellTestPlatformViewBuilder({
3969 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
3970 }),
3971 });
3972
3973 AddNativeCallback("NativeOnBeforeToImageSync",
3974 CREATE_NATIVE_ENTRY([&](auto args) {
3975 // nop
3976 }));
3977
3978 fml::CountDownLatch latch(2);
3979 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
3980 // Teardown and set up rasterizer again.
3981 PlatformViewNotifyDestroyed(shell.get());
3982 PlatformViewNotifyCreated(shell.get());
3983 latch.CountDown();
3984 }));
3985
3986 ASSERT_NE(shell, nullptr);
3987 ASSERT_TRUE(shell->IsSetup());
3988 auto configuration = RunConfiguration::InferFromSettings(settings);
3989 PlatformViewNotifyCreated(shell.get());
3990 configuration.SetEntrypoint("toImageSync");
3991 RunEngine(shell.get(), std::move(configuration));
3992 PumpOneFrame(shell.get());
3993
3994 latch.Wait();
3995
3996 PlatformViewNotifyDestroyed(shell.get());
3997 DestroyShell(std::move(shell));
3998#endif // !SHELL_ENABLE_GL
3999}
4000
4001TEST_F(ShellTest, PictureToImageSyncImpellerNoSurface) {
4002#if !SHELL_ENABLE_METAL
4003 // This test uses the Metal backend.
4004 GTEST_SKIP();
4005#else
4006 auto settings = CreateSettingsForFixture();
4007 settings.enable_impeller = true;
4008 std::unique_ptr<Shell> shell = CreateShell({
4009 .settings = settings,
4010 .platform_view_create_callback = ShellTestPlatformViewBuilder({
4011 .rendering_backend =
4012 ShellTestPlatformView::BackendType::kMetalBackend,
4013 }),
4014 });
4015
4016 AddNativeCallback("NativeOnBeforeToImageSync",
4017 CREATE_NATIVE_ENTRY([&](auto args) {
4018 // nop
4019 }));
4020
4021 fml::CountDownLatch latch(2);
4022 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4023 // Teardown and set up rasterizer again.
4024 PlatformViewNotifyDestroyed(shell.get());
4025 PlatformViewNotifyCreated(shell.get());
4026 latch.CountDown();
4027 }));
4028
4029 ASSERT_NE(shell, nullptr);
4030 ASSERT_TRUE(shell->IsSetup());
4031 auto configuration = RunConfiguration::InferFromSettings(settings);
4032
4033 // Important: Do not create the platform view yet!
4034 // This test is making sure that the rasterizer can create the texture
4035 // as expected without a surface.
4036
4037 configuration.SetEntrypoint("toImageSync");
4038 RunEngine(shell.get(), std::move(configuration));
4039 PumpOneFrame(shell.get());
4040
4041 latch.Wait();
4042
4043 PlatformViewNotifyDestroyed(shell.get());
4044 DestroyShell(std::move(shell));
4045#endif // !SHELL_ENABLE_METAL
4046}
4047
4048#if SHELL_ENABLE_GL
4049// This test uses the GL backend and refers to symbols in egl.h
4050TEST_F(ShellTest, PictureToImageSyncWithTrampledContext) {
4051 // make it easier to trample the GL context by running on a single task
4052 // runner.
4053 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4054 ThreadHost::Type::kPlatform);
4055 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4056 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4057 task_runner);
4058
4059 auto settings = CreateSettingsForFixture();
4060 std::unique_ptr<Shell> shell = CreateShell({
4061 .settings = settings,
4062 .task_runners = task_runners,
4063 .platform_view_create_callback = ShellTestPlatformViewBuilder({
4064 .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend,
4065 }),
4066 });
4067
4068 AddNativeCallback(
4069 "NativeOnBeforeToImageSync", CREATE_NATIVE_ENTRY([&](auto args) {
4070 // Trample the GL context. If the rasterizer fails
4071 // to make the right one current again, test will
4072 // fail.
4073 ::eglMakeCurrent(::eglGetCurrentDisplay(), NULL, NULL, NULL);
4074 }));
4075
4076 fml::CountDownLatch latch(2);
4077 AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4078 // Teardown and set up rasterizer again.
4079 PlatformViewNotifyDestroyed(shell.get());
4080 PlatformViewNotifyCreated(shell.get());
4081 latch.CountDown();
4082 }));
4083
4084 ASSERT_NE(shell, nullptr);
4085 ASSERT_TRUE(shell->IsSetup());
4086 auto configuration = RunConfiguration::InferFromSettings(settings);
4087 PlatformViewNotifyCreated(shell.get());
4088 configuration.SetEntrypoint("toImageSync");
4089 RunEngine(shell.get(), std::move(configuration));
4090 PumpOneFrame(shell.get());
4091
4092 latch.Wait();
4093
4094 PlatformViewNotifyDestroyed(shell.get());
4095 DestroyShell(std::move(shell), task_runners);
4096}
4097#endif // SHELL_ENABLE_GL
4098
4099TEST_F(ShellTest, PluginUtilitiesCallbackHandleErrorHandling) {
4100 auto settings = CreateSettingsForFixture();
4101 std::unique_ptr<Shell> shell = CreateShell(settings);
4102
4104 bool test_passed;
4105 AddNativeCallback("NotifyNativeBool", CREATE_NATIVE_ENTRY([&](auto args) {
4106 Dart_Handle exception = nullptr;
4108 args, 0, exception);
4109 latch.Signal();
4110 }));
4111
4112 ASSERT_NE(shell, nullptr);
4113 ASSERT_TRUE(shell->IsSetup());
4114 auto configuration = RunConfiguration::InferFromSettings(settings);
4115 PlatformViewNotifyCreated(shell.get());
4116 configuration.SetEntrypoint("testPluginUtilitiesCallbackHandle");
4117 RunEngine(shell.get(), std::move(configuration));
4118 PumpOneFrame(shell.get());
4119
4120 latch.Wait();
4121
4122 ASSERT_TRUE(test_passed);
4123
4124 PlatformViewNotifyDestroyed(shell.get());
4125 DestroyShell(std::move(shell));
4126}
4127
4128TEST_F(ShellTest, NotifyIdleRejectsPastAndNearFuture) {
4129 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4130 Settings settings = CreateSettingsForFixture();
4131 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4132 ThreadHost::Type::kPlatform | ThreadHost::kUi |
4133 ThreadHost::kIo | ThreadHost::kRaster);
4134 auto platform_task_runner = thread_host.platform_thread->GetTaskRunner();
4135 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4136 thread_host.raster_thread->GetTaskRunner(),
4137 thread_host.ui_thread->GetTaskRunner(),
4138 thread_host.io_thread->GetTaskRunner());
4139 auto shell = CreateShell(settings, task_runners);
4140 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
4141 ASSERT_TRUE(ValidateShell(shell.get()));
4142
4144
4145 auto configuration = RunConfiguration::InferFromSettings(settings);
4146 configuration.SetEntrypoint("emptyMain");
4147 RunEngine(shell.get(), std::move(configuration));
4148
4150 task_runners.GetUITaskRunner(), [&latch, &shell]() {
4151 auto runtime_controller = const_cast<RuntimeController*>(
4152 shell->GetEngine()->GetRuntimeController());
4153
4154 auto now = fml::TimeDelta::FromMicroseconds(Dart_TimelineGetMicros());
4155
4156 EXPECT_FALSE(runtime_controller->NotifyIdle(
4157 now - fml::TimeDelta::FromMilliseconds(10)));
4158 EXPECT_FALSE(runtime_controller->NotifyIdle(now));
4159 EXPECT_FALSE(runtime_controller->NotifyIdle(
4160 now + fml::TimeDelta::FromNanoseconds(100)));
4161
4162 EXPECT_TRUE(runtime_controller->NotifyIdle(
4163 now + fml::TimeDelta::FromMilliseconds(100)));
4164 latch.Signal();
4165 });
4166
4167 latch.Wait();
4168
4169 DestroyShell(std::move(shell), task_runners);
4170 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4171}
4172
4173TEST_F(ShellTest, NotifyIdleNotCalledInLatencyMode) {
4174 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4175 Settings settings = CreateSettingsForFixture();
4176 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4177 ThreadHost::Type::kPlatform | ThreadHost::kUi |
4178 ThreadHost::kIo | ThreadHost::kRaster);
4179 auto platform_task_runner = thread_host.platform_thread->GetTaskRunner();
4180 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4181 thread_host.raster_thread->GetTaskRunner(),
4182 thread_host.ui_thread->GetTaskRunner(),
4183 thread_host.io_thread->GetTaskRunner());
4184 auto shell = CreateShell(settings, task_runners);
4185 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
4186 ASSERT_TRUE(ValidateShell(shell.get()));
4187
4188 // we start off in balanced mode, where we expect idle notifications to
4189 // succeed. After the first `NotifyNativeBool` we expect to be in latency
4190 // mode, where we expect idle notifications to fail.
4191 fml::CountDownLatch latch(2);
4192 AddNativeCallback(
4193 "NotifyNativeBool", CREATE_NATIVE_ENTRY([&](auto args) {
4194 Dart_Handle exception = nullptr;
4195 bool is_in_latency_mode =
4197 auto runtime_controller = const_cast<RuntimeController*>(
4198 shell->GetEngine()->GetRuntimeController());
4199 bool success =
4200 runtime_controller->NotifyIdle(fml::TimeDelta::FromMicroseconds(
4201 Dart_TimelineGetMicros() + 100000));
4202 EXPECT_EQ(success, !is_in_latency_mode);
4203 latch.CountDown();
4204 }));
4205
4206 auto configuration = RunConfiguration::InferFromSettings(settings);
4207 configuration.SetEntrypoint("performanceModeImpactsNotifyIdle");
4208 RunEngine(shell.get(), std::move(configuration));
4209
4210 latch.Wait();
4211
4212 DestroyShell(std::move(shell), task_runners);
4213 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4214}
4215
4216TEST_F(ShellTest, PrintsErrorWhenPlatformMessageSentFromWrongThread) {
4217#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DEBUG || OS_FUCHSIA
4218 GTEST_SKIP() << "Test is for debug mode only on non-fuchsia targets.";
4219#else
4220 Settings settings = CreateSettingsForFixture();
4221 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4222 ThreadHost::Type::kPlatform);
4223 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4224 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4225 task_runner);
4226 auto shell = CreateShell(settings, task_runners);
4227
4228 {
4229 fml::testing::LogCapture log_capture;
4230
4231 // The next call will result in a thread checker violation.
4233 SendPlatformMessage(shell.get(), std::make_unique<PlatformMessage>(
4234 "com.test.plugin", nullptr));
4235
4236 EXPECT_THAT(
4237 log_capture.str(),
4238 ::testing::EndsWith(
4239 "The 'com.test.plugin' channel sent a message from native to "
4240 "Flutter on a non-platform thread. Platform channel messages "
4241 "must be sent on the platform thread. Failure to do so may "
4242 "result in data loss or crashes, and must be fixed in the "
4243 "plugin or application code creating that channel.\nSee "
4244 "https://docs.flutter.dev/platform-integration/"
4245 "platform-channels#channels-and-platform-threading for more "
4246 "information.\n"));
4247 }
4248
4249 {
4250 fml::testing::LogCapture log_capture;
4251
4252 // The next call will result in a thread checker violation.
4254 SendPlatformMessage(shell.get(), std::make_unique<PlatformMessage>(
4255 "com.test.plugin", nullptr));
4256
4257 EXPECT_EQ(log_capture.str(), "");
4258 }
4259
4260 DestroyShell(std::move(shell), task_runners);
4261 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4262#endif
4263}
4264
4265TEST_F(ShellTest, NavigationMessageDispachedImmediately) {
4266 Settings settings = CreateSettingsForFixture();
4267 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4268 ThreadHost::Type::kPlatform);
4269 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4270 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4271 task_runner);
4272 auto shell = CreateShell(settings, task_runners);
4273
4274 auto latch = std::make_shared<fml::CountDownLatch>(1u);
4275 task_runner->PostTask([&]() {
4276 auto message = MakePlatformMessage(
4277 "flutter/navigation",
4278 {{"method", "setInitialRoute"}, {"args", "/testo"}}, nullptr);
4279 SendPlatformMessage(shell.get(), std::move(message));
4280 EXPECT_EQ(shell->GetEngine()->InitialRoute(), "/testo");
4281
4282 latch->CountDown();
4283 });
4284 latch->Wait();
4285
4286 DestroyShell(std::move(shell), task_runners);
4287 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4288}
4289
4290// Verifies a semantics Action will flush the dart event loop.
4291TEST_F(ShellTest, SemanticsActionsFlushMessageLoop) {
4292 Settings settings = CreateSettingsForFixture();
4293 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4294 ThreadHost::Type::kPlatform);
4295 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4296 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4297 task_runner);
4298
4299 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4300 task_runners.GetUITaskRunner());
4301 auto shell = CreateShell(settings, task_runners);
4302 auto configuration = RunConfiguration::InferFromSettings(settings);
4303 configuration.SetEntrypoint("testSemanticsActions");
4304
4305 RunEngine(shell.get(), std::move(configuration));
4306 fml::CountDownLatch latch(1);
4307 AddNativeCallback(
4308 // The Dart native function names aren't very consistent but this is
4309 // just the native function name of the second vm entrypoint in the
4310 // fixture.
4311 "NotifyNative",
4312 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
4313
4314 task_runners.GetPlatformTaskRunner()->PostTask([&] {
4315 SendSemanticsAction(shell.get(), 456, 0, SemanticsAction::kTap,
4316 fml::MallocMapping(nullptr, 0));
4317 });
4318 latch.Wait();
4319
4320 DestroyShell(std::move(shell), task_runners);
4321 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4322}
4323
4324// Verifies a pointer event will flush the dart event loop.
4325TEST_F(ShellTest, PointerPacketFlushMessageLoop) {
4326 Settings settings = CreateSettingsForFixture();
4327 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4328 ThreadHost::Type::kPlatform);
4329 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4330 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4331 task_runner);
4332
4333 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4334 task_runners.GetUITaskRunner());
4335 auto shell = CreateShell(settings, task_runners);
4336 auto configuration = RunConfiguration::InferFromSettings(settings);
4337 configuration.SetEntrypoint("testPointerActions");
4338
4339 RunEngine(shell.get(), std::move(configuration));
4340 fml::CountDownLatch latch(1);
4341 AddNativeCallback(
4342 // The Dart native function names aren't very consistent but this is
4343 // just the native function name of the second vm entrypoint in the
4344 // fixture.
4345 "NotifyNative",
4346 CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
4347
4348 DispatchFakePointerData(shell.get(), 23);
4349 latch.Wait();
4350
4351 DestroyShell(std::move(shell), task_runners);
4352 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4353}
4354
4355// Verifies a pointer event will flush the dart event loop.
4356TEST_F(ShellTest, DISABLED_PointerPacketsAreDispatchedWithTask) {
4357 Settings settings = CreateSettingsForFixture();
4358 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4359 ThreadHost::Type::kPlatform);
4360 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4361 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4362 task_runner);
4363
4364 EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4365 task_runners.GetUITaskRunner());
4366 auto shell = CreateShell(settings, task_runners);
4367 auto configuration = RunConfiguration::InferFromSettings(settings);
4368 configuration.SetEntrypoint("testDispatchEvents");
4369
4370 RunEngine(shell.get(), std::move(configuration));
4371 fml::CountDownLatch latch(1);
4372 bool did_invoke_callback = false;
4373 AddNativeCallback(
4374 // The Dart native function names aren't very consistent but this is
4375 // just the native function name of the second vm entrypoint in the
4376 // fixture.
4377 "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4378 did_invoke_callback = true;
4379 latch.CountDown();
4380 }));
4381
4382 DispatchFakePointerData(shell.get(), 23);
4383 EXPECT_FALSE(did_invoke_callback);
4384 latch.Wait();
4385 EXPECT_TRUE(did_invoke_callback);
4386
4387 DestroyShell(std::move(shell), task_runners);
4388 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4389}
4390
4391TEST_F(ShellTest, DiesIfSoftwareRenderingAndImpellerAreEnabledDeathTest) {
4392#if defined(OS_FUCHSIA)
4393 GTEST_SKIP() << "Fuchsia";
4394#else
4395 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
4396 Settings settings = CreateSettingsForFixture();
4397 settings.enable_impeller = true;
4398 settings.enable_software_rendering = true;
4399 ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4400 ThreadHost::Type::kPlatform);
4401 auto task_runner = thread_host.platform_thread->GetTaskRunner();
4402 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4403 task_runner);
4404 EXPECT_DEATH_IF_SUPPORTED(
4405 CreateShell(settings, task_runners),
4406 "Software rendering is incompatible with Impeller.");
4407#endif // OS_FUCHSIA
4408}
4409
4410// Parse the arguments of NativeReportViewIdsCallback and
4411// store them in hasImplicitView and viewIds.
4412static void ParseViewIdsCallback(const Dart_NativeArguments& args,
4413 bool* hasImplicitView,
4414 std::vector<int64_t>* viewIds) {
4415 Dart_Handle exception = nullptr;
4416 viewIds->clear();
4417 *hasImplicitView =
4419 ASSERT_EQ(exception, nullptr);
4420 *viewIds = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
4421 args, 1, exception);
4422 ASSERT_EQ(exception, nullptr);
4423}
4424
4425TEST_F(ShellTest, ShellStartsWithImplicitView) {
4426 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4427 Settings settings = CreateSettingsForFixture();
4428 auto task_runner = CreateNewThread();
4429 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4430 task_runner);
4431 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4432 ASSERT_TRUE(shell);
4433
4434 bool hasImplicitView;
4435 std::vector<int64_t> viewIds;
4436 fml::AutoResetWaitableEvent reportLatch;
4437 auto nativeViewIdsCallback = [&reportLatch, &hasImplicitView,
4438 &viewIds](Dart_NativeArguments args) {
4439 ParseViewIdsCallback(args, &hasImplicitView, &viewIds);
4440 reportLatch.Signal();
4441 };
4442 AddNativeCallback("NativeReportViewIdsCallback",
4443 CREATE_NATIVE_ENTRY(nativeViewIdsCallback));
4444
4445 PlatformViewNotifyCreated(shell.get());
4446 auto configuration = RunConfiguration::InferFromSettings(settings);
4447 configuration.SetEntrypoint("testReportViewIds");
4448 RunEngine(shell.get(), std::move(configuration));
4449 reportLatch.Wait();
4450
4451 ASSERT_TRUE(hasImplicitView);
4452 ASSERT_EQ(viewIds.size(), 1u);
4453 ASSERT_EQ(viewIds[0], 0ll);
4454
4455 PlatformViewNotifyDestroyed(shell.get());
4456 DestroyShell(std::move(shell), task_runners);
4457}
4458
4459// Tests that Shell::AddView and Shell::RemoveView works.
4460TEST_F(ShellTest, ShellCanAddViewOrRemoveView) {
4461 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4462 Settings settings = CreateSettingsForFixture();
4464 "io.flutter.test." + GetCurrentTestName() + ".",
4465 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4466 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4467 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4468 thread_host.raster_thread->GetTaskRunner(),
4469 thread_host.ui_thread->GetTaskRunner(),
4470 thread_host.io_thread->GetTaskRunner());
4471 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4472 ASSERT_TRUE(shell);
4473
4474 bool hasImplicitView;
4475 std::vector<int64_t> viewIds;
4476 fml::AutoResetWaitableEvent reportLatch;
4477 auto nativeViewIdsCallback = [&reportLatch, &hasImplicitView,
4478 &viewIds](Dart_NativeArguments args) {
4479 ParseViewIdsCallback(args, &hasImplicitView, &viewIds);
4480 reportLatch.Signal();
4481 };
4482 AddNativeCallback("NativeReportViewIdsCallback",
4483 CREATE_NATIVE_ENTRY(nativeViewIdsCallback));
4484
4485 PlatformViewNotifyCreated(shell.get());
4486 auto configuration = RunConfiguration::InferFromSettings(settings);
4487 configuration.SetEntrypoint("testReportViewIds");
4488 RunEngine(shell.get(), std::move(configuration));
4489
4490 reportLatch.Wait();
4491 ASSERT_TRUE(hasImplicitView);
4492 ASSERT_EQ(viewIds.size(), 1u);
4493 ASSERT_EQ(viewIds[0], 0ll);
4494
4495 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4496 shell->GetPlatformView()->AddView(2, ViewportMetrics{},
4497 [](bool added) { EXPECT_TRUE(added); });
4498 });
4499 reportLatch.Wait();
4500 ASSERT_TRUE(hasImplicitView);
4501 ASSERT_EQ(viewIds.size(), 2u);
4502 ASSERT_EQ(viewIds[1], 2ll);
4503
4504 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4505 shell->GetPlatformView()->RemoveView(
4506 2, [](bool removed) { ASSERT_TRUE(removed); });
4507 });
4508 reportLatch.Wait();
4509 ASSERT_TRUE(hasImplicitView);
4510 ASSERT_EQ(viewIds.size(), 1u);
4511 ASSERT_EQ(viewIds[0], 0ll);
4512
4513 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4514 shell->GetPlatformView()->AddView(4, ViewportMetrics{},
4515 [](bool added) { EXPECT_TRUE(added); });
4516 });
4517 reportLatch.Wait();
4518 ASSERT_TRUE(hasImplicitView);
4519 ASSERT_EQ(viewIds.size(), 2u);
4520 ASSERT_EQ(viewIds[1], 4ll);
4521
4522 PlatformViewNotifyDestroyed(shell.get());
4523 DestroyShell(std::move(shell), task_runners);
4524}
4525
4526// Test that add view fails if the view ID already exists.
4527TEST_F(ShellTest, ShellCannotAddDuplicateViewId) {
4528 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4529 Settings settings = CreateSettingsForFixture();
4531 "io.flutter.test." + GetCurrentTestName() + ".",
4532 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4533 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4534 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4535 thread_host.raster_thread->GetTaskRunner(),
4536 thread_host.ui_thread->GetTaskRunner(),
4537 thread_host.io_thread->GetTaskRunner());
4538 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4539 ASSERT_TRUE(shell);
4540
4541 bool has_implicit_view;
4542 std::vector<int64_t> view_ids;
4543 fml::AutoResetWaitableEvent report_latch;
4544 AddNativeCallback("NativeReportViewIdsCallback",
4545 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4546 ParseViewIdsCallback(args, &has_implicit_view, &view_ids);
4547 report_latch.Signal();
4548 }));
4549
4550 PlatformViewNotifyCreated(shell.get());
4551 auto configuration = RunConfiguration::InferFromSettings(settings);
4552 configuration.SetEntrypoint("testReportViewIds");
4553 RunEngine(shell.get(), std::move(configuration));
4554
4555 report_latch.Wait();
4556 ASSERT_TRUE(has_implicit_view);
4557 ASSERT_EQ(view_ids.size(), 1u);
4558 ASSERT_EQ(view_ids[0], kImplicitViewId);
4559
4560 // Add view 123.
4562 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell,
4563 &add_latch] {
4564 shell->GetPlatformView()->AddView(123, ViewportMetrics{}, [&](bool added) {
4565 EXPECT_TRUE(added);
4566 add_latch.Signal();
4567 });
4568 });
4569
4570 add_latch.Wait();
4571
4572 report_latch.Wait();
4573 ASSERT_EQ(view_ids.size(), 2u);
4574 ASSERT_EQ(view_ids[0], kImplicitViewId);
4575 ASSERT_EQ(view_ids[1], 123);
4576
4577 // Attempt to add duplicate view ID 123. This should fail.
4578 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell,
4579 &add_latch] {
4580 shell->GetPlatformView()->AddView(123, ViewportMetrics{}, [&](bool added) {
4581 EXPECT_FALSE(added);
4582 add_latch.Signal();
4583 });
4584 });
4585
4586 add_latch.Wait();
4587
4588 PlatformViewNotifyDestroyed(shell.get());
4589 DestroyShell(std::move(shell), task_runners);
4590}
4591
4592// Test that remove view fails if the view ID does not exist.
4593TEST_F(ShellTest, ShellCannotRemoveNonexistentId) {
4594 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign)
4595 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4596 Settings settings = CreateSettingsForFixture();
4598 "io.flutter.test." + GetCurrentTestName() + ".",
4599 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4600 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4601 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4602 thread_host.raster_thread->GetTaskRunner(),
4603 thread_host.ui_thread->GetTaskRunner(),
4604 thread_host.io_thread->GetTaskRunner());
4605 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4606 ASSERT_TRUE(shell);
4607
4608 bool has_implicit_view;
4609 std::vector<int64_t> view_ids;
4610 fml::AutoResetWaitableEvent report_latch;
4611 AddNativeCallback("NativeReportViewIdsCallback",
4612 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4613 ParseViewIdsCallback(args, &has_implicit_view, &view_ids);
4614 report_latch.Signal();
4615 }));
4616
4617 PlatformViewNotifyCreated(shell.get());
4618 auto configuration = RunConfiguration::InferFromSettings(settings);
4619 configuration.SetEntrypoint("testReportViewIds");
4620 RunEngine(shell.get(), std::move(configuration));
4621
4622 report_latch.Wait();
4623 ASSERT_TRUE(has_implicit_view);
4624 ASSERT_EQ(view_ids.size(), 1u);
4625 ASSERT_EQ(view_ids[0], kImplicitViewId);
4626
4627 // Remove view 123. This should fail as this view doesn't exist.
4628 fml::AutoResetWaitableEvent remove_latch;
4629 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(),
4630 [&shell, &remove_latch] {
4631 shell->GetPlatformView()->RemoveView(123, [&](bool removed) {
4632 EXPECT_FALSE(removed);
4633 remove_latch.Signal();
4634 });
4635 });
4636
4637 remove_latch.Wait();
4638
4639 PlatformViewNotifyDestroyed(shell.get());
4640 DestroyShell(std::move(shell), task_runners);
4641}
4642
4643// Parse the arguments of NativeReportViewWidthsCallback and
4644// store them in viewWidths.
4645static void ParseViewWidthsCallback(const Dart_NativeArguments& args,
4646 std::map<int64_t, int64_t>* viewWidths) {
4647 Dart_Handle exception = nullptr;
4648 viewWidths->clear();
4649 std::vector<int64_t> viewWidthPacket =
4651 exception);
4652 ASSERT_EQ(exception, nullptr);
4653 ASSERT_EQ(viewWidthPacket.size() % 2, 0ul);
4654 for (size_t packetIndex = 0; packetIndex < viewWidthPacket.size();
4655 packetIndex += 2) {
4656 (*viewWidths)[viewWidthPacket[packetIndex]] =
4657 viewWidthPacket[packetIndex + 1];
4658 }
4659}
4660
4661// Ensure that PlatformView::SetViewportMetrics and Shell::AddView that were
4662// dispatched before the isolate is run have been flushed to the Dart VM when
4663// the main function starts.
4664TEST_F(ShellTest, ShellFlushesPlatformStatesByMain) {
4665 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4666 Settings settings = CreateSettingsForFixture();
4668 "io.flutter.test." + GetCurrentTestName() + ".",
4669 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4670 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4671 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4672 thread_host.raster_thread->GetTaskRunner(),
4673 thread_host.ui_thread->GetTaskRunner(),
4674 thread_host.io_thread->GetTaskRunner());
4675 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4676 ASSERT_TRUE(shell);
4677
4678 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4679 auto platform_view = shell->GetPlatformView();
4680 // The construtor for ViewportMetrics{_, width, _, _, _} (only the 2nd
4681 // argument matters in this test).
4682 platform_view->SetViewportMetrics(0, ViewportMetrics{1, 10, 1, 0, 0});
4683 shell->GetPlatformView()->AddView(1, ViewportMetrics{1, 30, 1, 0, 0},
4684 [](bool added) { ASSERT_TRUE(added); });
4685 platform_view->SetViewportMetrics(0, ViewportMetrics{1, 20, 1, 0, 0});
4686 });
4687
4688 bool first_report = true;
4689 std::map<int64_t, int64_t> viewWidths;
4690 fml::AutoResetWaitableEvent reportLatch;
4691 auto nativeViewWidthsCallback = [&reportLatch, &viewWidths,
4692 &first_report](Dart_NativeArguments args) {
4693 EXPECT_TRUE(first_report);
4694 first_report = false;
4695 ParseViewWidthsCallback(args, &viewWidths);
4696 reportLatch.Signal();
4697 };
4698 AddNativeCallback("NativeReportViewWidthsCallback",
4699 CREATE_NATIVE_ENTRY(nativeViewWidthsCallback));
4700
4701 PlatformViewNotifyCreated(shell.get());
4702 auto configuration = RunConfiguration::InferFromSettings(settings);
4703 configuration.SetEntrypoint("testReportViewWidths");
4704 RunEngine(shell.get(), std::move(configuration));
4705
4706 reportLatch.Wait();
4707 EXPECT_EQ(viewWidths.size(), 2u);
4708 EXPECT_EQ(viewWidths[0], 20ll);
4709 EXPECT_EQ(viewWidths[1], 30ll);
4710
4711 PlatformViewNotifyDestroyed(shell.get());
4712 DestroyShell(std::move(shell), task_runners);
4713}
4714
4715// A view can be added and removed before the Dart isolate is launched.
4716TEST_F(ShellTest, CanRemoveViewBeforeLaunchingIsolate) {
4717 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4718 Settings settings = CreateSettingsForFixture();
4720 "io.flutter.test." + GetCurrentTestName() + ".",
4721 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4722 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4723 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4724 thread_host.raster_thread->GetTaskRunner(),
4725 thread_host.ui_thread->GetTaskRunner(),
4726 thread_host.io_thread->GetTaskRunner());
4727 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4728 ASSERT_TRUE(shell);
4729
4730 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4731 auto platform_view = shell->GetPlatformView();
4732
4733 // A view can be added and removed all before the isolate launches.
4734 // The pending add view operation is cancelled, the view is never
4735 // added to the Dart isolate.
4736 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 30, 1, 0, 0},
4737 [](bool added) { ASSERT_FALSE(added); });
4738 shell->GetPlatformView()->RemoveView(
4739 123, [](bool removed) { ASSERT_FALSE(removed); });
4740 });
4741
4742 bool first_report = true;
4743 std::map<int64_t, int64_t> view_widths;
4744 fml::AutoResetWaitableEvent report_latch;
4745 AddNativeCallback("NativeReportViewWidthsCallback",
4746 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4747 EXPECT_TRUE(first_report);
4748 first_report = false;
4749 ParseViewWidthsCallback(args, &view_widths);
4750 report_latch.Signal();
4751 }));
4752
4753 PlatformViewNotifyCreated(shell.get());
4754 auto configuration = RunConfiguration::InferFromSettings(settings);
4755 configuration.SetEntrypoint("testReportViewWidths");
4756 RunEngine(shell.get(), std::move(configuration));
4757
4758 report_latch.Wait();
4759 EXPECT_EQ(view_widths.size(), 1u);
4760
4761 PlatformViewNotifyDestroyed(shell.get());
4762 DestroyShell(std::move(shell), task_runners);
4763}
4764
4765// Ensure pending "add views" failures are properly flushed when the Dart
4766// isolate is launched.
4767TEST_F(ShellTest, IgnoresBadAddViewsBeforeLaunchingIsolate) {
4768 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4769 Settings settings = CreateSettingsForFixture();
4771 "io.flutter.test." + GetCurrentTestName() + ".",
4772 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4773 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4774 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4775 thread_host.raster_thread->GetTaskRunner(),
4776 thread_host.ui_thread->GetTaskRunner(),
4777 thread_host.io_thread->GetTaskRunner());
4778 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4779 ASSERT_TRUE(shell);
4780
4781 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell] {
4782 auto platform_view = shell->GetPlatformView();
4783
4784 // Add the same view twice. The second time should fail.
4785 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 100, 1, 0, 0},
4786 [](bool added) { ASSERT_TRUE(added); });
4787
4788 shell->GetPlatformView()->AddView(123, ViewportMetrics{1, 200, 1, 0, 0},
4789 [](bool added) { ASSERT_FALSE(added); });
4790
4791 // Add another view. Previous failures should not affect this.
4792 shell->GetPlatformView()->AddView(456, ViewportMetrics{1, 300, 1, 0, 0},
4793 [](bool added) { ASSERT_TRUE(added); });
4794 });
4795
4796 bool first_report = true;
4797 std::map<int64_t, int64_t> view_widths;
4798 fml::AutoResetWaitableEvent report_latch;
4799 AddNativeCallback("NativeReportViewWidthsCallback",
4800 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4801 EXPECT_TRUE(first_report);
4802 first_report = false;
4803 ParseViewWidthsCallback(args, &view_widths);
4804 report_latch.Signal();
4805 }));
4806
4807 PlatformViewNotifyCreated(shell.get());
4808 auto configuration = RunConfiguration::InferFromSettings(settings);
4809 configuration.SetEntrypoint("testReportViewWidths");
4810 RunEngine(shell.get(), std::move(configuration));
4811
4812 report_latch.Wait();
4813 EXPECT_EQ(view_widths.size(), 3u);
4814 EXPECT_EQ(view_widths[0], 0);
4815 EXPECT_EQ(view_widths[123], 100);
4816 EXPECT_EQ(view_widths[456], 300);
4817
4818 PlatformViewNotifyDestroyed(shell.get());
4819 DestroyShell(std::move(shell), task_runners);
4820}
4821
4822TEST_F(ShellTest, WillLogWarningWhenImpellerIsOptedOut) {
4823#if !IMPELLER_SUPPORTS_RENDERING
4824 GTEST_SKIP() << "This platform doesn't support Impeller.";
4825#endif
4826 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4827 Settings settings = CreateSettingsForFixture();
4828 settings.enable_impeller = false;
4829 settings.warn_on_impeller_opt_out = true;
4830 // Log captures are thread specific. Just put the shell in single threaded
4831 // configuration.
4832 const auto& runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
4833 TaskRunners task_runners("test", runner, runner, runner, runner);
4834 std::ostringstream stream;
4836 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4837 ASSERT_TRUE(
4838 stream.str().find("[Action Required]: Impeller opt-out deprecated.") !=
4839 std::string::npos);
4840 ASSERT_TRUE(shell);
4841 DestroyShell(std::move(shell), task_runners);
4842}
4843
4844TEST_F(ShellTest, SendViewFocusEvent) {
4845 Settings settings = CreateSettingsForFixture();
4846 TaskRunners task_runners = GetTaskRunnersForFixture();
4848 std::string last_event;
4849
4850 AddNativeCallback(
4851 "NotifyNative",
4852 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4853
4854 AddNativeCallback("NotifyMessage",
4855 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4856 const auto message_from_dart =
4858 Dart_GetNativeArgument(args, 0));
4859 last_event = message_from_dart;
4860 latch.Signal();
4861 }));
4862 fml::AutoResetWaitableEvent check_latch;
4863
4864 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4865 ASSERT_TRUE(shell->IsSetup());
4866
4867 auto configuration = RunConfiguration::InferFromSettings(settings);
4868
4869 configuration.SetEntrypoint("testSendViewFocusEvent");
4870 RunEngine(shell.get(), std::move(configuration));
4871 latch.Wait();
4872 latch.Reset();
4873
4874 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
4875 shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
4876 1, ViewFocusState::kFocused, ViewFocusDirection::kUndefined));
4877 });
4878 latch.Wait();
4879 ASSERT_EQ(last_event,
4880 "1 ViewFocusState.focused ViewFocusDirection.undefined");
4881
4882 latch.Reset();
4883 PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
4884 shell->GetPlatformView()->SendViewFocusEvent(ViewFocusEvent(
4885 2, ViewFocusState::kUnfocused, ViewFocusDirection::kBackward));
4886 });
4887 latch.Wait();
4888 ASSERT_EQ(last_event,
4889 "2 ViewFocusState.unfocused ViewFocusDirection.backward");
4890 DestroyShell(std::move(shell), task_runners);
4891}
4892
4893TEST_F(ShellTest, ProvidesEngineId) {
4894 Settings settings = CreateSettingsForFixture();
4895 TaskRunners task_runners = GetTaskRunnersForFixture();
4897
4898 std::optional<int> reported_handle = std::nullopt;
4899
4900 AddNativeCallback(
4901 "ReportEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4902 Dart_Handle arg = Dart_GetNativeArgument(args, 0);
4903 if (Dart_IsNull(arg)) {
4904 reported_handle = std::nullopt;
4905 } else {
4906 reported_handle = tonic::DartConverter<int64_t>::FromDart(arg);
4907 }
4908 latch.Signal();
4909 }));
4910 fml::AutoResetWaitableEvent check_latch;
4911
4912 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4913 ASSERT_TRUE(shell->IsSetup());
4914
4915 auto configuration = RunConfiguration::InferFromSettings(settings);
4916 configuration.SetEngineId(99);
4917 configuration.SetEntrypoint("providesEngineId");
4918 RunEngine(shell.get(), std::move(configuration));
4919
4920 latch.Wait();
4921 ASSERT_EQ(reported_handle, 99);
4922
4923 latch.Reset();
4924
4926 shell->GetTaskRunners().GetUITaskRunner(), [&]() {
4927 ASSERT_EQ(shell->GetEngine()->GetLastEngineId(), 99);
4928 latch.Signal();
4929 });
4930 latch.Wait();
4931 DestroyShell(std::move(shell), task_runners);
4932}
4933
4934TEST_F(ShellTest, ProvidesNullEngineId) {
4935 Settings settings = CreateSettingsForFixture();
4936 TaskRunners task_runners = GetTaskRunnersForFixture();
4938
4939 std::optional<int> reported_handle = std::nullopt;
4940
4941 AddNativeCallback(
4942 "ReportEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4943 Dart_Handle arg = Dart_GetNativeArgument(args, 0);
4944 if (Dart_IsNull(arg)) {
4945 reported_handle = std::nullopt;
4946 } else {
4947 reported_handle = tonic::DartConverter<int64_t>::FromDart(arg);
4948 }
4949 latch.Signal();
4950 }));
4951 fml::AutoResetWaitableEvent check_latch;
4952
4953 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4954 ASSERT_TRUE(shell->IsSetup());
4955
4956 auto configuration = RunConfiguration::InferFromSettings(settings);
4957 configuration.SetEntrypoint("providesEngineId");
4958 RunEngine(shell.get(), std::move(configuration));
4959
4960 latch.Wait();
4961 ASSERT_EQ(reported_handle, std::nullopt);
4962 DestroyShell(std::move(shell), task_runners);
4963}
4964
4965TEST_F(ShellTest, MergeUIAndPlatformThreadsAfterLaunch) {
4966 Settings settings = CreateSettingsForFixture();
4967 settings.merged_platform_ui_thread =
4968 Settings::MergedPlatformUIThread::kMergeAfterLaunch;
4970 "io.flutter.test." + GetCurrentTestName() + ".",
4971 ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
4972 ThreadHost::Type::kIo | ThreadHost::Type::kUi));
4973 TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
4974 thread_host.raster_thread->GetTaskRunner(),
4975 thread_host.ui_thread->GetTaskRunner(),
4976 thread_host.io_thread->GetTaskRunner());
4977
4978 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
4979 ASSERT_TRUE(shell);
4980
4982 task_runners.GetUITaskRunner()->GetTaskQueueId(),
4983 task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
4984
4986 AddNativeCallback(
4987 "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
4988 ASSERT_TRUE(
4990 latch.Signal();
4991 }));
4992
4993 auto configuration = RunConfiguration::InferFromSettings(settings);
4994 configuration.SetEntrypoint("mainNotifyNative");
4995 RunEngine(shell.get(), std::move(configuration));
4996
4997 latch.Wait();
4998
5000 task_runners.GetUITaskRunner()->GetTaskQueueId(),
5001 task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
5002
5003 DestroyShell(std::move(shell), task_runners);
5004}
5005
5006TEST_F(ShellTest, ReleaseResourceContextWhenIOManagerIsDeleted) {
5007 TaskRunners task_runners = GetTaskRunnersForFixture();
5008 auto settings = CreateSettingsForFixture();
5009 bool called_release_resource_context = false;
5010 Shell::CreateCallback<PlatformView> platform_view_create_callback =
5011 [task_runners, &called_release_resource_context](flutter::Shell& shell) {
5012 auto result = std::make_unique<::testing::NiceMock<TestPlatformView>>(
5013 shell, task_runners);
5014 ON_CALL(*result, ReleaseResourceContext())
5015 .WillByDefault(
5016 ::testing::Assign(&called_release_resource_context, true));
5017 return result;
5018 };
5019
5020 auto parent_shell = CreateShell({
5021 .settings = settings,
5022 .task_runners = task_runners,
5023 .platform_view_create_callback = platform_view_create_callback,
5024 });
5025
5026 std::unique_ptr<Shell> child_shell;
5027 PostSync(
5028 parent_shell->GetTaskRunners().GetPlatformTaskRunner(),
5029 [&parent_shell, &settings, &child_shell, platform_view_create_callback] {
5030 auto configuration = RunConfiguration::InferFromSettings(settings);
5031 configuration.SetEntrypoint("emptyMain");
5032 auto child = parent_shell->Spawn(
5033 std::move(configuration), "", platform_view_create_callback,
5034 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
5035 child_shell = std::move(child);
5036 });
5037
5038 DestroyShell(std::move(parent_shell), task_runners);
5039 ASSERT_FALSE(called_release_resource_context);
5040
5041 DestroyShell(std::move(child_shell), task_runners);
5042 ASSERT_TRUE(called_release_resource_context);
5043}
5044
5045TEST_F(ShellTest, ShoulDiscardLayerTreeIfFrameIsSizedIncorrectly) {
5046 Settings settings = CreateSettingsForFixture();
5047 auto task_runner = CreateNewThread();
5048 TaskRunners task_runners("test", task_runner, task_runner, task_runner,
5049 task_runner);
5050 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
5051
5053 shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
5054 shell->GetPlatformView()->SetViewportMetrics(
5055 kImplicitViewId,
5056 {
5057 1.0, // p_device_pixel_ratio
5058 500, // p_physical_width
5059 800, // p_physical_height
5060 1, // p_min_width_constraint,
5061 1000, // p_max_width_constraint,
5062 1, // p_min_height_constraint,
5063 1000, // p_max_height_constraint,
5064 0, // p_physical_padding_top
5065 0, // p_physical_padding_right
5066 0, // p_physical_padding_bottom
5067 0, // p_physical_padding_left
5068 0, // p_physical_view_inset_top,
5069 0, // p_physical_view_inset_right,
5070 0, // p_physical_view_inset_bottom,
5071 0, // p_physical_view_inset_left,
5072 0, // p_physical_system_gesture_inset_top,
5073 0, // p_physical_system_gesture_inset_right,
5074 0, // p_physical_system_gesture_inset_bottom,
5075 0, // p_physical_system_gesture_inset_left,
5076 22, // p_physical_touch_slop,
5077 {}, // p_physical_display_features_bounds,
5078 {}, // p_physical_display_features_type,
5079 {}, // p_physical_display_features_state,
5080 0 // p_display_id
5081 });
5082 });
5083 PumpOneFrame(shell.get());
5084
5085 auto layer_tree =
5086 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5087 /*frame_size=*/DlISize(100, 100));
5088 ASSERT_FALSE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5089 *layer_tree));
5090 auto over_width =
5091 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5092 /*frame_size=*/DlISize(1010, 100));
5093 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5094 *over_width));
5095 auto over_height =
5096 std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5097 /*frame_size=*/DlISize(100, 1010));
5098 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5099 *over_height));
5100 auto min_width = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5101 /*frame_size=*/DlISize(0, 100));
5102 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5103 *min_width));
5104 auto min_height = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
5105 /*frame_size=*/DlISize(100, 0));
5106 ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId,
5107 *min_height));
5108 DestroyShell(std::move(shell), task_runners);
5109}
5110
5111} // namespace testing
5112} // namespace flutter
5113
5114// 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:222
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition shell.cc:916
std::shared_ptr< const fml::SyncSwitch > GetIsGpuDisabledSyncSwitch() const override
Accessor for the disable GPU SyncSwitch.
Definition shell.cc:2389
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:839
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
const char * message
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
static constexpr FlutterViewId kImplicitViewId
std::shared_ptr< ImpellerAllocator > allocator
union flutter::testing::@2832::KeyboardChange::@77 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