Flutter Engine
shell_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define FML_USED_ON_EMBEDDER
6 
7 #include <algorithm>
8 #include <ctime>
9 #include <functional>
10 #include <future>
11 #include <memory>
12 #include <vector>
13 
16 #include "flutter/common/graphics/persistent_cache.h"
17 #include "flutter/flow/layers/layer_tree.h"
18 #include "flutter/flow/layers/picture_layer.h"
19 #include "flutter/flow/layers/transform_layer.h"
20 #include "flutter/fml/command_line.h"
21 #include "flutter/fml/dart/dart_converter.h"
22 #include "flutter/fml/make_copyable.h"
23 #include "flutter/fml/message_loop.h"
24 #include "flutter/fml/synchronization/count_down_latch.h"
25 #include "flutter/fml/synchronization/waitable_event.h"
26 #include "flutter/runtime/dart_vm.h"
27 #include "flutter/shell/common/platform_view.h"
28 #include "flutter/shell/common/rasterizer.h"
29 #include "flutter/shell/common/shell_test.h"
30 #include "flutter/shell/common/shell_test_external_view_embedder.h"
31 #include "flutter/shell/common/shell_test_platform_view.h"
32 #include "flutter/shell/common/switches.h"
33 #include "flutter/shell/common/thread_host.h"
34 #include "flutter/shell/common/vsync_waiter_fallback.h"
35 #include "flutter/shell/version/version.h"
36 #include "flutter/testing/testing.h"
37 #include "gmock/gmock.h"
38 #include "third_party/rapidjson/include/rapidjson/writer.h"
39 #include "third_party/skia/include/core/SkPictureRecorder.h"
41 
42 #ifdef SHELL_ENABLE_VULKAN
43 #include "flutter/vulkan/vulkan_application.h" // nogncheck
44 #endif
45 
46 namespace flutter {
47 namespace testing {
48 
49 using ::testing::_;
50 using ::testing::Return;
51 
52 namespace {
53 class MockPlatformViewDelegate : public PlatformView::Delegate {
54  MOCK_METHOD1(OnPlatformViewCreated, void(std::unique_ptr<Surface> surface));
55 
56  MOCK_METHOD0(OnPlatformViewDestroyed, void());
57 
58  MOCK_METHOD1(OnPlatformViewSetNextFrameCallback,
59  void(const fml::closure& closure));
60 
61  MOCK_METHOD1(OnPlatformViewSetViewportMetrics,
62  void(const ViewportMetrics& metrics));
63 
64  MOCK_METHOD1(OnPlatformViewDispatchPlatformMessage,
65  void(std::unique_ptr<PlatformMessage> message));
66 
67  MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket,
68  void(std::unique_ptr<PointerDataPacket> packet));
69 
70  MOCK_METHOD2(OnPlatformViewDispatchKeyDataPacket,
71  void(std::unique_ptr<KeyDataPacket> packet,
73 
74  MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction,
75  void(int32_t id,
78 
79  MOCK_METHOD1(OnPlatformViewSetSemanticsEnabled, void(bool enabled));
80 
81  MOCK_METHOD1(OnPlatformViewSetAccessibilityFeatures, void(int32_t flags));
82 
83  MOCK_METHOD1(OnPlatformViewRegisterTexture,
84  void(std::shared_ptr<Texture> texture));
85 
86  MOCK_METHOD1(OnPlatformViewUnregisterTexture, void(int64_t texture_id));
87 
88  MOCK_METHOD1(OnPlatformViewMarkTextureFrameAvailable,
89  void(int64_t texture_id));
90 
91  MOCK_METHOD3(LoadDartDeferredLibrary,
92  void(intptr_t loading_unit_id,
93  std::unique_ptr<const fml::Mapping> snapshot_data,
94  std::unique_ptr<const fml::Mapping> snapshot_instructions));
95 
96  MOCK_METHOD3(LoadDartDeferredLibraryError,
97  void(intptr_t loading_unit_id,
98  const std::string error_message,
99  bool transient));
100 
101  MOCK_METHOD2(UpdateAssetResolverByType,
102  void(std::unique_ptr<AssetResolver> updated_asset_resolver,
104 };
105 
106 class MockSurface : public Surface {
107  MOCK_METHOD0(IsValid, bool());
108 
109  MOCK_METHOD1(AcquireFrame,
110  std::unique_ptr<SurfaceFrame>(const SkISize& size));
111 
112  MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix());
113 
114  MOCK_METHOD0(GetContext, GrDirectContext*());
115 
116  MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr<GLContextResult>());
117 
118  MOCK_METHOD0(ClearRenderContext, bool());
119 };
120 
121 class MockPlatformView : public PlatformView {
122  public:
123  MockPlatformView(MockPlatformViewDelegate& delegate, TaskRunners task_runners)
124  : PlatformView(delegate, task_runners) {}
125  MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr<Surface>());
126  MOCK_CONST_METHOD0(GetPlatformMessageHandler,
127  std::shared_ptr<PlatformMessageHandler>());
128 };
129 
130 class MockPlatformMessageHandler : public PlatformMessageHandler {
131  public:
132  MOCK_METHOD1(HandlePlatformMessage,
133  void(std::unique_ptr<PlatformMessage> message));
135  void(int response_id, std::unique_ptr<fml::Mapping> mapping));
137  void(int response_id));
138 };
139 
140 class MockPlatformMessageResponse : public PlatformMessageResponse {
141  public:
143  return fml::AdoptRef(new MockPlatformMessageResponse());
144  }
145  MOCK_METHOD1(Complete, void(std::unique_ptr<fml::Mapping> data));
146  MOCK_METHOD0(CompleteEmpty, void());
147 };
148 } // namespace
149 
151  public:
153  : valid_(valid), type_(type) {}
154 
155  bool IsValid() const override { return true; }
156 
157  // This is used to identify if replacement was made or not.
158  bool IsValidAfterAssetManagerChange() const override { return valid_; }
159 
160  AssetResolver::AssetResolverType GetType() const override { return type_; }
161 
162  std::unique_ptr<fml::Mapping> GetAsMapping(
163  const std::string& asset_name) const override {
164  return nullptr;
165  }
166 
167  std::vector<std::unique_ptr<fml::Mapping>> GetAsMappings(
168  const std::string& asset_pattern,
169  const std::optional<std::string>& subdir) const override {
170  return {};
171  };
172 
173  private:
174  bool valid_;
176 };
177 
178 static bool ValidateShell(Shell* shell) {
179  if (!shell) {
180  return false;
181  }
182 
183  if (!shell->IsSetup()) {
184  return false;
185  }
186 
188 
189  {
192  shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() {
193  shell->GetPlatformView()->NotifyDestroyed();
194  latch.Signal();
195  });
196  latch.Wait();
197  }
198 
199  return true;
200 }
201 
202 static bool RasterizerHasLayerTree(Shell* shell) {
204  bool has_layer_tree = false;
207  [shell, &latch, &has_layer_tree]() {
208  has_layer_tree = shell->GetRasterizer()->GetLastLayerTree() != nullptr;
209  latch.Signal();
210  });
211  latch.Wait();
212  return has_layer_tree;
213 }
214 
215 static void ValidateDestroyPlatformView(Shell* shell) {
216  ASSERT_TRUE(shell != nullptr);
217  ASSERT_TRUE(shell->IsSetup());
218 
219  // To validate destroy platform view, we must ensure the rasterizer has a
220  // layer tree before the platform view is destroyed.
221  ASSERT_TRUE(RasterizerHasLayerTree(shell));
222 
224  // Validate the layer tree is destroyed
225  ASSERT_FALSE(RasterizerHasLayerTree(shell));
226 }
227 
228 static std::string CreateFlagsString(std::vector<const char*>& flags) {
229  if (flags.size() == 0) {
230  return "";
231  }
232  std::string flags_string = flags[0];
233  for (size_t i = 1; i < flags.size(); ++i) {
234  flags_string += ",";
235  flags_string += flags[i];
236  }
237  return flags_string;
238 }
239 
240 static void TestDartVmFlags(std::vector<const char*>& flags) {
241  std::string flags_string = CreateFlagsString(flags);
242  const std::vector<fml::CommandLine::Option> options = {
243  fml::CommandLine::Option("dart-flags", flags_string)};
244  fml::CommandLine command_line("", options, std::vector<std::string>());
245  flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
246  EXPECT_EQ(settings.dart_flags.size(), flags.size());
247  for (size_t i = 0; i < flags.size(); ++i) {
248  EXPECT_EQ(settings.dart_flags[i], flags[i]);
249  }
250 }
251 
252 static void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
253  const fml::closure& task) {
255  fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] {
256  task();
257  latch.Signal();
258  });
259  latch.Wait();
260 }
261 
262 TEST_F(ShellTest, InitializeWithInvalidThreads) {
263  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
264  Settings settings = CreateSettingsForFixture();
265  TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr);
266  auto shell = CreateShell(std::move(settings), std::move(task_runners));
267  ASSERT_FALSE(shell);
268  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
269 }
270 
271 TEST_F(ShellTest, InitializeWithDifferentThreads) {
272  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
273  Settings settings = CreateSettingsForFixture();
274  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
275  ThreadHost::Type::Platform | ThreadHost::Type::RASTER |
276  ThreadHost::Type::IO | ThreadHost::Type::UI);
277  TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
278  thread_host.raster_thread->GetTaskRunner(),
279  thread_host.ui_thread->GetTaskRunner(),
280  thread_host.io_thread->GetTaskRunner());
281  auto shell = CreateShell(std::move(settings), std::move(task_runners));
282  ASSERT_TRUE(ValidateShell(shell.get()));
283  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
284  DestroyShell(std::move(shell), std::move(task_runners));
285  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
286 }
287 
288 TEST_F(ShellTest, InitializeWithSingleThread) {
289  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
290  Settings settings = CreateSettingsForFixture();
291  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
292  ThreadHost::Type::Platform);
293  auto task_runner = thread_host.platform_thread->GetTaskRunner();
294  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
295  task_runner);
296  auto shell = CreateShell(std::move(settings), task_runners);
297  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
298  ASSERT_TRUE(ValidateShell(shell.get()));
299  DestroyShell(std::move(shell), std::move(task_runners));
300  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
301 }
302 
303 TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) {
304  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
305  Settings settings = CreateSettingsForFixture();
307  auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
308  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
309  task_runner);
310  auto shell = CreateShell(std::move(settings), task_runners);
311  ASSERT_TRUE(ValidateShell(shell.get()));
312  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
313  DestroyShell(std::move(shell), std::move(task_runners));
314  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
315 }
316 
318  InitializeWithMultipleThreadButCallingThreadAsPlatformThread) {
319  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
320  Settings settings = CreateSettingsForFixture();
321  ThreadHost thread_host(
322  "io.flutter.test." + GetCurrentTestName() + ".",
323  ThreadHost::Type::RASTER | ThreadHost::Type::IO | ThreadHost::Type::UI);
325  TaskRunners task_runners("test",
326  fml::MessageLoop::GetCurrent().GetTaskRunner(),
327  thread_host.raster_thread->GetTaskRunner(),
328  thread_host.ui_thread->GetTaskRunner(),
329  thread_host.io_thread->GetTaskRunner());
330  auto shell = Shell::Create(
331  flutter::PlatformData(), std::move(task_runners), settings,
332  [](Shell& shell) {
333  // This is unused in the platform view as we are not using the simulated
334  // vsync mechanism. We should have better DI in the tests.
335  const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
337  shell, shell.GetTaskRunners(), vsync_clock,
338  [task_runners = shell.GetTaskRunners()]() {
339  return static_cast<std::unique_ptr<VsyncWaiter>>(
340  std::make_unique<VsyncWaiterFallback>(task_runners));
341  },
343  },
344  [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
345  ASSERT_TRUE(ValidateShell(shell.get()));
346  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
347  DestroyShell(std::move(shell), std::move(task_runners));
348  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
349 }
350 
351 TEST_F(ShellTest, InitializeWithDisabledGpu) {
352  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
353  Settings settings = CreateSettingsForFixture();
354  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
355  ThreadHost::Type::Platform);
356  auto task_runner = thread_host.platform_thread->GetTaskRunner();
357  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
358  task_runner);
359  auto shell = CreateShell(
360  std::move(settings), std::move(task_runners), /*simulate_vsync=*/false,
361  /*shell_test_external_view_embedder=*/nullptr, /*is_gpu_disabled=*/true);
362  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
363  ASSERT_TRUE(ValidateShell(shell.get()));
364 
365  bool is_disabled = false;
366  shell->GetIsGpuDisabledSyncSwitch()->Execute(
367  fml::SyncSwitch::Handlers().SetIfTrue([&] { is_disabled = true; }));
368  ASSERT_TRUE(is_disabled);
369 
370  DestroyShell(std::move(shell), std::move(task_runners));
371  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
372 }
373 
374 TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) {
375  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
376  Settings settings = CreateSettingsForFixture();
377  ThreadHost thread_host(
378  "io.flutter.test." + GetCurrentTestName() + ".",
379  ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI);
380  TaskRunners task_runners(
381  "test",
382  thread_host.platform_thread->GetTaskRunner(), // platform
383  thread_host.platform_thread->GetTaskRunner(), // raster
384  thread_host.ui_thread->GetTaskRunner(), // ui
385  thread_host.io_thread->GetTaskRunner() // io
386  );
387  auto shell = CreateShell(std::move(settings), std::move(task_runners));
388  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
389  ASSERT_TRUE(ValidateShell(shell.get()));
390  DestroyShell(std::move(shell), std::move(task_runners));
391  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
392 }
393 
394 TEST_F(ShellTest, FixturesAreFunctional) {
395  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
396  auto settings = CreateSettingsForFixture();
397  auto shell = CreateShell(settings);
398  ASSERT_TRUE(ValidateShell(shell.get()));
399 
400  auto configuration = RunConfiguration::InferFromSettings(settings);
401  ASSERT_TRUE(configuration.IsValid());
402  configuration.SetEntrypoint("fixturesAreFunctionalMain");
403 
404  fml::AutoResetWaitableEvent main_latch;
405  AddNativeCallback(
406  "SayHiFromFixturesAreFunctionalMain",
407  CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); }));
408 
409  RunEngine(shell.get(), std::move(configuration));
410  main_latch.Wait();
411  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
412  DestroyShell(std::move(shell));
413  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
414 }
415 
416 TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) {
417  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
418  auto settings = CreateSettingsForFixture();
419  auto shell = CreateShell(settings);
420  ASSERT_TRUE(ValidateShell(shell.get()));
421 
422  auto configuration = RunConfiguration::InferFromSettings(settings);
423  ASSERT_TRUE(configuration.IsValid());
424  configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
425 
426  fml::CountDownLatch latch(2);
427  AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
428  latch.CountDown();
429  }));
430 
431  RunEngine(shell.get(), std::move(configuration));
432 
433  latch.Wait();
434 
435  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
436  DestroyShell(std::move(shell));
437  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
438 }
439 
440 TEST_F(ShellTest, LastEntrypoint) {
441  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
442  auto settings = CreateSettingsForFixture();
443  auto shell = CreateShell(settings);
444  ASSERT_TRUE(ValidateShell(shell.get()));
445 
446  auto configuration = RunConfiguration::InferFromSettings(settings);
447  ASSERT_TRUE(configuration.IsValid());
448  std::string entry_point = "fixturesAreFunctionalMain";
449  configuration.SetEntrypoint(entry_point);
450 
451  fml::AutoResetWaitableEvent main_latch;
452  std::string last_entry_point;
453  AddNativeCallback(
454  "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
455  last_entry_point = shell->GetEngine()->GetLastEntrypoint();
456  main_latch.Signal();
457  }));
458 
459  RunEngine(shell.get(), std::move(configuration));
460  main_latch.Wait();
461  EXPECT_EQ(entry_point, last_entry_point);
462  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
463  DestroyShell(std::move(shell));
464  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
465 }
466 
468 #if defined(WINUWP)
469  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
470  DISABLED_DisallowedDartVMFlag
471 #else
472  DisallowedDartVMFlag
473 #endif // defined(WINUWP)
474 ) {
475  // Run this test in a thread-safe manner, otherwise gtest will complain.
476  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
477 
478  const std::vector<fml::CommandLine::Option> options = {
479  fml::CommandLine::Option("dart-flags", "--verify_after_gc")};
480  fml::CommandLine command_line("", options, std::vector<std::string>());
481 
482  // Upon encountering a disallowed Dart flag the process terminates.
483  const char* expected =
484  "Encountered disallowed Dart VM flag: --verify_after_gc";
485  ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected);
486 }
487 
489 #if defined(WINUWP)
490  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
491  DISABLED_AllowedDartVMFlag
492 #else
493  AllowedDartVMFlag
494 #endif // defined(WINUWP)
495 ) {
496  std::vector<const char*> flags = {
497  "--enable-isolate-groups",
498  "--no-enable-isolate-groups",
499  "--lazy_async_stacks",
500  };
501 #if !FLUTTER_RELEASE
502  flags.push_back("--max_profile_depth 1");
503  flags.push_back("--random_seed 42");
505  flags.push_back("--enable_mirrors");
506  }
507 #endif
508 
509  TestDartVmFlags(flags);
510 }
511 
512 TEST_F(ShellTest, NoNeedToReportTimingsByDefault) {
513  auto settings = CreateSettingsForFixture();
514  std::unique_ptr<Shell> shell = CreateShell(settings);
515 
516  // Create the surface needed by rasterizer
517  PlatformViewNotifyCreated(shell.get());
518 
519  auto configuration = RunConfiguration::InferFromSettings(settings);
520  configuration.SetEntrypoint("emptyMain");
521 
522  RunEngine(shell.get(), std::move(configuration));
523  PumpOneFrame(shell.get());
524  ASSERT_FALSE(GetNeedsReportTimings(shell.get()));
525 
526  // This assertion may or may not be the direct result of needs_report_timings_
527  // being false. The count could be 0 simply because we just cleared
528  // unreported timings by reporting them. Hence this can't replace the
529  // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added
530  // this assertion for an additional confidence that we're not pushing
531  // back to unreported timings unnecessarily.
532  //
533  // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be
534  // positive in any tests. Otherwise those tests will be flaky as the clearing
535  // of unreported timings is unpredictive.
536  ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0);
537  DestroyShell(std::move(shell));
538 }
539 
540 TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) {
541  auto settings = CreateSettingsForFixture();
542  std::unique_ptr<Shell> shell = CreateShell(settings);
543 
544  // Create the surface needed by rasterizer
545  PlatformViewNotifyCreated(shell.get());
546 
547  auto configuration = RunConfiguration::InferFromSettings(settings);
548  configuration.SetEntrypoint("dummyReportTimingsMain");
549 
550  RunEngine(shell.get(), std::move(configuration));
551  PumpOneFrame(shell.get());
552  ASSERT_TRUE(GetNeedsReportTimings(shell.get()));
553  DestroyShell(std::move(shell));
554 }
555 
556 static void CheckFrameTimings(const std::vector<FrameTiming>& timings,
557  fml::TimePoint start,
558  fml::TimePoint finish) {
559  fml::TimePoint last_frame_start;
560  for (size_t i = 0; i < timings.size(); i += 1) {
561  // Ensure that timings are sorted.
562  ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start);
563  last_frame_start = timings[i].Get(FrameTiming::kPhases[0]);
564 
565  fml::TimePoint last_phase_time;
566  for (auto phase : FrameTiming::kPhases) {
567  // raster finish wall time doesn't use the same clock base
568  // as rest of the frame timings.
569  if (phase == FrameTiming::kRasterFinishWallTime) {
570  continue;
571  }
572 
573  ASSERT_TRUE(timings[i].Get(phase) >= start);
574  ASSERT_TRUE(timings[i].Get(phase) <= finish);
575 
576  // phases should have weakly increasing time points
577  ASSERT_TRUE(last_phase_time <= timings[i].Get(phase));
578  last_phase_time = timings[i].Get(phase);
579  }
580  }
581 }
582 
583 TEST_F(ShellTest, ReportTimingsIsCalled) {
585  auto settings = CreateSettingsForFixture();
586  std::unique_ptr<Shell> shell = CreateShell(settings);
587 
588  // Create the surface needed by rasterizer
589  PlatformViewNotifyCreated(shell.get());
590 
591  auto configuration = RunConfiguration::InferFromSettings(settings);
592  ASSERT_TRUE(configuration.IsValid());
593  configuration.SetEntrypoint("reportTimingsMain");
594  fml::AutoResetWaitableEvent reportLatch;
595  std::vector<int64_t> timestamps;
596  auto nativeTimingCallback = [&reportLatch,
597  &timestamps](Dart_NativeArguments args) {
598  Dart_Handle exception = nullptr;
599  ASSERT_EQ(timestamps.size(), 0ul);
600  timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
601  args, 0, exception);
602  reportLatch.Signal();
603  };
604  AddNativeCallback("NativeReportTimingsCallback",
605  CREATE_NATIVE_ENTRY(nativeTimingCallback));
606  RunEngine(shell.get(), std::move(configuration));
607 
608  // Pump many frames so we can trigger the report quickly instead of waiting
609  // for the 1 second threshold.
610  for (int i = 0; i < 200; i += 1) {
611  PumpOneFrame(shell.get());
612  }
613 
614  reportLatch.Wait();
615  DestroyShell(std::move(shell));
616 
618  ASSERT_TRUE(timestamps.size() > 0);
619  ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0);
620  std::vector<FrameTiming> timings(timestamps.size() / FrameTiming::kCount);
621 
622  for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) {
623  for (auto phase : FrameTiming::kPhases) {
624  timings[i].Set(
625  phase,
627  timestamps[i * FrameTiming::kCount + phase])));
628  }
629  }
630  CheckFrameTimings(timings, start, finish);
631 }
632 
633 TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) {
635 
636  auto settings = CreateSettingsForFixture();
637  fml::AutoResetWaitableEvent timingLatch;
638  FrameTiming timing;
639 
640  for (auto phase : FrameTiming::kPhases) {
641  timing.Set(phase, fml::TimePoint());
642  // Check that the time points are initially smaller than start, so
643  // CheckFrameTimings will fail if they're not properly set later.
644  ASSERT_TRUE(timing.Get(phase) < start);
645  }
646 
647  settings.frame_rasterized_callback = [&timing,
648  &timingLatch](const FrameTiming& t) {
649  timing = t;
650  timingLatch.Signal();
651  };
652 
653  std::unique_ptr<Shell> shell = CreateShell(settings);
654 
655  // Create the surface needed by rasterizer
656  PlatformViewNotifyCreated(shell.get());
657 
658  auto configuration = RunConfiguration::InferFromSettings(settings);
659  configuration.SetEntrypoint("onBeginFrameMain");
660 
661  int64_t frame_target_time;
662  auto nativeOnBeginFrame = [&frame_target_time](Dart_NativeArguments args) {
663  Dart_Handle exception = nullptr;
664  frame_target_time =
666  };
667  AddNativeCallback("NativeOnBeginFrame",
668  CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
669 
670  RunEngine(shell.get(), std::move(configuration));
671  PumpOneFrame(shell.get());
672 
673  // Check that timing is properly set. This implies that
674  // settings.frame_rasterized_callback is called.
675  timingLatch.Wait();
677  std::vector<FrameTiming> timings = {timing};
678  CheckFrameTimings(timings, start, finish);
679 
680  // Check that onBeginFrame, which is the frame_target_time, is after
681  // FrameTiming's build start
682  int64_t build_start =
684  ASSERT_GT(frame_target_time, build_start);
685  DestroyShell(std::move(shell));
686 }
687 
688 TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) {
689  auto settings = CreateSettingsForFixture();
690  fml::AutoResetWaitableEvent end_frame_latch;
691  bool end_frame_called = false;
692  auto end_frame_callback =
693  [&](bool should_resubmit_frame,
694  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
695  ASSERT_TRUE(raster_thread_merger.get() == nullptr);
696  ASSERT_FALSE(should_resubmit_frame);
697  end_frame_called = true;
698  end_frame_latch.Signal();
699  };
700  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
701  end_frame_callback, PostPrerollResult::kResubmitFrame, false);
702  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
703  false, external_view_embedder);
704 
705  // Create the surface needed by rasterizer
706  PlatformViewNotifyCreated(shell.get());
707 
708  auto configuration = RunConfiguration::InferFromSettings(settings);
709  configuration.SetEntrypoint("emptyMain");
710 
711  RunEngine(shell.get(), std::move(configuration));
712 
713  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
714  SkPictureRecorder recorder;
715  SkCanvas* recording_canvas =
716  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
717  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
718  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
719  auto sk_picture = recorder.finishRecordingAsPicture();
720  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
721  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
722  auto picture_layer = std::make_shared<PictureLayer>(
723  SkPoint::Make(10, 10),
724  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
725  root->Add(picture_layer);
726  };
727 
728  PumpOneFrame(shell.get(), 100, 100, builder);
729  end_frame_latch.Wait();
730 
731  ASSERT_TRUE(end_frame_called);
732 
733  DestroyShell(std::move(shell));
734 }
735 
736 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
738 #if defined(OS_FUCHSIA)
739  DISABLED_ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit
740 #else
741  ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit
742 #endif
743 ) {
744  auto settings = CreateSettingsForFixture();
745  fml::AutoResetWaitableEvent end_frame_latch;
746  bool end_frame_called = false;
747  auto end_frame_callback =
748  [&](bool should_resubmit_frame,
749  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
750  ASSERT_TRUE(raster_thread_merger.get() != nullptr);
751  ASSERT_TRUE(should_resubmit_frame);
752  end_frame_called = true;
753  end_frame_latch.Signal();
754  };
755  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
756  end_frame_callback, PostPrerollResult::kResubmitFrame, true);
757  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
758  false, external_view_embedder);
759 
760  // Create the surface needed by rasterizer
761  PlatformViewNotifyCreated(shell.get());
762 
763  auto configuration = RunConfiguration::InferFromSettings(settings);
764  configuration.SetEntrypoint("emptyMain");
765 
766  RunEngine(shell.get(), std::move(configuration));
767 
768  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
769  SkPictureRecorder recorder;
770  SkCanvas* recording_canvas =
771  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
772  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
773  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
774  auto sk_picture = recorder.finishRecordingAsPicture();
775  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
776  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
777  auto picture_layer = std::make_shared<PictureLayer>(
778  SkPoint::Make(10, 10),
779  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
780  root->Add(picture_layer);
781  };
782 
783  PumpOneFrame(shell.get(), 100, 100, builder);
784  end_frame_latch.Wait();
785 
786  ASSERT_TRUE(end_frame_called);
787 
788  DestroyShell(std::move(shell));
789 }
790 
792 #if defined(OS_FUCHSIA) || defined(WINUWP)
793  // TODO(dworsham): https://github.com/flutter/flutter/issues/59816
794  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
795  DISABLED_OnPlatformViewDestroyDisablesThreadMerger
796 #else
797  OnPlatformViewDestroyDisablesThreadMerger
798 #endif
799 ) {
800  auto settings = CreateSettingsForFixture();
801  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;
802  auto end_frame_callback =
803  [&](bool should_resubmit_frame,
804  fml::RefPtr<fml::RasterThreadMerger> thread_merger) {
805  raster_thread_merger = thread_merger;
806  };
807  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
808  end_frame_callback, PostPrerollResult::kSuccess, true);
809 
810  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
811  false, external_view_embedder);
812 
813  // Create the surface needed by rasterizer
814  PlatformViewNotifyCreated(shell.get());
815 
816  auto configuration = RunConfiguration::InferFromSettings(settings);
817  configuration.SetEntrypoint("emptyMain");
818 
819  RunEngine(shell.get(), std::move(configuration));
820 
821  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
822  SkPictureRecorder recorder;
823  SkCanvas* recording_canvas =
824  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
825  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
826  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
827  auto sk_picture = recorder.finishRecordingAsPicture();
828  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
829  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
830  auto picture_layer = std::make_shared<PictureLayer>(
831  SkPoint::Make(10, 10),
832  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
833  root->Add(picture_layer);
834  };
835 
836  PumpOneFrame(shell.get(), 100, 100, builder);
837 
838  auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
839  ASSERT_TRUE(result.ok()) << "Result: " << static_cast<int>(result.code())
840  << ": " << result.message();
841 
842  ASSERT_TRUE(raster_thread_merger->IsEnabled());
843 
844  ValidateDestroyPlatformView(shell.get());
845  ASSERT_TRUE(raster_thread_merger->IsEnabled());
846 
847  // Validate the platform view can be recreated and destroyed again
848  ValidateShell(shell.get());
849  ASSERT_TRUE(raster_thread_merger->IsEnabled());
850  DestroyShell(std::move(shell));
851 }
852 
853 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
855 #if defined(OS_FUCHSIA)
856  DISABLED_OnPlatformViewDestroyAfterMergingThreads
857 #else
858  OnPlatformViewDestroyAfterMergingThreads
859 #endif
860 ) {
861  const int ThreadMergingLease = 10;
862  auto settings = CreateSettingsForFixture();
863  fml::AutoResetWaitableEvent end_frame_latch;
864  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
865 
866  auto end_frame_callback =
867  [&](bool should_resubmit_frame,
868  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
869  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
870  raster_thread_merger->MergeWithLease(ThreadMergingLease);
871 
872  ASSERT_TRUE(raster_thread_merger->IsMerged());
873  external_view_embedder->UpdatePostPrerollResult(
875  }
876  end_frame_latch.Signal();
877  };
878  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
879  end_frame_callback, PostPrerollResult::kSuccess, true);
880  // Set resubmit once to trigger thread merging.
881  external_view_embedder->UpdatePostPrerollResult(
883  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
884  false, external_view_embedder);
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 = [&](std::shared_ptr<ContainerLayer> root) {
895  SkPictureRecorder recorder;
896  SkCanvas* recording_canvas =
897  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
898  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
899  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
900  auto sk_picture = recorder.finishRecordingAsPicture();
901  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
902  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
903  auto picture_layer = std::make_shared<PictureLayer>(
904  SkPoint::Make(10, 10),
905  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
906  root->Add(picture_layer);
907  };
908 
909  PumpOneFrame(shell.get(), 100, 100, builder);
910  // Pump one frame to trigger thread merging.
911  end_frame_latch.Wait();
912  // Pump another frame to ensure threads are merged and a regular layer tree is
913  // submitted.
914  PumpOneFrame(shell.get(), 100, 100, builder);
915  // Threads are merged here. PlatformViewNotifyDestroy should be executed
916  // successfully.
918  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
919  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
920  ValidateDestroyPlatformView(shell.get());
921 
922  // Ensure threads are unmerged after platform view destroy
924  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
925  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
926 
927  // Validate the platform view can be recreated and destroyed again
928  ValidateShell(shell.get());
929 
930  DestroyShell(std::move(shell));
931 }
932 
933 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
935 #if defined(OS_FUCHSIA)
936  DISABLED_OnPlatformViewDestroyWhenThreadsAreMerging
937 #else
938  OnPlatformViewDestroyWhenThreadsAreMerging
939 #endif
940 ) {
941  const int kThreadMergingLease = 10;
942  auto settings = CreateSettingsForFixture();
943  fml::AutoResetWaitableEvent end_frame_latch;
944  auto end_frame_callback =
945  [&](bool should_resubmit_frame,
946  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
947  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
948  raster_thread_merger->MergeWithLease(kThreadMergingLease);
949  }
950  end_frame_latch.Signal();
951  };
952  // Start with a regular layer tree with `PostPrerollResult::kSuccess` so we
953  // can later check if the rasterizer is tore down using
954  // |ValidateDestroyPlatformView|
955  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
956  end_frame_callback, PostPrerollResult::kSuccess, true);
957 
958  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
959  false, external_view_embedder);
960 
961  // Create the surface needed by rasterizer
962  PlatformViewNotifyCreated(shell.get());
963 
964  auto configuration = RunConfiguration::InferFromSettings(settings);
965  configuration.SetEntrypoint("emptyMain");
966 
967  RunEngine(shell.get(), std::move(configuration));
968 
969  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
970  SkPictureRecorder recorder;
971  SkCanvas* recording_canvas =
972  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
973  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
974  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
975  auto sk_picture = recorder.finishRecordingAsPicture();
976  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
977  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
978  auto picture_layer = std::make_shared<PictureLayer>(
979  SkPoint::Make(10, 10),
980  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
981  root->Add(picture_layer);
982  };
983 
984  PumpOneFrame(shell.get(), 100, 100, builder);
985  // Pump one frame and threads aren't merged
986  end_frame_latch.Wait();
988  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
989  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
990 
991  // Pump a frame with `PostPrerollResult::kResubmitFrame` to start merging
992  // threads
993  external_view_embedder->UpdatePostPrerollResult(
995  PumpOneFrame(shell.get(), 100, 100, builder);
996 
997  // Now destroy the platform view immediately.
998  // Two things can happen here:
999  // 1. Threads haven't merged. 2. Threads has already merged.
1000  // |Shell:OnPlatformViewDestroy| should be able to handle both cases.
1001  ValidateDestroyPlatformView(shell.get());
1002 
1003  // Ensure threads are unmerged after platform view destroy
1005  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1006  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1007 
1008  // Validate the platform view can be recreated and destroyed again
1009  ValidateShell(shell.get());
1010 
1011  DestroyShell(std::move(shell));
1012 }
1013 
1014 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1016 #if defined(OS_FUCHSIA)
1017  DISABLED_OnPlatformViewDestroyWithThreadMergerWhileThreadsAreUnmerged
1018 #else
1019  OnPlatformViewDestroyWithThreadMergerWhileThreadsAreUnmerged
1020 #endif
1021 ) {
1022  auto settings = CreateSettingsForFixture();
1023  fml::AutoResetWaitableEvent end_frame_latch;
1024  auto end_frame_callback =
1025  [&](bool should_resubmit_frame,
1026  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1027  end_frame_latch.Signal();
1028  };
1029  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1030  end_frame_callback, PostPrerollResult::kSuccess, true);
1031  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1032  false, external_view_embedder);
1033 
1034  // Create the surface needed by rasterizer
1035  PlatformViewNotifyCreated(shell.get());
1036 
1037  auto configuration = RunConfiguration::InferFromSettings(settings);
1038  configuration.SetEntrypoint("emptyMain");
1039 
1040  RunEngine(shell.get(), std::move(configuration));
1041 
1042  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
1043  SkPictureRecorder recorder;
1044  SkCanvas* recording_canvas =
1045  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
1046  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
1047  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1048  auto sk_picture = recorder.finishRecordingAsPicture();
1049  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1050  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
1051  auto picture_layer = std::make_shared<PictureLayer>(
1052  SkPoint::Make(10, 10),
1053  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
1054  root->Add(picture_layer);
1055  };
1056  PumpOneFrame(shell.get(), 100, 100, builder);
1057  end_frame_latch.Wait();
1058 
1059  // Threads should not be merged.
1061  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1062  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1063  ValidateDestroyPlatformView(shell.get());
1064 
1065  // Ensure threads are unmerged after platform view destroy
1067  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1068  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1069 
1070  // Validate the platform view can be recreated and destroyed again
1071  ValidateShell(shell.get());
1072 
1073  DestroyShell(std::move(shell));
1074 }
1075 
1076 TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) {
1077  auto settings = CreateSettingsForFixture();
1078 
1079  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1080  false, nullptr);
1081 
1082  // Create the surface needed by rasterizer
1083  PlatformViewNotifyCreated(shell.get());
1084 
1085  auto configuration = RunConfiguration::InferFromSettings(settings);
1086  configuration.SetEntrypoint("emptyMain");
1087 
1088  RunEngine(shell.get(), std::move(configuration));
1089 
1090  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
1091  SkPictureRecorder recorder;
1092  SkCanvas* recording_canvas =
1093  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
1094  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
1095  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1096  auto sk_picture = recorder.finishRecordingAsPicture();
1097  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1098  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
1099  auto picture_layer = std::make_shared<PictureLayer>(
1100  SkPoint::Make(10, 10),
1101  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
1102  root->Add(picture_layer);
1103  };
1104  PumpOneFrame(shell.get(), 100, 100, builder);
1105 
1106  // Threads should not be merged.
1108  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1109  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1110  ValidateDestroyPlatformView(shell.get());
1111 
1112  // Ensure threads are unmerged after platform view destroy
1114  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
1115  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
1116 
1117  // Validate the platform view can be recreated and destroyed again
1118  ValidateShell(shell.get());
1119 
1120  DestroyShell(std::move(shell));
1121 }
1122 
1123 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1125 #if defined(OS_FUCHSIA)
1126  DISABLED_OnPlatformViewDestroyWithStaticThreadMerging
1127 #else
1128  OnPlatformViewDestroyWithStaticThreadMerging
1129 #endif
1130 ) {
1131  auto settings = CreateSettingsForFixture();
1132  fml::AutoResetWaitableEvent end_frame_latch;
1133  auto end_frame_callback =
1134  [&](bool should_resubmit_frame,
1135  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1136  end_frame_latch.Signal();
1137  };
1138  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1139  end_frame_callback, PostPrerollResult::kSuccess, true);
1140  ThreadHost thread_host(
1141  "io.flutter.test." + GetCurrentTestName() + ".",
1142  ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI);
1143  TaskRunners task_runners(
1144  "test",
1145  thread_host.platform_thread->GetTaskRunner(), // platform
1146  thread_host.platform_thread->GetTaskRunner(), // raster
1147  thread_host.ui_thread->GetTaskRunner(), // ui
1148  thread_host.io_thread->GetTaskRunner() // io
1149  );
1150  auto shell = CreateShell(std::move(settings), std::move(task_runners), false,
1151  external_view_embedder);
1152 
1153  // Create the surface needed by rasterizer
1154  PlatformViewNotifyCreated(shell.get());
1155 
1156  auto configuration = RunConfiguration::InferFromSettings(settings);
1157  configuration.SetEntrypoint("emptyMain");
1158 
1159  RunEngine(shell.get(), std::move(configuration));
1160 
1161  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
1162  SkPictureRecorder recorder;
1163  SkCanvas* recording_canvas =
1164  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
1165  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
1166  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1167  auto sk_picture = recorder.finishRecordingAsPicture();
1168  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1169  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
1170  auto picture_layer = std::make_shared<PictureLayer>(
1171  SkPoint::Make(10, 10),
1172  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
1173  root->Add(picture_layer);
1174  };
1175  PumpOneFrame(shell.get(), 100, 100, builder);
1176  end_frame_latch.Wait();
1177 
1178  ValidateDestroyPlatformView(shell.get());
1179 
1180  // Validate the platform view can be recreated and destroyed again
1181  ValidateShell(shell.get());
1182 
1183  DestroyShell(std::move(shell), std::move(task_runners));
1184 }
1185 
1186 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1187 // TODO(https://github.com/flutter/flutter/issues/66056): Deflake on all other
1188 // platforms
1190 #if defined(OS_FUCHSIA)
1191  DISABLED_SkipAndSubmitFrame
1192 #else
1193  DISABLED_SkipAndSubmitFrame
1194 #endif
1195 ) {
1196  auto settings = CreateSettingsForFixture();
1197  fml::AutoResetWaitableEvent end_frame_latch;
1198  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1199 
1200  auto end_frame_callback =
1201  [&](bool should_resubmit_frame,
1202  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1203  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1204  raster_thread_merger->MergeWithLease(10);
1205  external_view_embedder->UpdatePostPrerollResult(
1207  }
1208  end_frame_latch.Signal();
1209  };
1210  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1211  end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true);
1212 
1213  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1214  false, external_view_embedder);
1215 
1216  PlatformViewNotifyCreated(shell.get());
1217 
1218  auto configuration = RunConfiguration::InferFromSettings(settings);
1219  configuration.SetEntrypoint("emptyMain");
1220  RunEngine(shell.get(), std::move(configuration));
1221 
1222  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1223 
1224  PumpOneFrame(shell.get());
1225 
1226  // `EndFrame` changed the post preroll result to `kSuccess`.
1227  end_frame_latch.Wait();
1228 
1229  // Let the resubmitted frame to run and `GetSubmittedFrameCount` should be
1230  // called.
1231  end_frame_latch.Wait();
1232  // 2 frames are submitted because `kSkipAndRetryFrame`, but only the 2nd frame
1233  // should be submitted with `external_view_embedder`, hence the below check.
1234  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1235 
1236  PlatformViewNotifyDestroyed(shell.get());
1237  DestroyShell(std::move(shell));
1238 }
1239 
1240 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1242 #if defined(OS_FUCHSIA)
1243  DISABLED_ResubmitFrame
1244 #else
1245  ResubmitFrame
1246 #endif
1247 ) {
1248  auto settings = CreateSettingsForFixture();
1249  fml::AutoResetWaitableEvent end_frame_latch;
1250  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1251  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
1252  auto end_frame_callback =
1253  [&](bool should_resubmit_frame,
1254  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1255  if (!raster_thread_merger_ref) {
1256  raster_thread_merger_ref = raster_thread_merger;
1257  }
1258  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1259  raster_thread_merger->MergeWithLease(10);
1260  external_view_embedder->UpdatePostPrerollResult(
1262  }
1263  end_frame_latch.Signal();
1264  };
1265  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1266  end_frame_callback, PostPrerollResult::kResubmitFrame, true);
1267 
1268  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1269  false, external_view_embedder);
1270  PlatformViewNotifyCreated(shell.get());
1271 
1272  auto configuration = RunConfiguration::InferFromSettings(settings);
1273  configuration.SetEntrypoint("emptyMain");
1274  RunEngine(shell.get(), std::move(configuration));
1275 
1276  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1277 
1278  PumpOneFrame(shell.get());
1279  // `EndFrame` changed the post preroll result to `kSuccess` and merged the
1280  // threads. During the frame, the threads are not merged, So no
1281  // `external_view_embedder->GetSubmittedFrameCount()` is called.
1282  end_frame_latch.Wait();
1283  ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
1284 
1285  // This is the resubmitted frame, which threads are also merged.
1286  end_frame_latch.Wait();
1287  // 2 frames are submitted because `kResubmitFrame`, but only the 2nd frame
1288  // should be submitted with `external_view_embedder`, hence the below check.
1289  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1290 
1291  PlatformViewNotifyDestroyed(shell.get());
1292  DestroyShell(std::move(shell));
1293 }
1294 
1295 TEST(SettingsTest, FrameTimingSetsAndGetsProperly) {
1296  // Ensure that all phases are in kPhases.
1297  ASSERT_EQ(sizeof(FrameTiming::kPhases),
1299 
1300  int lastPhaseIndex = -1;
1301  FrameTiming timing;
1302  for (auto phase : FrameTiming::kPhases) {
1303  ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in order.
1304  lastPhaseIndex = phase;
1305  auto fake_time =
1307  timing.Set(phase, fake_time);
1308  ASSERT_TRUE(timing.Get(phase) == fake_time);
1309  }
1310 }
1311 
1312 TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) {
1313  auto settings = CreateSettingsForFixture();
1314  std::unique_ptr<Shell> shell = CreateShell(settings);
1315 
1316  // Create the surface needed by rasterizer
1317  PlatformViewNotifyCreated(shell.get());
1318 
1319  auto configuration = RunConfiguration::InferFromSettings(settings);
1320  ASSERT_TRUE(configuration.IsValid());
1321  configuration.SetEntrypoint("reportTimingsMain");
1322  fml::AutoResetWaitableEvent reportLatch;
1323  std::vector<int64_t> timestamps;
1324  auto nativeTimingCallback = [&reportLatch,
1325  &timestamps](Dart_NativeArguments args) {
1326  Dart_Handle exception = nullptr;
1327  ASSERT_EQ(timestamps.size(), 0ul);
1328  timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
1329  args, 0, exception);
1330  reportLatch.Signal();
1331  };
1332  AddNativeCallback("NativeReportTimingsCallback",
1333  CREATE_NATIVE_ENTRY(nativeTimingCallback));
1334  ASSERT_TRUE(configuration.IsValid());
1335  RunEngine(shell.get(), std::move(configuration));
1336 
1337  for (int i = 0; i < 10; i += 1) {
1338  PumpOneFrame(shell.get());
1339  }
1340 
1341  reportLatch.Wait();
1342  DestroyShell(std::move(shell));
1343 
1344  // Check for the immediate callback of the first frame that doesn't wait for
1345  // the other 9 frames to be rasterized.
1346  ASSERT_EQ(timestamps.size(), FrameTiming::kCount);
1347 }
1348 
1349 TEST_F(ShellTest, ReloadSystemFonts) {
1350  auto settings = CreateSettingsForFixture();
1351 
1353  auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
1354  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1355  task_runner);
1356  auto shell = CreateShell(std::move(settings), std::move(task_runners));
1357 
1358  auto fontCollection = GetFontCollection(shell.get());
1359  std::vector<std::string> families(1, "Robotofake");
1360  auto font =
1361  fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1362  if (font == nullptr) {
1363  // The system does not have default font. Aborts this test.
1364  return;
1365  }
1366  unsigned int id = font->getId();
1367  // The result should be cached.
1368  font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1369  ASSERT_EQ(font->getId(), id);
1370  bool result = shell->ReloadSystemFonts();
1371 
1372  // The cache is cleared, and FontCollection will be assigned a new id.
1373  font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1374  ASSERT_NE(font->getId(), id);
1375  ASSERT_TRUE(result);
1376  shell.reset();
1377 }
1378 
1380 #if defined(WINUWP)
1381  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
1382  DISABLED_WaitForFirstFrame
1383 #else
1384  WaitForFirstFrame
1385 #endif
1386 ) {
1387  auto settings = CreateSettingsForFixture();
1388  std::unique_ptr<Shell> shell = CreateShell(settings);
1389 
1390  // Create the surface needed by rasterizer
1391  PlatformViewNotifyCreated(shell.get());
1392 
1393  auto configuration = RunConfiguration::InferFromSettings(settings);
1394  configuration.SetEntrypoint("emptyMain");
1395 
1396  RunEngine(shell.get(), std::move(configuration));
1397  PumpOneFrame(shell.get());
1398  fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1399  ASSERT_TRUE(result.ok());
1400 
1401  DestroyShell(std::move(shell));
1402 }
1403 
1404 TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) {
1405  auto settings = CreateSettingsForFixture();
1406  std::unique_ptr<Shell> shell = CreateShell(settings);
1407 
1408  // Create the surface needed by rasterizer
1409  PlatformViewNotifyCreated(shell.get());
1410 
1411  auto configuration = RunConfiguration::InferFromSettings(settings);
1412  configuration.SetEntrypoint("emptyMain");
1413 
1414  RunEngine(shell.get(), std::move(configuration));
1415  PumpOneFrame(shell.get(), {1.0, 0.0, 0.0, 22});
1416  fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1417  ASSERT_FALSE(result.ok());
1418  ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1419 
1420  DestroyShell(std::move(shell));
1421 }
1422 
1423 TEST_F(ShellTest, WaitForFirstFrameTimeout) {
1424  auto settings = CreateSettingsForFixture();
1425  std::unique_ptr<Shell> shell = CreateShell(settings);
1426 
1427  // Create the surface needed by rasterizer
1428  PlatformViewNotifyCreated(shell.get());
1429 
1430  auto configuration = RunConfiguration::InferFromSettings(settings);
1431  configuration.SetEntrypoint("emptyMain");
1432 
1433  RunEngine(shell.get(), std::move(configuration));
1434  fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1435  ASSERT_FALSE(result.ok());
1436  ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1437 
1438  DestroyShell(std::move(shell));
1439 }
1440 
1442 #if defined(WINUWP)
1443  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
1444  DISABLED_WaitForFirstFrameMultiple
1445 #else
1446  WaitForFirstFrameMultiple
1447 #endif // defined(WINUWP)
1448 ) {
1449 
1450  auto settings = CreateSettingsForFixture();
1451  std::unique_ptr<Shell> shell = CreateShell(settings);
1452 
1453  // Create the surface needed by rasterizer
1454  PlatformViewNotifyCreated(shell.get());
1455 
1456  auto configuration = RunConfiguration::InferFromSettings(settings);
1457  configuration.SetEntrypoint("emptyMain");
1458 
1459  RunEngine(shell.get(), std::move(configuration));
1460  PumpOneFrame(shell.get());
1461  fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1462  ASSERT_TRUE(result.ok());
1463  for (int i = 0; i < 100; ++i) {
1464  result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
1465  ASSERT_TRUE(result.ok());
1466  }
1467 
1468  DestroyShell(std::move(shell));
1469 }
1470 
1471 /// Makes sure that WaitForFirstFrame works if we rendered a frame with the
1472 /// single-thread setup.
1474 #if defined(WINUWP)
1475  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
1476  DISABLED_WaitForFirstFrameInlined
1477 #else
1478  WaitForFirstFrameInlined
1479 #endif // defined(WINUWP)
1480 ) {
1481 
1482  Settings settings = CreateSettingsForFixture();
1483  auto task_runner = CreateNewThread();
1484  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1485  task_runner);
1486  std::unique_ptr<Shell> shell =
1487  CreateShell(std::move(settings), std::move(task_runners));
1488 
1489  // Create the surface needed by rasterizer
1490  PlatformViewNotifyCreated(shell.get());
1491 
1492  auto configuration = RunConfiguration::InferFromSettings(settings);
1493  configuration.SetEntrypoint("emptyMain");
1494 
1495  RunEngine(shell.get(), std::move(configuration));
1496  PumpOneFrame(shell.get());
1498  task_runner->PostTask([&shell, &event] {
1499  fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
1500  ASSERT_FALSE(result.ok());
1501  ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition);
1502  event.Signal();
1503  });
1504  ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::Max()));
1505 
1506  DestroyShell(std::move(shell), std::move(task_runners));
1507 }
1508 
1509 static size_t GetRasterizerResourceCacheBytesSync(const Shell& shell) {
1510  size_t bytes = 0;
1513  shell.GetTaskRunners().GetRasterTaskRunner(), [&]() {
1514  if (auto rasterizer = shell.GetRasterizer()) {
1515  bytes = rasterizer->GetResourceCacheMaxBytes().value_or(0U);
1516  }
1517  latch.Signal();
1518  });
1519  latch.Wait();
1520  return bytes;
1521 }
1522 
1523 TEST_F(ShellTest, SetResourceCacheSize) {
1524  Settings settings = CreateSettingsForFixture();
1525  auto task_runner = CreateNewThread();
1526  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1527  task_runner);
1528  std::unique_ptr<Shell> shell =
1529  CreateShell(std::move(settings), std::move(task_runners));
1530 
1531  // Create the surface needed by rasterizer
1532  PlatformViewNotifyCreated(shell.get());
1533 
1534  auto configuration = RunConfiguration::InferFromSettings(settings);
1535  configuration.SetEntrypoint("emptyMain");
1536 
1537  RunEngine(shell.get(), std::move(configuration));
1538  PumpOneFrame(shell.get());
1539 
1540  // The Vulkan and GL backends set different default values for the resource
1541  // cache size.
1542 #ifdef SHELL_ENABLE_VULKAN
1543  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1545 #else
1546  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1547  static_cast<size_t>(24 * (1 << 20)));
1548 #endif
1549 
1551  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1552  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22});
1553  });
1554  PumpOneFrame(shell.get());
1555 
1556  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 3840000U);
1557 
1558  std::string request_json = R"json({
1559  "method": "Skia.setResourceCacheMaxBytes",
1560  "args": 10000
1561  })json";
1562  auto data =
1563  fml::MallocMapping::Copy(request_json.c_str(), request_json.length());
1564  auto platform_message = std::make_unique<PlatformMessage>(
1565  "flutter/skia", std::move(data), nullptr);
1566  SendEnginePlatformMessage(shell.get(), std::move(platform_message));
1567  PumpOneFrame(shell.get());
1568  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1569 
1571  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1572  shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 400, 22});
1573  });
1574  PumpOneFrame(shell.get());
1575 
1576  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1577  DestroyShell(std::move(shell), std::move(task_runners));
1578 }
1579 
1580 TEST_F(ShellTest, SetResourceCacheSizeEarly) {
1581  Settings settings = CreateSettingsForFixture();
1582  auto task_runner = CreateNewThread();
1583  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1584  task_runner);
1585  std::unique_ptr<Shell> shell =
1586  CreateShell(std::move(settings), std::move(task_runners));
1587 
1589  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1590  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22});
1591  });
1592  PumpOneFrame(shell.get());
1593 
1594  // Create the surface needed by rasterizer
1595  PlatformViewNotifyCreated(shell.get());
1596 
1597  auto configuration = RunConfiguration::InferFromSettings(settings);
1598  configuration.SetEntrypoint("emptyMain");
1599 
1600  RunEngine(shell.get(), std::move(configuration));
1601  PumpOneFrame(shell.get());
1602 
1603  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1604  static_cast<size_t>(3840000U));
1605  DestroyShell(std::move(shell), std::move(task_runners));
1606 }
1607 
1608 TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) {
1609  Settings settings = CreateSettingsForFixture();
1610  auto task_runner = CreateNewThread();
1611  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1612  task_runner);
1613  std::unique_ptr<Shell> shell =
1614  CreateShell(std::move(settings), std::move(task_runners));
1615 
1617  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1618  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22});
1619  });
1620  PumpOneFrame(shell.get());
1621 
1622  // Create the surface needed by rasterizer
1623  PlatformViewNotifyCreated(shell.get());
1624 
1625  auto configuration = RunConfiguration::InferFromSettings(settings);
1626  configuration.SetEntrypoint("testSkiaResourceCacheSendsResponse");
1627 
1628  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1629  static_cast<size_t>(3840000U));
1630 
1632  AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
1633  latch.Signal();
1634  }));
1635 
1636  RunEngine(shell.get(), std::move(configuration));
1637  PumpOneFrame(shell.get());
1638 
1639  latch.Wait();
1640 
1641  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1642  static_cast<size_t>(10000U));
1643  DestroyShell(std::move(shell), std::move(task_runners));
1644 }
1645 
1646 TEST_F(ShellTest, CanCreateImagefromDecompressedBytes) {
1647  Settings settings = CreateSettingsForFixture();
1648  auto task_runner = CreateNewThread();
1649 
1650  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1651  task_runner);
1652 
1653  std::unique_ptr<Shell> shell =
1654  CreateShell(std::move(settings), std::move(task_runners));
1655 
1656  // Create the surface needed by rasterizer
1657  PlatformViewNotifyCreated(shell.get());
1658 
1659  auto configuration = RunConfiguration::InferFromSettings(settings);
1660  configuration.SetEntrypoint("canCreateImageFromDecompressedData");
1661 
1663  AddNativeCallback("NotifyWidthHeight",
1664  CREATE_NATIVE_ENTRY([&latch](auto args) {
1666  Dart_GetNativeArgument(args, 0));
1668  Dart_GetNativeArgument(args, 1));
1669  ASSERT_EQ(width, 10);
1670  ASSERT_EQ(height, 10);
1671  latch.Signal();
1672  }));
1673 
1674  RunEngine(shell.get(), std::move(configuration));
1675 
1676  latch.Wait();
1677  DestroyShell(std::move(shell), std::move(task_runners));
1678 }
1679 
1680 class MockTexture : public Texture {
1681  public:
1682  MockTexture(int64_t textureId,
1683  std::shared_ptr<fml::AutoResetWaitableEvent> latch)
1684  : Texture(textureId), latch_(latch) {}
1685 
1686  ~MockTexture() override = default;
1687 
1688  // Called from raster thread.
1689  void Paint(SkCanvas& canvas,
1690  const SkRect& bounds,
1691  bool freeze,
1692  GrDirectContext* context,
1693  const SkSamplingOptions&) override {}
1694 
1695  void OnGrContextCreated() override {}
1696 
1697  void OnGrContextDestroyed() override {}
1698 
1699  void MarkNewFrameAvailable() override {
1700  frames_available_++;
1701  latch_->Signal();
1702  }
1703 
1704  void OnTextureUnregistered() override {
1705  unregistered_ = true;
1706  latch_->Signal();
1707  }
1708 
1709  bool unregistered() { return unregistered_; }
1710  int frames_available() { return frames_available_; }
1711 
1712  private:
1713  bool unregistered_ = false;
1714  int frames_available_ = 0;
1715  std::shared_ptr<fml::AutoResetWaitableEvent> latch_;
1716 };
1717 
1718 TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) {
1719  Settings settings = CreateSettingsForFixture();
1720  auto configuration = RunConfiguration::InferFromSettings(settings);
1721  auto task_runner = CreateNewThread();
1722  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1723  task_runner);
1724  std::unique_ptr<Shell> shell =
1725  CreateShell(std::move(settings), std::move(task_runners));
1726 
1727  ASSERT_TRUE(ValidateShell(shell.get()));
1728  PlatformViewNotifyCreated(shell.get());
1729 
1730  RunEngine(shell.get(), std::move(configuration));
1731 
1732  std::shared_ptr<fml::AutoResetWaitableEvent> latch =
1733  std::make_shared<fml::AutoResetWaitableEvent>();
1734 
1735  std::shared_ptr<MockTexture> mockTexture =
1736  std::make_shared<MockTexture>(0, latch);
1737 
1739  shell->GetTaskRunners().GetRasterTaskRunner(), [&]() {
1740  shell->GetPlatformView()->RegisterTexture(mockTexture);
1741  shell->GetPlatformView()->MarkTextureFrameAvailable(0);
1742  });
1743  latch->Wait();
1744 
1745  EXPECT_EQ(mockTexture->frames_available(), 1);
1746 
1748  shell->GetTaskRunners().GetRasterTaskRunner(),
1749  [&]() { shell->GetPlatformView()->UnregisterTexture(0); });
1750  latch->Wait();
1751 
1752  EXPECT_EQ(mockTexture->unregistered(), true);
1753  DestroyShell(std::move(shell), std::move(task_runners));
1754 }
1755 
1756 TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) {
1757  const std::string message = "dummy isolate launch data.";
1758 
1759  Settings settings = CreateSettingsForFixture();
1760  settings.persistent_isolate_data =
1761  std::make_shared<fml::DataMapping>(message);
1762  TaskRunners task_runners("test", // label
1763  GetCurrentTaskRunner(), // platform
1764  CreateNewThread(), // raster
1765  CreateNewThread(), // ui
1766  CreateNewThread() // io
1767  );
1768 
1769  fml::AutoResetWaitableEvent message_latch;
1770  AddNativeCallback("NotifyMessage",
1771  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1772  const auto message_from_dart =
1774  Dart_GetNativeArgument(args, 0));
1775  ASSERT_EQ(message, message_from_dart);
1776  message_latch.Signal();
1777  }));
1778 
1779  std::unique_ptr<Shell> shell =
1780  CreateShell(std::move(settings), std::move(task_runners));
1781 
1782  ASSERT_TRUE(shell->IsSetup());
1783  auto configuration = RunConfiguration::InferFromSettings(settings);
1784  configuration.SetEntrypoint("canAccessIsolateLaunchData");
1785 
1787  shell->RunEngine(std::move(configuration), [&](auto result) {
1788  ASSERT_EQ(result, Engine::RunStatus::Success);
1789  });
1790 
1791  message_latch.Wait();
1792  DestroyShell(std::move(shell), std::move(task_runners));
1793 }
1794 
1795 static void LogSkData(sk_sp<SkData> data, const char* title) {
1796  FML_LOG(ERROR) << "---------- " << title;
1797  std::ostringstream ostr;
1798  for (size_t i = 0; i < data->size();) {
1799  ostr << std::hex << std::setfill('0') << std::setw(2)
1800  << static_cast<int>(data->bytes()[i]) << " ";
1801  i++;
1802  if (i % 16 == 0 || i == data->size()) {
1803  FML_LOG(ERROR) << ostr.str();
1804  ostr.str("");
1805  ostr.clear();
1806  }
1807  }
1808 }
1809 
1810 TEST_F(ShellTest, Screenshot) {
1811  auto settings = CreateSettingsForFixture();
1812  fml::AutoResetWaitableEvent firstFrameLatch;
1813  settings.frame_rasterized_callback =
1814  [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); };
1815 
1816  std::unique_ptr<Shell> shell = CreateShell(settings);
1817 
1818  // Create the surface needed by rasterizer
1819  PlatformViewNotifyCreated(shell.get());
1820 
1821  auto configuration = RunConfiguration::InferFromSettings(settings);
1822  configuration.SetEntrypoint("emptyMain");
1823 
1824  RunEngine(shell.get(), std::move(configuration));
1825 
1826  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
1827  SkPictureRecorder recorder;
1828  SkCanvas* recording_canvas =
1829  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
1830  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
1831  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1832  auto sk_picture = recorder.finishRecordingAsPicture();
1833  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1834  this->GetCurrentTaskRunner(), fml::TimeDelta::Zero());
1835  auto picture_layer = std::make_shared<PictureLayer>(
1836  SkPoint::Make(10, 10),
1837  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
1838  root->Add(picture_layer);
1839  };
1840 
1841  PumpOneFrame(shell.get(), 100, 100, builder);
1842  firstFrameLatch.Wait();
1843 
1844  std::promise<Rasterizer::Screenshot> screenshot_promise;
1845  auto screenshot_future = screenshot_promise.get_future();
1846 
1848  shell->GetTaskRunners().GetRasterTaskRunner(),
1849  [&screenshot_promise, &shell]() {
1850  auto rasterizer = shell->GetRasterizer();
1851  screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree(
1853  });
1854 
1855  auto fixtures_dir =
1857 
1858  auto reference_png = fml::FileMapping::CreateReadOnly(
1859  fixtures_dir, "shelltest_screenshot.png");
1860 
1861  // Use MakeWithoutCopy instead of MakeWithCString because we don't want to
1862  // encode the null sentinel
1863  sk_sp<SkData> reference_data = SkData::MakeWithoutCopy(
1864  reference_png->GetMapping(), reference_png->GetSize());
1865 
1866  sk_sp<SkData> screenshot_data = screenshot_future.get().data;
1867  if (!reference_data->equals(screenshot_data.get())) {
1868  LogSkData(reference_data, "reference");
1869  LogSkData(screenshot_data, "screenshot");
1870  ASSERT_TRUE(false);
1871  }
1872 
1873  DestroyShell(std::move(shell));
1874 }
1875 
1876 TEST_F(ShellTest, CanConvertToAndFromMappings) {
1877  const size_t buffer_size = 2 << 20;
1878 
1879  uint8_t* buffer = static_cast<uint8_t*>(::malloc(buffer_size));
1880  ASSERT_NE(buffer, nullptr);
1881  ASSERT_TRUE(MemsetPatternSetOrCheck(
1882  buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer));
1883 
1884  std::unique_ptr<fml::Mapping> mapping =
1885  std::make_unique<fml::MallocMapping>(buffer, buffer_size);
1886 
1887  ASSERT_EQ(mapping->GetSize(), buffer_size);
1888 
1890  AddNativeCallback(
1891  "SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1892  auto mapping_from_dart =
1894  Dart_GetNativeArgument(args, 0));
1895  ASSERT_NE(mapping_from_dart, nullptr);
1896  ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size);
1897  ASSERT_TRUE(MemsetPatternSetOrCheck(
1898  const_cast<uint8_t*>(mapping_from_dart->GetMapping()), // buffer
1899  mapping_from_dart->GetSize(), // size
1901  ));
1902  latch.Signal();
1903  }));
1904 
1905  AddNativeCallback(
1906  "GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1908  args, mapping);
1909  }));
1910 
1911  auto settings = CreateSettingsForFixture();
1912  auto configuration = RunConfiguration::InferFromSettings(settings);
1913  configuration.SetEntrypoint("canConvertMappings");
1914  std::unique_ptr<Shell> shell = CreateShell(settings);
1915  ASSERT_NE(shell.get(), nullptr);
1916  RunEngine(shell.get(), std::move(configuration));
1917  latch.Wait();
1918  DestroyShell(std::move(shell));
1919 }
1920 
1921 // Compares local times as seen by the dart isolate and as seen by this test
1922 // fixture, to a resolution of 1 hour.
1923 //
1924 // This verifies that (1) the isolate is able to get a timezone (doesn't lock
1925 // up for example), and (2) that the host and the isolate agree on what the
1926 // timezone is.
1927 TEST_F(ShellTest, LocaltimesMatch) {
1929  std::string dart_isolate_time_str;
1930 
1931  // See fixtures/shell_test.dart, the callback NotifyLocalTime is declared
1932  // there.
1933  AddNativeCallback("NotifyLocalTime", CREATE_NATIVE_ENTRY([&](auto args) {
1934  dart_isolate_time_str =
1936  Dart_GetNativeArgument(args, 0));
1937  latch.Signal();
1938  }));
1939 
1940  auto settings = CreateSettingsForFixture();
1941  auto configuration = RunConfiguration::InferFromSettings(settings);
1942  configuration.SetEntrypoint("localtimesMatch");
1943  std::unique_ptr<Shell> shell = CreateShell(settings);
1944  ASSERT_NE(shell.get(), nullptr);
1945  RunEngine(shell.get(), std::move(configuration));
1946  latch.Wait();
1947 
1948  char timestr[200];
1949  const time_t timestamp = time(nullptr);
1950  const struct tm* local_time = localtime(&timestamp);
1951  ASSERT_NE(local_time, nullptr)
1952  << "Could not get local time: errno=" << errno << ": " << strerror(errno);
1953  // Example: "2020-02-26 14" for 2pm on February 26, 2020.
1954  const size_t format_size =
1955  strftime(timestr, sizeof(timestr), "%Y-%m-%d %H", local_time);
1956  ASSERT_NE(format_size, 0UL)
1957  << "strftime failed: host time: " << std::string(timestr)
1958  << " dart isolate time: " << dart_isolate_time_str;
1959 
1960  const std::string host_local_time_str = timestr;
1961 
1962  ASSERT_EQ(dart_isolate_time_str, host_local_time_str)
1963  << "Local times in the dart isolate and the local time seen by the test "
1964  << "differ by more than 1 hour, but are expected to be about equal";
1965 
1966  DestroyShell(std::move(shell));
1967 }
1968 
1969 TEST_F(ShellTest, CanDecompressImageFromAsset) {
1971  AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
1973  Dart_GetNativeArgument(args, 0));
1975  Dart_GetNativeArgument(args, 1));
1976  ASSERT_EQ(width, 100);
1977  ASSERT_EQ(height, 100);
1978  latch.Signal();
1979  }));
1980 
1981  AddNativeCallback(
1982  "GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) {
1983  auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png");
1985  args, fixture);
1986  }));
1987 
1988  auto settings = CreateSettingsForFixture();
1989  auto configuration = RunConfiguration::InferFromSettings(settings);
1990  configuration.SetEntrypoint("canDecompressImageFromAsset");
1991  std::unique_ptr<Shell> shell = CreateShell(settings);
1992  ASSERT_NE(shell.get(), nullptr);
1993  RunEngine(shell.get(), std::move(configuration));
1994  latch.Wait();
1995  DestroyShell(std::move(shell));
1996 }
1997 
1998 /// An image generator that always creates a 1x1 single-frame green image.
2000  public:
2002  : info_(SkImageInfo::MakeN32(1, 1, SkAlphaType::kOpaque_SkAlphaType)){};
2003  ~SinglePixelImageGenerator() = default;
2004  const SkImageInfo& GetInfo() { return info_; }
2005 
2006  unsigned int GetFrameCount() const { return 1; }
2007 
2008  unsigned int GetPlayCount() const { return 1; }
2009 
2010  const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const {
2011  return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep};
2012  }
2013 
2014  SkISize GetScaledDimensions(float scale) {
2015  return SkISize::Make(info_.width(), info_.height());
2016  }
2017 
2018  bool GetPixels(const SkImageInfo& info,
2019  void* pixels,
2020  size_t row_bytes,
2021  unsigned int frame_index,
2022  std::optional<unsigned int> prior_frame) {
2023  assert(info.width() == 1);
2024  assert(info.height() == 1);
2025  assert(row_bytes == 4);
2026 
2027  reinterpret_cast<uint32_t*>(pixels)[0] = 0x00ff00ff;
2028  return true;
2029  };
2030 
2031  private:
2032  SkImageInfo info_;
2033 };
2034 
2035 TEST_F(ShellTest, CanRegisterImageDecoders) {
2037  AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
2039  Dart_GetNativeArgument(args, 0));
2041  Dart_GetNativeArgument(args, 1));
2042  ASSERT_EQ(width, 1);
2043  ASSERT_EQ(height, 1);
2044  latch.Signal();
2045  }));
2046 
2047  auto settings = CreateSettingsForFixture();
2048  auto configuration = RunConfiguration::InferFromSettings(settings);
2049  configuration.SetEntrypoint("canRegisterImageDecoders");
2050  std::unique_ptr<Shell> shell = CreateShell(settings);
2051  ASSERT_NE(shell.get(), nullptr);
2052 
2054  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
2055  shell->RegisterImageDecoder(
2056  [](sk_sp<SkData> buffer) {
2057  return std::make_unique<SinglePixelImageGenerator>();
2058  },
2059  100);
2060  });
2061 
2062  RunEngine(shell.get(), std::move(configuration));
2063  latch.Wait();
2064  DestroyShell(std::move(shell));
2065 }
2066 
2068 #if defined(WINUWP)
2069  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
2070  DISABLED_OnServiceProtocolGetSkSLsWorks
2071 #else
2072  OnServiceProtocolGetSkSLsWorks
2073 #endif // defined(WINUWP)
2074 ) {
2076  ASSERT_TRUE(base_dir.fd().is_valid());
2079 
2080  // Create 2 dummy SkSL cache file IE (base32 encoding of A), II (base32
2081  // encoding of B) with content x and y.
2082  std::vector<std::string> components = {
2083  "flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion(),
2085  auto sksl_dir = fml::CreateDirectory(base_dir.fd(), components,
2087  const std::string x_key_str = "A";
2088  const std::string x_value_str = "x";
2089  sk_sp<SkData> x_key =
2090  SkData::MakeWithCopy(x_key_str.data(), x_key_str.size());
2091  sk_sp<SkData> x_value =
2092  SkData::MakeWithCopy(x_value_str.data(), x_value_str.size());
2093  auto x_data = PersistentCache::BuildCacheObject(*x_key, *x_value);
2094 
2095  const std::string y_key_str = "B";
2096  const std::string y_value_str = "y";
2097  sk_sp<SkData> y_key =
2098  SkData::MakeWithCopy(y_key_str.data(), y_key_str.size());
2099  sk_sp<SkData> y_value =
2100  SkData::MakeWithCopy(y_value_str.data(), y_value_str.size());
2101  auto y_data = PersistentCache::BuildCacheObject(*y_key, *y_value);
2102 
2103  ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "x_cache", *x_data));
2104  ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "y_cache", *y_data));
2105 
2106  Settings settings = CreateSettingsForFixture();
2107  std::unique_ptr<Shell> shell = CreateShell(settings);
2109  rapidjson::Document document;
2110  OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs,
2111  shell->GetTaskRunners().GetIOTaskRunner(), empty_params,
2112  &document);
2113  rapidjson::StringBuffer buffer;
2114  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2115  document.Accept(writer);
2116  DestroyShell(std::move(shell));
2117 
2118  const std::string expected_json1 =
2119  "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}";
2120  const std::string expected_json2 =
2121  "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"IE\":\"eA==\",\"II\":\"eQ==\"}}";
2122  bool json_is_expected = (expected_json1 == buffer.GetString()) ||
2123  (expected_json2 == buffer.GetString());
2124  ASSERT_TRUE(json_is_expected) << buffer.GetString() << " is not equal to "
2125  << expected_json1 << " or " << expected_json2;
2126 }
2127 
2128 TEST_F(ShellTest, RasterizerScreenshot) {
2129  Settings settings = CreateSettingsForFixture();
2130  auto configuration = RunConfiguration::InferFromSettings(settings);
2131  auto task_runner = CreateNewThread();
2132  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2133  task_runner);
2134  std::unique_ptr<Shell> shell =
2135  CreateShell(std::move(settings), std::move(task_runners));
2136 
2137  ASSERT_TRUE(ValidateShell(shell.get()));
2138  PlatformViewNotifyCreated(shell.get());
2139 
2140  RunEngine(shell.get(), std::move(configuration));
2141 
2142  auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
2143 
2144  PumpOneFrame(shell.get());
2145 
2147  shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
2148  Rasterizer::Screenshot screenshot =
2149  shell->GetRasterizer()->ScreenshotLastLayerTree(
2151  EXPECT_NE(screenshot.data, nullptr);
2152 
2153  latch->Signal();
2154  });
2155  latch->Wait();
2156  DestroyShell(std::move(shell), std::move(task_runners));
2157 }
2158 
2159 TEST_F(ShellTest, RasterizerMakeRasterSnapshot) {
2160  Settings settings = CreateSettingsForFixture();
2161  auto configuration = RunConfiguration::InferFromSettings(settings);
2162  auto task_runner = CreateNewThread();
2163  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2164  task_runner);
2165  std::unique_ptr<Shell> shell =
2166  CreateShell(std::move(settings), std::move(task_runners));
2167 
2168  ASSERT_TRUE(ValidateShell(shell.get()));
2169  PlatformViewNotifyCreated(shell.get());
2170 
2171  RunEngine(shell.get(), std::move(configuration));
2172 
2173  auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
2174 
2175  PumpOneFrame(shell.get());
2176 
2178  shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
2179  SnapshotDelegate* delegate =
2180  reinterpret_cast<Rasterizer*>(shell->GetRasterizer().get());
2181  sk_sp<SkImage> image = delegate->MakeRasterSnapshot(
2182  SkPicture::MakePlaceholder({0, 0, 50, 50}), SkISize::Make(50, 50));
2183  EXPECT_NE(image, nullptr);
2184 
2185  latch->Signal();
2186  });
2187  latch->Wait();
2188  DestroyShell(std::move(shell), std::move(task_runners));
2189 }
2190 
2191 static sk_sp<SkPicture> MakeSizedPicture(int width, int height) {
2192  SkPictureRecorder recorder;
2193  SkCanvas* recording_canvas =
2194  recorder.beginRecording(SkRect::MakeXYWH(0, 0, width, height));
2195  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, width, height),
2196  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
2197  return recorder.finishRecordingAsPicture();
2198 }
2199 
2200 TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
2201  Settings settings = CreateSettingsForFixture();
2202  std::unique_ptr<Shell> shell = CreateShell(settings);
2203 
2204  // 1. Construct a picture and a picture layer to be raster cached.
2205  sk_sp<SkPicture> picture = MakeSizedPicture(10, 10);
2206  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
2207  GetCurrentTaskRunner(), fml::TimeDelta::Zero());
2208  auto picture_layer = std::make_shared<PictureLayer>(
2209  SkPoint::Make(0, 0),
2211  false, false);
2212  picture_layer->set_paint_bounds(SkRect::MakeWH(100, 100));
2213 
2214  // 2. Rasterize the picture and the picture layer in the raster cache.
2215  std::promise<bool> rasterized;
2216  shell->GetTaskRunners().GetRasterTaskRunner()->PostTask(
2217  [&shell, &rasterized, &picture, &picture_layer] {
2218  auto* compositor_context = shell->GetRasterizer()->compositor_context();
2219  auto& raster_cache = compositor_context->raster_cache();
2220 
2221  Stopwatch raster_time;
2222  Stopwatch ui_time;
2223  MutatorsStack mutators_stack;
2224  TextureRegistry texture_registry;
2225  PrerollContext preroll_context = {
2226  nullptr, /* raster_cache */
2227  nullptr, /* gr_context */
2228  nullptr, /* external_view_embedder */
2229  mutators_stack, nullptr, /* color_space */
2230  kGiantRect, /* cull_rect */
2231  false, /* layer reads from surface */
2232  raster_time, ui_time, texture_registry,
2233  false, /* checkerboard_offscreen_layers */
2234  1.0f, /* frame_device_pixel_ratio */
2235  false, /* has_platform_view */
2236  };
2237 
2238  // 2.1. Rasterize the picture. Call Draw multiple times to pass the
2239  // access threshold (default to 3) so a cache can be generated.
2240  SkCanvas dummy_canvas;
2241  bool picture_cache_generated;
2242  for (int i = 0; i < 4; i += 1) {
2243  SkMatrix matrix = SkMatrix::I();
2244 
2245  picture_cache_generated = raster_cache.Prepare(
2246  &preroll_context, picture.get(), true, false, matrix);
2247  raster_cache.Draw(*picture, dummy_canvas);
2248  }
2249  ASSERT_TRUE(picture_cache_generated);
2250 
2251  // 2.2. Rasterize the picture layer.
2252  raster_cache.Prepare(&preroll_context, picture_layer.get(),
2253  SkMatrix::I());
2254  rasterized.set_value(true);
2255  });
2256  rasterized.get_future().wait();
2257 
2258  // 3. Call the service protocol and check its output.
2260  rapidjson::Document document;
2261  OnServiceProtocol(
2262  shell.get(), ServiceProtocolEnum::kEstimateRasterCacheMemory,
2263  shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2264  rapidjson::StringBuffer buffer;
2265  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2266  document.Accept(writer);
2267  std::string expected_json =
2268  "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40000,\"picture"
2269  "Bytes\":400}";
2270  std::string actual_json = buffer.GetString();
2271  ASSERT_EQ(actual_json, expected_json);
2272 
2273  DestroyShell(std::move(shell));
2274 }
2275 
2276 TEST_F(ShellTest, DiscardLayerTreeOnResize) {
2277  auto settings = CreateSettingsForFixture();
2278 
2279  SkISize wrong_size = SkISize::Make(400, 100);
2280  SkISize expected_size = SkISize::Make(400, 200);
2281 
2282  fml::AutoResetWaitableEvent end_frame_latch;
2283  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
2284  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
2285  auto end_frame_callback =
2286  [&](bool should_merge_thread,
2287  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
2288  if (!raster_thread_merger_ref) {
2289  raster_thread_merger_ref = raster_thread_merger;
2290  }
2291  if (should_merge_thread) {
2292  // TODO(cyanglaz): This test used external_view_embedder so we need to
2293  // merge the threads here. However, the scenario it is testing is
2294  // unrelated to platform views. We should consider to update this test
2295  // so it doesn't require external_view_embedder.
2296  // https://github.com/flutter/flutter/issues/69895
2297  raster_thread_merger->MergeWithLease(10);
2298  external_view_embedder->UpdatePostPrerollResult(
2300  }
2301  end_frame_latch.Signal();
2302  };
2303 
2304  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2305  std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true);
2306 
2307  std::unique_ptr<Shell> shell = CreateShell(
2308  settings, GetTaskRunnersForFixture(), false, external_view_embedder);
2309 
2310  // Create the surface needed by rasterizer
2311  PlatformViewNotifyCreated(shell.get());
2312 
2314  shell->GetTaskRunners().GetPlatformTaskRunner(),
2315  [&shell, &expected_size]() {
2316  shell->GetPlatformView()->SetViewportMetrics(
2317  {1.0, static_cast<double>(expected_size.width()),
2318  static_cast<double>(expected_size.height()), 22});
2319  });
2320 
2321  auto configuration = RunConfiguration::InferFromSettings(settings);
2322  configuration.SetEntrypoint("emptyMain");
2323 
2324  RunEngine(shell.get(), std::move(configuration));
2325 
2326  PumpOneFrame(shell.get(), static_cast<double>(wrong_size.width()),
2327  static_cast<double>(wrong_size.height()));
2328 
2329  end_frame_latch.Wait();
2330 
2331  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2332 
2333  // Threads will be merged at the end of this frame.
2334  PumpOneFrame(shell.get(), static_cast<double>(expected_size.width()),
2335  static_cast<double>(expected_size.height()));
2336 
2337  end_frame_latch.Wait();
2338  ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
2339 
2340  end_frame_latch.Wait();
2341  // 2 frames are submitted because `kResubmitFrame`, but only the 2nd frame
2342  // should be submitted with `external_view_embedder`, hence the below check.
2343  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2344  ASSERT_EQ(expected_size, external_view_embedder->GetLastSubmittedFrameSize());
2345 
2346  PlatformViewNotifyDestroyed(shell.get());
2347  DestroyShell(std::move(shell));
2348 }
2349 
2350 TEST_F(ShellTest, DiscardResubmittedLayerTreeOnResize) {
2351  auto settings = CreateSettingsForFixture();
2352 
2353  SkISize origin_size = SkISize::Make(400, 100);
2354  SkISize new_size = SkISize::Make(400, 200);
2355 
2356  fml::AutoResetWaitableEvent end_frame_latch;
2357 
2358  fml::AutoResetWaitableEvent resize_latch;
2359 
2360  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
2361  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
2362  auto end_frame_callback =
2363  [&](bool should_merge_thread,
2364  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
2365  if (!raster_thread_merger_ref) {
2366  raster_thread_merger_ref = raster_thread_merger;
2367  }
2368  if (should_merge_thread) {
2369  raster_thread_merger->MergeWithLease(10);
2370  external_view_embedder->UpdatePostPrerollResult(
2372  }
2373  end_frame_latch.Signal();
2374 
2375  if (should_merge_thread) {
2376  resize_latch.Wait();
2377  }
2378  };
2379 
2380  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2381  std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true);
2382 
2383  std::unique_ptr<Shell> shell = CreateShell(
2384  settings, GetTaskRunnersForFixture(), false, external_view_embedder);
2385 
2386  // Create the surface needed by rasterizer
2387  PlatformViewNotifyCreated(shell.get());
2388 
2390  shell->GetTaskRunners().GetPlatformTaskRunner(),
2391  [&shell, &origin_size]() {
2392  shell->GetPlatformView()->SetViewportMetrics(
2393  {1.0, static_cast<double>(origin_size.width()),
2394  static_cast<double>(origin_size.height()), 22});
2395  });
2396 
2397  auto configuration = RunConfiguration::InferFromSettings(settings);
2398  configuration.SetEntrypoint("emptyMain");
2399 
2400  RunEngine(shell.get(), std::move(configuration));
2401 
2402  PumpOneFrame(shell.get(), static_cast<double>(origin_size.width()),
2403  static_cast<double>(origin_size.height()));
2404 
2405  end_frame_latch.Wait();
2406  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2407 
2409  shell->GetTaskRunners().GetPlatformTaskRunner(),
2410  [&shell, &new_size, &resize_latch]() {
2411  shell->GetPlatformView()->SetViewportMetrics(
2412  {1.0, static_cast<double>(new_size.width()),
2413  static_cast<double>(new_size.height()), 22});
2414  resize_latch.Signal();
2415  });
2416 
2417  end_frame_latch.Wait();
2418 
2419  // The frame resubmitted with origin size should be discarded after the
2420  // viewport metrics changed.
2421  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2422 
2423  // Threads will be merged at the end of this frame.
2424  PumpOneFrame(shell.get(), static_cast<double>(new_size.width()),
2425  static_cast<double>(new_size.height()));
2426 
2427  end_frame_latch.Wait();
2428  ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
2429  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2430  ASSERT_EQ(new_size, external_view_embedder->GetLastSubmittedFrameSize());
2431 
2432  PlatformViewNotifyDestroyed(shell.get());
2433  DestroyShell(std::move(shell));
2434 }
2435 
2436 TEST_F(ShellTest, IgnoresInvalidMetrics) {
2438  double last_device_pixel_ratio;
2439  double last_width;
2440  double last_height;
2441  auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2442  auto dpr_handle = Dart_GetNativeArgument(args, 0);
2443  ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2444  Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2445  ASSERT_FALSE(last_device_pixel_ratio == 0.0);
2446 
2447  auto width_handle = Dart_GetNativeArgument(args, 1);
2448  ASSERT_TRUE(Dart_IsDouble(width_handle));
2449  Dart_DoubleValue(width_handle, &last_width);
2450  ASSERT_FALSE(last_width == 0.0);
2451 
2452  auto height_handle = Dart_GetNativeArgument(args, 2);
2453  ASSERT_TRUE(Dart_IsDouble(height_handle));
2454  Dart_DoubleValue(height_handle, &last_height);
2455  ASSERT_FALSE(last_height == 0.0);
2456 
2457  latch.Signal();
2458  };
2459 
2460  Settings settings = CreateSettingsForFixture();
2461  auto task_runner = CreateNewThread();
2462  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2463  task_runner);
2464 
2465  AddNativeCallback("ReportMetrics",
2466  CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2467 
2468  std::unique_ptr<Shell> shell =
2469  CreateShell(std::move(settings), std::move(task_runners));
2470 
2471  auto configuration = RunConfiguration::InferFromSettings(settings);
2472  configuration.SetEntrypoint("reportMetrics");
2473 
2474  RunEngine(shell.get(), std::move(configuration));
2475 
2476  task_runner->PostTask([&]() {
2477  shell->GetPlatformView()->SetViewportMetrics({0.0, 400, 200, 22});
2478  task_runner->PostTask([&]() {
2479  shell->GetPlatformView()->SetViewportMetrics({0.8, 0.0, 200, 22});
2480  task_runner->PostTask([&]() {
2481  shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 0.0, 22});
2482  task_runner->PostTask([&]() {
2483  shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 200.0, 22});
2484  });
2485  });
2486  });
2487  });
2488  latch.Wait();
2489  ASSERT_EQ(last_device_pixel_ratio, 0.8);
2490  ASSERT_EQ(last_width, 400.0);
2491  ASSERT_EQ(last_height, 200.0);
2492  latch.Reset();
2493 
2494  task_runner->PostTask([&]() {
2495  shell->GetPlatformView()->SetViewportMetrics({1.2, 600, 300, 22});
2496  });
2497  latch.Wait();
2498  ASSERT_EQ(last_device_pixel_ratio, 1.2);
2499  ASSERT_EQ(last_width, 600.0);
2500  ASSERT_EQ(last_height, 300.0);
2501 
2502  DestroyShell(std::move(shell), std::move(task_runners));
2503 }
2504 
2506 #if defined(WINUWP)
2507  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
2508  DISABLED_OnServiceProtocolSetAssetBundlePathWorks
2509 #else
2510  OnServiceProtocolSetAssetBundlePathWorks
2511 #endif // defined(WINUWP)
2512 ) {
2513 
2514  Settings settings = CreateSettingsForFixture();
2515  std::unique_ptr<Shell> shell = CreateShell(settings);
2516  RunConfiguration configuration =
2518  configuration.SetEntrypoint("canAccessResourceFromAssetDir");
2519 
2520  // Verify isolate can load a known resource with the
2521  // default asset directory - kernel_blob.bin
2523 
2524  // Callback used to signal whether the resource was loaded successfully.
2525  bool can_access_resource = false;
2526  auto native_can_access_resource = [&can_access_resource,
2527  &latch](Dart_NativeArguments args) {
2528  Dart_Handle exception = nullptr;
2529  can_access_resource =
2531  latch.Signal();
2532  };
2533  AddNativeCallback("NotifyCanAccessResource",
2534  CREATE_NATIVE_ENTRY(native_can_access_resource));
2535 
2536  // Callback used to delay the asset load until after the service
2537  // protocol method has finished.
2538  auto native_notify_set_asset_bundle_path =
2539  [&shell](Dart_NativeArguments args) {
2540  // Update the asset directory to a bonus path.
2542  params["assetDirectory"] = "assetDirectory";
2543  rapidjson::Document document;
2544  OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath,
2545  shell->GetTaskRunners().GetUITaskRunner(), params,
2546  &document);
2547  rapidjson::StringBuffer buffer;
2548  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2549  document.Accept(writer);
2550  };
2551  AddNativeCallback("NotifySetAssetBundlePath",
2552  CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path));
2553 
2554  RunEngine(shell.get(), std::move(configuration));
2555 
2556  latch.Wait();
2557  ASSERT_TRUE(can_access_resource);
2558 
2559  DestroyShell(std::move(shell));
2560 }
2561 
2562 TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) {
2563  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2564  // Make sure the shell launch does not kick off the creation of the VM
2565  // instance by already creating one upfront.
2566  auto vm_settings = CreateSettingsForFixture();
2567  auto vm_ref = DartVMRef::Create(vm_settings);
2568  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2569 
2570  auto settings = vm_settings;
2571  fml::AutoResetWaitableEvent isolate_create_latch;
2572  settings.root_isolate_create_callback = [&](const auto& isolate) {
2573  isolate_create_latch.Signal();
2574  };
2575  auto shell = CreateShell(settings);
2576  ASSERT_TRUE(ValidateShell(shell.get()));
2577  auto configuration = RunConfiguration::InferFromSettings(settings);
2578  ASSERT_TRUE(configuration.IsValid());
2579  RunEngine(shell.get(), std::move(configuration));
2580  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2581  DestroyShell(std::move(shell));
2582  isolate_create_latch.Wait();
2583 }
2584 
2586 #if defined(WINUWP)
2587  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
2588  DISABLED_AssetManagerSingle
2589 #else
2590  AssetManagerSingle
2591 #endif // defined(WINUWP)
2592 ) {
2593 
2595  fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2596  asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2597 
2598  std::string filename = "test_name";
2599  std::string content = "test_content";
2600 
2601  bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2602  fml::DataMapping(content));
2603  ASSERT_TRUE(success);
2604 
2605  AssetManager asset_manager;
2606  asset_manager.PushBack(
2607  std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
2608 
2609  auto mapping = asset_manager.GetAsMapping(filename);
2610  ASSERT_TRUE(mapping != nullptr);
2611 
2612  std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
2613  mapping->GetSize());
2614 
2615  ASSERT_TRUE(result == content);
2616 }
2617 
2619 #if defined(WINUWP)
2620  // TODO(cbracken): https://github.com/flutter/flutter/issues/90481
2621  DISABLED_AssetManagerMulti
2622 #else
2623  AssetManagerMulti
2624 #endif // defined(WINUWP)
2625 ) {
2626 
2628  fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2629  asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2630 
2631  std::vector<std::string> filenames = {
2632  "good0",
2633  "bad0",
2634  "good1",
2635  "bad1",
2636  };
2637 
2638  for (auto filename : filenames) {
2639  bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2640  fml::DataMapping(filename));
2641  ASSERT_TRUE(success);
2642  }
2643 
2644  AssetManager asset_manager;
2645  asset_manager.PushBack(
2646  std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
2647 
2648  auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
2649  EXPECT_EQ(mappings.size(), 4u);
2650 
2651  std::vector<std::string> expected_results = {
2652  "good0",
2653  "good1",
2654  };
2655 
2656  mappings = asset_manager.GetAsMappings("(.*)good(.*)", std::nullopt);
2657  ASSERT_EQ(mappings.size(), expected_results.size());
2658 
2659  for (auto& mapping : mappings) {
2660  std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
2661  mapping->GetSize());
2662  EXPECT_NE(
2663  std::find(expected_results.begin(), expected_results.end(), result),
2664  expected_results.end());
2665  }
2666 }
2667 
2668 #if defined(OS_FUCHSIA)
2669 TEST_F(ShellTest, AssetManagerMultiSubdir) {
2670  std::string subdir_path = "subdir";
2671 
2673  fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
2674  asset_dir.path().c_str(), false, fml::FilePermission::kRead);
2675  fml::UniqueFD subdir_fd =
2676  fml::OpenDirectory((asset_dir.path() + "/" + subdir_path).c_str(), true,
2678 
2679  std::vector<std::string> filenames = {
2680  "bad0",
2681  "notgood", // this is to make sure the pattern (.*)good(.*) only matches
2682  // things in the subdirectory
2683  };
2684 
2685  std::vector<std::string> subdir_filenames = {
2686  "good0",
2687  "good1",
2688  "bad1",
2689  };
2690 
2691  for (auto filename : filenames) {
2692  bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(),
2693  fml::DataMapping(filename));
2694  ASSERT_TRUE(success);
2695  }
2696 
2697  for (auto filename : subdir_filenames) {
2698  bool success = fml::WriteAtomically(subdir_fd, filename.c_str(),
2699  fml::DataMapping(filename));
2700  ASSERT_TRUE(success);
2701  }
2702 
2703  AssetManager asset_manager;
2704  asset_manager.PushBack(
2705  std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
2706 
2707  auto mappings = asset_manager.GetAsMappings("(.*)", std::nullopt);
2708  EXPECT_EQ(mappings.size(), 5u);
2709 
2710  mappings = asset_manager.GetAsMappings("(.*)", subdir_path);
2711  EXPECT_EQ(mappings.size(), 3u);
2712 
2713  std::vector<std::string> expected_results = {
2714  "good0",
2715  "good1",
2716  };
2717 
2718  mappings = asset_manager.GetAsMappings("(.*)good(.*)", subdir_path);
2719  ASSERT_EQ(mappings.size(), expected_results.size());
2720 
2721  for (auto& mapping : mappings) {
2722  std::string result(reinterpret_cast<const char*>(mapping->GetMapping()),
2723  mapping->GetSize());
2724  ASSERT_NE(
2725  std::find(expected_results.begin(), expected_results.end(), result),
2726  expected_results.end());
2727  }
2728 }
2729 #endif // OS_FUCHSIA
2730 
2732  auto settings = CreateSettingsForFixture();
2733  auto shell = CreateShell(settings);
2734  ASSERT_TRUE(ValidateShell(shell.get()));
2735 
2736  auto configuration = RunConfiguration::InferFromSettings(settings);
2737  ASSERT_TRUE(configuration.IsValid());
2738  configuration.SetEntrypoint("fixturesAreFunctionalMain");
2739 
2740  auto second_configuration = RunConfiguration::InferFromSettings(settings);
2741  ASSERT_TRUE(second_configuration.IsValid());
2742  second_configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
2743 
2744  const std::string initial_route("/foo");
2745 
2746  fml::AutoResetWaitableEvent main_latch;
2747  std::string last_entry_point;
2748  // Fulfill native function for the first Shell's entrypoint.
2749  AddNativeCallback(
2750  "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
2751  last_entry_point = shell->GetEngine()->GetLastEntrypoint();
2752  main_latch.Signal();
2753  }));
2754  // Fulfill native function for the second Shell's entrypoint.
2755  fml::CountDownLatch second_latch(2);
2756  AddNativeCallback(
2757  // The Dart native function names aren't very consistent but this is just
2758  // the native function name of the second vm entrypoint in the fixture.
2759  "NotifyNative",
2760  CREATE_NATIVE_ENTRY([&](auto args) { second_latch.CountDown(); }));
2761 
2762  RunEngine(shell.get(), std::move(configuration));
2763  main_latch.Wait();
2764  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2765  // Check first Shell ran the first entrypoint.
2766  ASSERT_EQ("fixturesAreFunctionalMain", last_entry_point);
2767 
2768  PostSync(
2769  shell->GetTaskRunners().GetPlatformTaskRunner(),
2770  [this, &spawner = shell, &second_configuration, &second_latch,
2771  initial_route]() {
2772  MockPlatformViewDelegate platform_view_delegate;
2773  auto spawn = spawner->Spawn(
2774  std::move(second_configuration), initial_route,
2775  [&platform_view_delegate](Shell& shell) {
2776  auto result = std::make_unique<MockPlatformView>(
2777  platform_view_delegate, shell.GetTaskRunners());
2778  ON_CALL(*result, CreateRenderingSurface())
2779  .WillByDefault(::testing::Invoke(
2780  [] { return std::make_unique<MockSurface>(); }));
2781  return result;
2782  },
2783  [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
2784  ASSERT_NE(nullptr, spawn.get());
2785  ASSERT_TRUE(ValidateShell(spawn.get()));
2786 
2787  PostSync(spawner->GetTaskRunners().GetUITaskRunner(),
2788  [&spawn, &spawner, initial_route] {
2789  // Check second shell ran the second entrypoint.
2790  ASSERT_EQ("testCanLaunchSecondaryIsolate",
2791  spawn->GetEngine()->GetLastEntrypoint());
2792  ASSERT_EQ(initial_route, spawn->GetEngine()->InitialRoute());
2793 
2794  // TODO(74520): Remove conditional once isolate groups are
2795  // supported by JIT.
2797  ASSERT_NE(spawner->GetEngine()
2798  ->GetRuntimeController()
2799  ->GetRootIsolateGroup(),
2800  0u);
2801  ASSERT_EQ(spawner->GetEngine()
2802  ->GetRuntimeController()
2803  ->GetRootIsolateGroup(),
2804  spawn->GetEngine()
2805  ->GetRuntimeController()
2806  ->GetRootIsolateGroup());
2807  }
2808  });
2809 
2810  PostSync(
2811  spawner->GetTaskRunners().GetIOTaskRunner(), [&spawner, &spawn] {
2812  ASSERT_EQ(spawner->GetIOManager()->GetResourceContext().get(),
2813  spawn->GetIOManager()->GetResourceContext().get());
2814  });
2815 
2816  // Before destroying the shell, wait for expectations of the spawned
2817  // isolate to be met.
2818  second_latch.Wait();
2819 
2820  DestroyShell(std::move(spawn));
2821  });
2822 
2823  DestroyShell(std::move(shell));
2824  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2825 }
2826 
2827 TEST_F(ShellTest, UpdateAssetResolverByTypeReplaces) {
2828  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2829  Settings settings = CreateSettingsForFixture();
2830  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
2831  ThreadHost::Type::Platform);
2832  auto task_runner = thread_host.platform_thread->GetTaskRunner();
2833  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2834  task_runner);
2835  auto shell = CreateShell(std::move(settings), task_runners);
2836  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2837  ASSERT_TRUE(ValidateShell(shell.get()));
2838 
2839  auto configuration = RunConfiguration::InferFromSettings(settings);
2840  configuration.SetEntrypoint("emptyMain");
2841  auto asset_manager = configuration.GetAssetManager();
2842  RunEngine(shell.get(), std::move(configuration));
2843 
2844  auto platform_view =
2845  std::make_unique<PlatformView>(*shell.get(), std::move(task_runners));
2846 
2847  auto old_resolver = std::make_unique<TestAssetResolver>(
2848  true, AssetResolver::AssetResolverType::kApkAssetProvider);
2849  ASSERT_TRUE(old_resolver->IsValid());
2850  asset_manager->PushBack(std::move(old_resolver));
2851 
2852  auto updated_resolver = std::make_unique<TestAssetResolver>(
2853  false, AssetResolver::AssetResolverType::kApkAssetProvider);
2854  ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
2855  platform_view->UpdateAssetResolverByType(
2856  std::move(updated_resolver),
2857  AssetResolver::AssetResolverType::kApkAssetProvider);
2858 
2859  auto resolvers = asset_manager->TakeResolvers();
2860  ASSERT_EQ(resolvers.size(), 2ull);
2861  ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
2862 
2863  ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
2864 
2865  DestroyShell(std::move(shell), std::move(task_runners));
2866  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2867 }
2868 
2869 TEST_F(ShellTest, UpdateAssetResolverByTypeAppends) {
2870  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2871  Settings settings = CreateSettingsForFixture();
2872  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
2873  ThreadHost::Type::Platform);
2874  auto task_runner = thread_host.platform_thread->GetTaskRunner();
2875  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2876  task_runner);
2877  auto shell = CreateShell(std::move(settings), task_runners);
2878  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2879  ASSERT_TRUE(ValidateShell(shell.get()));
2880 
2881  auto configuration = RunConfiguration::InferFromSettings(settings);
2882  configuration.SetEntrypoint("emptyMain");
2883  auto asset_manager = configuration.GetAssetManager();
2884  RunEngine(shell.get(), std::move(configuration));
2885 
2886  auto platform_view =
2887  std::make_unique<PlatformView>(*shell.get(), std::move(task_runners));
2888 
2889  auto updated_resolver = std::make_unique<TestAssetResolver>(
2890  false, AssetResolver::AssetResolverType::kApkAssetProvider);
2891  ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
2892  platform_view->UpdateAssetResolverByType(
2893  std::move(updated_resolver),
2894  AssetResolver::AssetResolverType::kApkAssetProvider);
2895 
2896  auto resolvers = asset_manager->TakeResolvers();
2897  ASSERT_EQ(resolvers.size(), 2ull);
2898  ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
2899 
2900  ASSERT_FALSE(resolvers[1]->IsValidAfterAssetManagerChange());
2901 
2902  DestroyShell(std::move(shell), std::move(task_runners));
2903  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2904 }
2905 
2906 TEST_F(ShellTest, UpdateAssetResolverByTypeNull) {
2907  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2908  Settings settings = CreateSettingsForFixture();
2909  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
2910  ThreadHost::Type::Platform);
2911  auto task_runner = thread_host.platform_thread->GetTaskRunner();
2912  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2913  task_runner);
2914  auto shell = CreateShell(std::move(settings), task_runners);
2915  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2916  ASSERT_TRUE(ValidateShell(shell.get()));
2917 
2918  auto configuration = RunConfiguration::InferFromSettings(settings);
2919  configuration.SetEntrypoint("emptyMain");
2920  auto asset_manager = configuration.GetAssetManager();
2921  RunEngine(shell.get(), std::move(configuration));
2922 
2923  auto platform_view =
2924  std::make_unique<PlatformView>(*shell.get(), std::move(task_runners));
2925 
2926  auto old_resolver = std::make_unique<TestAssetResolver>(
2927  true, AssetResolver::AssetResolverType::kApkAssetProvider);
2928  ASSERT_TRUE(old_resolver->IsValid());
2929  asset_manager->PushBack(std::move(old_resolver));
2930 
2931  platform_view->UpdateAssetResolverByType(
2932  std::move(nullptr), AssetResolver::AssetResolverType::kApkAssetProvider);
2933 
2934  auto resolvers = asset_manager->TakeResolvers();
2935  ASSERT_EQ(resolvers.size(), 2ull);
2936  ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
2937  ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
2938 
2939  DestroyShell(std::move(shell), std::move(task_runners));
2940  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2941 }
2942 
2943 TEST_F(ShellTest, UpdateAssetResolverByTypeDoesNotReplaceMismatchType) {
2944  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2945  Settings settings = CreateSettingsForFixture();
2946  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
2947  ThreadHost::Type::Platform);
2948  auto task_runner = thread_host.platform_thread->GetTaskRunner();
2949  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2950  task_runner);
2951  auto shell = CreateShell(std::move(settings), task_runners);
2952  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2953  ASSERT_TRUE(ValidateShell(shell.get()));
2954 
2955  auto configuration = RunConfiguration::InferFromSettings(settings);
2956  configuration.SetEntrypoint("emptyMain");
2957  auto asset_manager = configuration.GetAssetManager();
2958  RunEngine(shell.get(), std::move(configuration));
2959 
2960  auto platform_view =
2961  std::make_unique<PlatformView>(*shell.get(), std::move(task_runners));
2962 
2963  auto old_resolver = std::make_unique<TestAssetResolver>(
2964  true, AssetResolver::AssetResolverType::kAssetManager);
2965  ASSERT_TRUE(old_resolver->IsValid());
2966  asset_manager->PushBack(std::move(old_resolver));
2967 
2968  auto updated_resolver = std::make_unique<TestAssetResolver>(
2969  false, AssetResolver::AssetResolverType::kApkAssetProvider);
2970  ASSERT_FALSE(updated_resolver->IsValidAfterAssetManagerChange());
2971  platform_view->UpdateAssetResolverByType(
2972  std::move(updated_resolver),
2973  AssetResolver::AssetResolverType::kApkAssetProvider);
2974 
2975  auto resolvers = asset_manager->TakeResolvers();
2976  ASSERT_EQ(resolvers.size(), 3ull);
2977  ASSERT_TRUE(resolvers[0]->IsValidAfterAssetManagerChange());
2978 
2979  ASSERT_TRUE(resolvers[1]->IsValidAfterAssetManagerChange());
2980 
2981  ASSERT_FALSE(resolvers[2]->IsValidAfterAssetManagerChange());
2982 
2983  DestroyShell(std::move(shell), std::move(task_runners));
2984  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2985 }
2986 
2987 TEST_F(ShellTest, CanCreateShellsWithGLBackend) {
2988 #if !SHELL_ENABLE_GL
2989  // GL emulation does not exist on Fuchsia.
2990  GTEST_SKIP();
2991 #endif // !SHELL_ENABLE_GL
2992  auto settings = CreateSettingsForFixture();
2993  std::unique_ptr<Shell> shell =
2994  CreateShell(settings, //
2995  GetTaskRunnersForFixture(), //
2996  false, //
2997  nullptr, //
2998  false, //
3000  );
3001  ASSERT_NE(shell, nullptr);
3002  ASSERT_TRUE(shell->IsSetup());
3003  PlatformViewNotifyCreated(shell.get());
3004  auto configuration = RunConfiguration::InferFromSettings(settings);
3005  configuration.SetEntrypoint("emptyMain");
3006  RunEngine(shell.get(), std::move(configuration));
3007  PumpOneFrame(shell.get());
3008  PlatformViewNotifyDestroyed(shell.get());
3009  DestroyShell(std::move(shell));
3010 }
3011 
3012 TEST_F(ShellTest, CanCreateShellsWithVulkanBackend) {
3013 #if !SHELL_ENABLE_VULKAN
3014  GTEST_SKIP();
3015 #endif // !SHELL_ENABLE_VULKAN
3016  auto settings = CreateSettingsForFixture();
3017  std::unique_ptr<Shell> shell =
3018  CreateShell(settings, //
3019  GetTaskRunnersForFixture(), //
3020  false, //
3021  nullptr, //
3022  false, //
3024  );
3025  ASSERT_NE(shell, nullptr);
3026  ASSERT_TRUE(shell->IsSetup());
3027  PlatformViewNotifyCreated(shell.get());
3028  auto configuration = RunConfiguration::InferFromSettings(settings);
3029  configuration.SetEntrypoint("emptyMain");
3030  RunEngine(shell.get(), std::move(configuration));
3031  PumpOneFrame(shell.get());
3032  PlatformViewNotifyDestroyed(shell.get());
3033  DestroyShell(std::move(shell));
3034 }
3035 
3036 TEST_F(ShellTest, CanCreateShellsWithMetalBackend) {
3037 #if !SHELL_ENABLE_METAL
3038  GTEST_SKIP();
3039 #endif // !SHELL_ENABLE_METAL
3040  auto settings = CreateSettingsForFixture();
3041  std::unique_ptr<Shell> shell =
3042  CreateShell(settings, //
3043  GetTaskRunnersForFixture(), //
3044  false, //
3045  nullptr, //
3046  false, //
3048  );
3049  ASSERT_NE(shell, nullptr);
3050  ASSERT_TRUE(shell->IsSetup());
3051  PlatformViewNotifyCreated(shell.get());
3052  auto configuration = RunConfiguration::InferFromSettings(settings);
3053  configuration.SetEntrypoint("emptyMain");
3054  RunEngine(shell.get(), std::move(configuration));
3055  PumpOneFrame(shell.get());
3056  PlatformViewNotifyDestroyed(shell.get());
3057  DestroyShell(std::move(shell));
3058 }
3059 
3060 TEST_F(ShellTest, UserTagSetOnStartup) {
3061  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
3062  // Make sure the shell launch does not kick off the creation of the VM
3063  // instance by already creating one upfront.
3064  auto vm_settings = CreateSettingsForFixture();
3065  auto vm_ref = DartVMRef::Create(vm_settings);
3066  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3067 
3068  auto settings = vm_settings;
3069  fml::AutoResetWaitableEvent isolate_create_latch;
3070 
3071  // ensure that "AppStartUpTag" is set during isolate creation.
3072  settings.root_isolate_create_callback = [&](const DartIsolate& isolate) {
3073  Dart_Handle current_tag = Dart_GetCurrentUserTag();
3074  Dart_Handle startup_tag = Dart_NewUserTag("AppStartUp");
3075  EXPECT_TRUE(Dart_IdentityEquals(current_tag, startup_tag));
3076 
3077  isolate_create_latch.Signal();
3078  };
3079 
3080  auto shell = CreateShell(settings);
3081  ASSERT_TRUE(ValidateShell(shell.get()));
3082 
3083  auto configuration = RunConfiguration::InferFromSettings(settings);
3084  ASSERT_TRUE(configuration.IsValid());
3085 
3086  RunEngine(shell.get(), std::move(configuration));
3087  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
3088 
3089  DestroyShell(std::move(shell));
3090  isolate_create_latch.Wait();
3091 }
3092 
3094  auto settings = CreateSettingsForFixture();
3095  settings.prefetched_default_font_manager = true;
3096  std::unique_ptr<Shell> shell;
3097 
3098  auto get_font_manager_count = [&] {
3100  size_t font_manager_count;
3102  shell->GetTaskRunners().GetUITaskRunner(),
3103  [this, &shell, &latch, &font_manager_count]() {
3104  font_manager_count =
3105  GetFontCollection(shell.get())->GetFontManagersCount();
3106  latch.Signal();
3107  });
3108  latch.Wait();
3109  return font_manager_count;
3110  };
3111  size_t initial_font_manager_count = 0;
3112  settings.root_isolate_create_callback = [&](const auto& isolate) {
3113  ASSERT_GT(initial_font_manager_count, 0ul);
3114  // Should not have fetched the default font manager yet, since the root
3115  // isolate was only just created.
3116  ASSERT_EQ(get_font_manager_count(), initial_font_manager_count);
3117  };
3118 
3119  shell = CreateShell(std::move(settings));
3120 
3121  initial_font_manager_count = get_font_manager_count();
3122 
3123  auto configuration = RunConfiguration::InferFromSettings(settings);
3124  configuration.SetEntrypoint("emptyMain");
3125  RunEngine(shell.get(), std::move(configuration));
3126 
3127  // If the prefetched_default_font_manager flag is set, then the default font
3128  // manager will not be added until the engine starts running.
3129  ASSERT_EQ(get_font_manager_count(), initial_font_manager_count + 1);
3130 
3131  DestroyShell(std::move(shell));
3132 }
3133 
3134 TEST_F(ShellTest, OnPlatformViewCreatedWhenUIThreadIsBusy) {
3135  // This test will deadlock if the threading logic in
3136  // Shell::OnCreatePlatformView is wrong.
3137  auto settings = CreateSettingsForFixture();
3138  auto shell = CreateShell(std::move(settings));
3139 
3141  fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetUITaskRunner(),
3142  [&latch]() { latch.Wait(); });
3143 
3145  latch.Signal();
3146 
3147  DestroyShell(std::move(shell));
3148 }
3149 
3150 TEST_F(ShellTest, UIWorkAfterOnPlatformViewDestroyed) {
3151  auto settings = CreateSettingsForFixture();
3152  auto shell = CreateShell(std::move(settings));
3153  auto configuration = RunConfiguration::InferFromSettings(settings);
3154  configuration.SetEntrypoint("drawFrames");
3155 
3157  fml::AutoResetWaitableEvent notify_native_latch;
3158  AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
3159  notify_native_latch.Signal();
3160  latch.Wait();
3161  }));
3162 
3163  RunEngine(shell.get(), std::move(configuration));
3164  // Wait to make sure we get called back from Dart and thus have latched
3165  // the UI thread before we create/destroy the platform view.
3166  notify_native_latch.Wait();
3167 
3169 
3170  fml::AutoResetWaitableEvent destroy_latch;
3172  shell->GetTaskRunners().GetPlatformTaskRunner(),
3173  [&shell, &destroy_latch]() {
3174  shell->GetPlatformView()->NotifyDestroyed();
3175  destroy_latch.Signal();
3176  });
3177 
3178  destroy_latch.Wait();
3179 
3180  // Unlatch the UI thread and let it send us a scene to render.
3181  latch.Signal();
3182 
3183  // Flush the UI task runner to make sure we process the render/scheduleFrame
3184  // request.
3185  fml::AutoResetWaitableEvent ui_flush_latch;
3187  shell->GetTaskRunners().GetUITaskRunner(),
3188  [&ui_flush_latch]() { ui_flush_latch.Signal(); });
3189  ui_flush_latch.Wait();
3190  DestroyShell(std::move(shell));
3191 }
3192 
3193 TEST_F(ShellTest, UsesPlatformMessageHandler) {
3194  TaskRunners task_runners = GetTaskRunnersForFixture();
3195  auto settings = CreateSettingsForFixture();
3196  MockPlatformViewDelegate platform_view_delegate;
3197  auto platform_message_handler =
3198  std::make_shared<MockPlatformMessageHandler>();
3199  int message_id = 1;
3200  EXPECT_CALL(*platform_message_handler, HandlePlatformMessage(_));
3201  EXPECT_CALL(*platform_message_handler,
3203  Shell::CreateCallback<PlatformView> platform_view_create_callback =
3204  [&platform_view_delegate, task_runners,
3205  platform_message_handler](flutter::Shell& shell) {
3206  auto result = std::make_unique<MockPlatformView>(platform_view_delegate,
3207  task_runners);
3208  EXPECT_CALL(*result, GetPlatformMessageHandler())
3209  .WillOnce(Return(platform_message_handler));
3210  return result;
3211  };
3212  auto shell = CreateShell(
3213  /*settings=*/std::move(settings),
3214  /*task_runners=*/task_runners,
3215  /*simulate_vsync=*/false,
3216  /*shell_test_external_view_embedder=*/nullptr,
3217  /*is_gpu_disabled=*/false,
3218  /*rendering_backend=*/
3220  /*platform_view_create_callback=*/platform_view_create_callback);
3221 
3222  EXPECT_EQ(platform_message_handler, shell->GetPlatformMessageHandler());
3223  PostSync(task_runners.GetUITaskRunner(), [&shell]() {
3224  size_t data_size = 4;
3225  fml::MallocMapping bytes =
3226  fml::MallocMapping(static_cast<uint8_t*>(malloc(data_size)), data_size);
3228  MockPlatformMessageResponse::Create();
3229  auto message = std::make_unique<PlatformMessage>(
3230  /*channel=*/"foo", /*data=*/std::move(bytes), /*response=*/response);
3231  (static_cast<Engine::Delegate*>(shell.get()))
3232  ->OnEngineHandlePlatformMessage(std::move(message));
3233  });
3234  shell->GetPlatformMessageHandler()
3235  ->InvokePlatformMessageEmptyResponseCallback(message_id);
3236  DestroyShell(std::move(shell));
3237 }
3238 
3239 } // namespace testing
3240 } // namespace flutter
const std::string & path() const
Definition: file.h:146
static bool RasterizerHasLayerTree(Shell *shell)
std::unique_ptr< fml::Thread > ui_thread
Definition: thread_host.h:27
G_BEGIN_DECLS FlValue * args
const char * GetFlutterEngineVersion()
Definition: version.cc:11
static void InvokePlatformMessageEmptyResponseCallback(JNIEnv *env, jobject jcaller, jlong shell_holder, jint responseId)
KeyCallType type
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...
MockTexture(int64_t textureId, std::shared_ptr< fml::AutoResetWaitableEvent > latch)
SkISize GetScaledDimensions(float scale)
Given a scale value, find the closest image size that can be used for efficiently decoding the image...
static std::unique_ptr< fml::MallocMapping > BuildCacheObject(const SkData &key, const SkData &data)
G_BEGIN_DECLS FlTexture * texture
While the engine operates entirely on the UI task runner, it needs the capabilities of the other comp...
Definition: engine.h:140
static void PlatformViewNotifyCreated(Shell *shell)
Definition: shell_test.cc:44
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition: shell.h:114
std::map< std::string_view, std::string_view > ServiceProtocolMap
static const size_t kGrCacheMaxByteSize
static bool ValidateShell(Shell *shell)
static constexpr TimeDelta Zero()
Definition: time_delta.h:33
std::unique_ptr< fml::Mapping > GetAsMapping(const std::string &asset_name) const override
std::unique_ptr< flutter::PlatformViewIOS > platform_view
bool MemsetPatternSetOrCheck(uint8_t *buffer, size_t size, MemsetPatternOp op)
Depending on the operation, either scribbles a known pattern into the buffer or checks if that patter...
Definition: testing.cc:61
static void PrefetchDefaultFontManager(JNIEnv *env, jclass jcaller)
Info about a single frame in the context of a multi-frame image, useful for animation and blending...
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition: shell.cc:674
fml::TimePoint Set(Phase phase, fml::TimePoint value)
Definition: settings.h:43
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
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:13
std::unique_ptr< fml::Mapping > OpenFixtureAsMapping(std::string fixture_name)
Opens a fixture of the given file name and returns a mapping to its contents.
Definition: testing.cc:57
static void TestDartVmFlags(std::vector< const char *> &flags)
unsigned int GetPlayCount() const
The number of times an animated image should play through before playback stops.
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
Definition: task_runners.cc:30
static void EnsureInitializedForCurrentThread()
Definition: message_loop.cc:27
int64_t texture_id
GAsyncResult * result
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
Settings SettingsFromCommandLine(const fml::CommandLine &command_line)
Definition: switches.cc:228
TimeDelta ToEpochDelta() const
Definition: time_point.h:47
#define FML_LOG(severity)
Definition: logging.h:65
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition: time_point.h:38
unsigned int GetFrameCount() const
Get the number of frames that the encoded image stores. This method is always expected to be called b...
static bool IsInstanceRunning()
fml::RefPtr< fml::TaskRunner > GetRasterTaskRunner() const
Definition: task_runners.cc:42
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&#39;s name matches a given pattern...
AssetResolver::AssetResolverType GetType() const override
Gets the type of AssetResolver this is. Types are defined in AssetResolverType.
void Paint(SkCanvas &canvas, const SkRect &bounds, bool freeze, GrDirectContext *context, const SkSamplingOptions &) override
FlKeyEvent FlKeyResponderAsyncCallback callback
void MergeWithLease(size_t lease_term)
static constexpr SkRect kGiantRect
Definition: layer.h:37
static DartVMRef Create(Settings settings, fml::RefPtr< const DartSnapshot > vm_snapshot=nullptr, fml::RefPtr< const DartSnapshot > isolate_snapshot=nullptr)
bool is_valid() const
Definition: unique_object.h:89
static void PlatformViewNotifyDestroyed(Shell *shell)
Definition: shell_test.cc:54
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:96
FlKeyEvent * event
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition: message_loop.cc:56
static void ValidateDestroyPlatformView(Shell *shell)
static constexpr Phase kPhases[kCount]
Definition: settings.h:36
virtual sk_sp< SkImage > MakeRasterSnapshot(std::function< void(SkCanvas *)> draw_callback, SkISize picture_size)=0
AssetResolverType
Identifies the type of AssetResolver an instance is.
static bool IsValid(double value)
std::function< void()> closure
Definition: closure.h:14
void SetEntrypoint(std::string entrypoint)
Updates the main application entrypoint. If this is not set, the "main" method is used as the entrypo...
static constexpr char kSkSLSubdirName[]
static RunConfiguration InferFromSettings(const Settings &settings, fml::RefPtr< fml::TaskRunner > io_worker=nullptr)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
A POD type used to return the screenshot data along with the size of the frame.
Definition: rasterizer.h:275
static void PostSync(const fml::RefPtr< fml::TaskRunner > &task_runner, const fml::closure &task)
std::unique_ptr< fml::Mapping > GetAsMapping(const std::string &asset_name) const override
static void SetCacheDirectoryPath(std::string path)
constexpr int64_t ToMicroseconds() const
Definition: time_delta.h:62
fml::TaskRunnerAffineWeakPtr< Rasterizer > GetRasterizer() const
Rasterizers may only be accessed on the raster task runner.
Definition: shell.cc:683
Specifies all the configuration required by the runtime library to launch the root isolate...
static void LogSkData(sk_sp< SkData > data, const char *title)
static void InvokePlatformMessageResponseCallback(JNIEnv *env, jobject jcaller, jlong shell_holder, jint responseId, jobject message, jint position)
static constexpr TimeDelta FromMicroseconds(int64_t micros)
Definition: time_delta.h:43
#define CREATE_NATIVE_ENTRY(native_entry)
RefPtr< T > AdoptRef(T *ptr)
Definition: ref_ptr.h:218
bool ok() const
Definition: status.h:71
Represents an instance of a live isolate. An isolate is a separate Dart execution context...
Definition: dart_isolate.h:62
const UniqueFD & fd()
Definition: file.h:147
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
SemanticsAction action
static size_t GetRasterizerResourceCacheBytesSync(const Shell &shell)
static fml::UniqueFD CreateDirectory(const fml::UniqueFD &base_directory, const std::vector< std::string > &components, FilePermission permission, size_t index)
Definition: file.cc:12
The collection of all the threads used by the engine.
Definition: thread_host.h:16
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:202
static void LoadDartDeferredLibrary(JNIEnv *env, jobject obj, jlong shell_holder, jint jLoadingUnitId, jobjectArray jSearchPaths)
TestAssetResolver(bool valid, AssetResolver::AssetResolverType type)
int32_t width
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, 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:126
void PushBack(std::unique_ptr< AssetResolver > resolver)
std::vector< std::string > dart_flags
Definition: settings.h:124
const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const
Get information about a single frame in the context of a multi-frame image, useful for animation and ...
fml::WeakPtr< PlatformView > GetPlatformView()
Platform views may only be accessed on the platform task runner.
Definition: shell.cc:693
const char * GetSkiaVersion()
Definition: version.cc:15
int32_t height
static MallocMapping Copy(const T *begin, const T *end)
Definition: mapping.h:147
An image generator that always creates a 1x1 single-frame green image.
static sk_sp< SkPicture > MakeSizedPicture(int width, int height)
The minimal interface necessary for defining a decoder that can be used for both single and multi-fra...
bool IsValidAfterAssetManagerChange() const override
Certain asset resolvers are still valid after the asset manager is replaced before a hot reload...
static void ResetCacheForProcess()
fml::StatusCode code() const
Definition: status.h:63
static std::string CreateFlagsString(std::vector< const char *> &flags)
const SkImageInfo & GetInfo()
Returns basic information about the contents of the encoded image. This information can almost always...
A Mapping like NonOwnedMapping, but uses Free as its release proc.
Definition: mapping.h:129
std::unique_ptr< fml::Thread > io_thread
Definition: thread_host.h:29
static const uint8_t buffer[]
std::function< void(bool)> KeyDataResponse
fml::TimePoint Get(Phase phase) const
Definition: settings.h:42
bool IsSetup() const
Used by embedders to check if all shell subcomponents are initialized. It is the embedder&#39;s responsib...
Definition: shell.cc:611
int32_t id
std::shared_ptr< const fml::Mapping > persistent_isolate_data
Definition: settings.h:276
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&#39;s name matches a given pattern...
static std::unique_ptr< FileMapping > CreateReadOnly(const std::string &path)
Definition: mapping.cc:18
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:190
static constexpr TimeDelta Max()
Definition: time_delta.h:37
static std::unique_ptr< ShellTestPlatformView > Create(PlatformView::Delegate &delegate, TaskRunners task_runners, std::shared_ptr< ShellTestVsyncClock > vsync_clock, CreateVsyncWaiter create_vsync_waiter, BackendType backend, std::shared_ptr< ShellTestExternalViewEmbedder > shell_test_external_view_embedder)
std::unique_ptr< fml::Thread > raster_thread
Definition: thread_host.h:28
static TimePoint Now()
Definition: time_point.cc:39
static void CheckFrameTimings(const std::vector< FrameTiming > &timings, fml::TimePoint start, fml::TimePoint finish)
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol Prefix for the symbols representing ICU data linked into the Flutter library dart flags
Definition: switches.h:66
std::unique_ptr< fml::Thread > platform_thread
Definition: thread_host.h:26
TEST(DisplayListCanvas, DrawPaint)
static bool RunsOnTheSameThread(TaskQueueId queue_a, TaskQueueId queue_b)
Represents the 2 code paths available when calling |SyncSwitch::Execute|.
Definition: sync_switch.h:24