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