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 
13 #include "flutter/flow/layers/layer_tree.h"
14 #include "flutter/flow/layers/picture_layer.h"
15 #include "flutter/flow/layers/transform_layer.h"
16 #include "flutter/fml/command_line.h"
17 #include "flutter/fml/dart/dart_converter.h"
18 #include "flutter/fml/make_copyable.h"
19 #include "flutter/fml/message_loop.h"
20 #include "flutter/fml/synchronization/count_down_latch.h"
21 #include "flutter/fml/synchronization/waitable_event.h"
22 #include "flutter/runtime/dart_vm.h"
23 #include "flutter/shell/common/persistent_cache.h"
24 #include "flutter/shell/common/platform_view.h"
25 #include "flutter/shell/common/rasterizer.h"
26 #include "flutter/shell/common/shell_test.h"
27 #include "flutter/shell/common/shell_test_external_view_embedder.h"
28 #include "flutter/shell/common/shell_test_platform_view.h"
29 #include "flutter/shell/common/switches.h"
30 #include "flutter/shell/common/thread_host.h"
31 #include "flutter/shell/common/vsync_waiter_fallback.h"
32 #include "flutter/shell/version/version.h"
33 #include "flutter/testing/testing.h"
34 #include "third_party/rapidjson/include/rapidjson/writer.h"
35 #include "third_party/skia/include/core/SkPictureRecorder.h"
37 
38 #ifdef SHELL_ENABLE_VULKAN
39 #include "flutter/vulkan/vulkan_application.h" // nogncheck
40 #endif
41 
42 namespace flutter {
43 namespace testing {
44 
45 static bool ValidateShell(Shell* shell) {
46  if (!shell) {
47  return false;
48  }
49 
50  if (!shell->IsSetup()) {
51  return false;
52  }
53 
55 
56  {
59  shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() {
60  shell->GetPlatformView()->NotifyDestroyed();
61  latch.Signal();
62  });
63  latch.Wait();
64  }
65 
66  return true;
67 }
68 
69 static bool RasterizerHasLayerTree(Shell* shell) {
71  bool has_layer_tree = false;
74  [shell, &latch, &has_layer_tree]() {
75  has_layer_tree = shell->GetRasterizer()->GetLastLayerTree() != nullptr;
76  latch.Signal();
77  });
78  latch.Wait();
79  return has_layer_tree;
80 }
81 
82 static void ValidateDestroyPlatformView(Shell* shell) {
83  ASSERT_TRUE(shell != nullptr);
84  ASSERT_TRUE(shell->IsSetup());
85 
86  // To validate destroy platform view, we must ensure the rasterizer has a
87  // layer tree before the platform view is destroyed.
88  ASSERT_TRUE(RasterizerHasLayerTree(shell));
89 
91  // Validate the layer tree is destroyed
92  ASSERT_FALSE(RasterizerHasLayerTree(shell));
93 }
94 
95 static std::string CreateFlagsString(std::vector<const char*>& flags) {
96  if (flags.size() == 0) {
97  return "";
98  }
99  std::string flags_string = flags[0];
100  for (size_t i = 1; i < flags.size(); ++i) {
101  flags_string += ",";
102  flags_string += flags[i];
103  }
104  return flags_string;
105 }
106 
107 static void TestDartVmFlags(std::vector<const char*>& flags) {
108  std::string flags_string = CreateFlagsString(flags);
109  const std::vector<fml::CommandLine::Option> options = {
110  fml::CommandLine::Option("dart-flags", flags_string)};
111  fml::CommandLine command_line("", options, std::vector<std::string>());
112  flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
113  EXPECT_EQ(settings.dart_flags.size(), flags.size());
114  for (size_t i = 0; i < flags.size(); ++i) {
115  EXPECT_EQ(settings.dart_flags[i], flags[i]);
116  }
117 }
118 
119 TEST_F(ShellTest, InitializeWithInvalidThreads) {
120  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
121  Settings settings = CreateSettingsForFixture();
122  TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr);
123  auto shell = CreateShell(std::move(settings), std::move(task_runners));
124  ASSERT_FALSE(shell);
125  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
126 }
127 
128 TEST_F(ShellTest, InitializeWithDifferentThreads) {
129  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
130  Settings settings = CreateSettingsForFixture();
131  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
132  ThreadHost::Type::Platform | ThreadHost::Type::GPU |
133  ThreadHost::Type::IO | ThreadHost::Type::UI);
134  TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
135  thread_host.raster_thread->GetTaskRunner(),
136  thread_host.ui_thread->GetTaskRunner(),
137  thread_host.io_thread->GetTaskRunner());
138  auto shell = CreateShell(std::move(settings), std::move(task_runners));
139  ASSERT_TRUE(ValidateShell(shell.get()));
140  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
141  DestroyShell(std::move(shell), std::move(task_runners));
142  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
143 }
144 
145 TEST_F(ShellTest, InitializeWithSingleThread) {
146  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
147  Settings settings = CreateSettingsForFixture();
148  ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
149  ThreadHost::Type::Platform);
150  auto task_runner = thread_host.platform_thread->GetTaskRunner();
151  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
152  task_runner);
153  auto shell = CreateShell(std::move(settings), task_runners);
154  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
155  ASSERT_TRUE(ValidateShell(shell.get()));
156  DestroyShell(std::move(shell), std::move(task_runners));
157  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
158 }
159 
160 TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) {
161  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
162  Settings settings = CreateSettingsForFixture();
164  auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
165  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
166  task_runner);
167  auto shell = CreateShell(std::move(settings), task_runners);
168  ASSERT_TRUE(ValidateShell(shell.get()));
169  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
170  DestroyShell(std::move(shell), std::move(task_runners));
171  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
172 }
173 
175  InitializeWithMultipleThreadButCallingThreadAsPlatformThread) {
176  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
177  Settings settings = CreateSettingsForFixture();
178  ThreadHost thread_host(
179  "io.flutter.test." + GetCurrentTestName() + ".",
180  ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI);
182  TaskRunners task_runners("test",
183  fml::MessageLoop::GetCurrent().GetTaskRunner(),
184  thread_host.raster_thread->GetTaskRunner(),
185  thread_host.ui_thread->GetTaskRunner(),
186  thread_host.io_thread->GetTaskRunner());
187  auto shell = Shell::Create(
188  std::move(task_runners), settings,
189  [](Shell& shell) {
190  // This is unused in the platform view as we are not using the simulated
191  // vsync mechanism. We should have better DI in the tests.
192  const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
194  shell, shell.GetTaskRunners(), vsync_clock,
195  [task_runners = shell.GetTaskRunners()]() {
196  return static_cast<std::unique_ptr<VsyncWaiter>>(
197  std::make_unique<VsyncWaiterFallback>(task_runners));
198  },
200  },
201  [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
202  ASSERT_TRUE(ValidateShell(shell.get()));
203  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
204  DestroyShell(std::move(shell), std::move(task_runners));
205  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
206 }
207 
208 TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) {
209  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
210  Settings settings = CreateSettingsForFixture();
211  ThreadHost thread_host(
212  "io.flutter.test." + GetCurrentTestName() + ".",
213  ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI);
214  TaskRunners task_runners(
215  "test",
216  thread_host.platform_thread->GetTaskRunner(), // platform
217  thread_host.platform_thread->GetTaskRunner(), // raster
218  thread_host.ui_thread->GetTaskRunner(), // ui
219  thread_host.io_thread->GetTaskRunner() // io
220  );
221  auto shell = CreateShell(std::move(settings), std::move(task_runners));
222  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
223  ASSERT_TRUE(ValidateShell(shell.get()));
224  DestroyShell(std::move(shell), std::move(task_runners));
225  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
226 }
227 
228 TEST_F(ShellTest, FixturesAreFunctional) {
229  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
230  auto settings = CreateSettingsForFixture();
231  auto shell = CreateShell(settings);
232  ASSERT_TRUE(ValidateShell(shell.get()));
233 
234  auto configuration = RunConfiguration::InferFromSettings(settings);
235  ASSERT_TRUE(configuration.IsValid());
236  configuration.SetEntrypoint("fixturesAreFunctionalMain");
237 
238  fml::AutoResetWaitableEvent main_latch;
239  AddNativeCallback(
240  "SayHiFromFixturesAreFunctionalMain",
241  CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); }));
242 
243  RunEngine(shell.get(), std::move(configuration));
244  main_latch.Wait();
245  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
246  DestroyShell(std::move(shell));
247  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
248 }
249 
250 TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) {
251  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
252  auto settings = CreateSettingsForFixture();
253  auto shell = CreateShell(settings);
254  ASSERT_TRUE(ValidateShell(shell.get()));
255 
256  auto configuration = RunConfiguration::InferFromSettings(settings);
257  ASSERT_TRUE(configuration.IsValid());
258  configuration.SetEntrypoint("testCanLaunchSecondaryIsolate");
259 
260  fml::CountDownLatch latch(2);
261  AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
262  latch.CountDown();
263  }));
264 
265  RunEngine(shell.get(), std::move(configuration));
266 
267  latch.Wait();
268 
269  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
270  DestroyShell(std::move(shell));
271  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
272 }
273 
274 TEST_F(ShellTest, LastEntrypoint) {
275  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
276  auto settings = CreateSettingsForFixture();
277  auto shell = CreateShell(settings);
278  ASSERT_TRUE(ValidateShell(shell.get()));
279 
280  auto configuration = RunConfiguration::InferFromSettings(settings);
281  ASSERT_TRUE(configuration.IsValid());
282  std::string entry_point = "fixturesAreFunctionalMain";
283  configuration.SetEntrypoint(entry_point);
284 
285  fml::AutoResetWaitableEvent main_latch;
286  std::string last_entry_point;
287  AddNativeCallback(
288  "SayHiFromFixturesAreFunctionalMain", CREATE_NATIVE_ENTRY([&](auto args) {
289  last_entry_point = shell->GetEngine()->GetLastEntrypoint();
290  main_latch.Signal();
291  }));
292 
293  RunEngine(shell.get(), std::move(configuration));
294  main_latch.Wait();
295  EXPECT_EQ(entry_point, last_entry_point);
296  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
297  DestroyShell(std::move(shell));
298  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
299 }
300 
301 TEST_F(ShellTest, DisallowedDartVMFlag) {
302  // Run this test in a thread-safe manner, otherwise gtest will complain.
303  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
304 
305  const std::vector<fml::CommandLine::Option> options = {
306  fml::CommandLine::Option("dart-flags", "--verify_after_gc")};
307  fml::CommandLine command_line("", options, std::vector<std::string>());
308 
309  // Upon encountering a disallowed Dart flag the process terminates.
310  const char* expected =
311  "Encountered disallowed Dart VM flag: --verify_after_gc";
312  ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected);
313 }
314 
315 TEST_F(ShellTest, AllowedDartVMFlag) {
316  std::vector<const char*> flags = {
317  "--enable-isolate-groups",
318  "--no-enable-isolate-groups",
319  "--lazy_async_stacks",
320  "--no-causal_async_stacks",
321  };
322 #if !FLUTTER_RELEASE
323  flags.push_back("--max_profile_depth 1");
324  flags.push_back("--random_seed 42");
326  flags.push_back("--enable_mirrors");
327  }
328 #endif
329  TestDartVmFlags(flags);
330 }
331 
332 TEST_F(ShellTest, NoNeedToReportTimingsByDefault) {
333  auto settings = CreateSettingsForFixture();
334  std::unique_ptr<Shell> shell = CreateShell(settings);
335 
336  // Create the surface needed by rasterizer
337  PlatformViewNotifyCreated(shell.get());
338 
339  auto configuration = RunConfiguration::InferFromSettings(settings);
340  configuration.SetEntrypoint("emptyMain");
341 
342  RunEngine(shell.get(), std::move(configuration));
343  PumpOneFrame(shell.get());
344  ASSERT_FALSE(GetNeedsReportTimings(shell.get()));
345 
346  // This assertion may or may not be the direct result of needs_report_timings_
347  // being false. The count could be 0 simply because we just cleared
348  // unreported timings by reporting them. Hence this can't replace the
349  // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added
350  // this assertion for an additional confidence that we're not pushing
351  // back to unreported timings unnecessarily.
352  //
353  // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be
354  // positive in any tests. Otherwise those tests will be flaky as the clearing
355  // of unreported timings is unpredictive.
356  ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0);
357  DestroyShell(std::move(shell));
358 }
359 
360 TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) {
361  auto settings = CreateSettingsForFixture();
362  std::unique_ptr<Shell> shell = CreateShell(settings);
363 
364  // Create the surface needed by rasterizer
365  PlatformViewNotifyCreated(shell.get());
366 
367  auto configuration = RunConfiguration::InferFromSettings(settings);
368  configuration.SetEntrypoint("dummyReportTimingsMain");
369 
370  RunEngine(shell.get(), std::move(configuration));
371  PumpOneFrame(shell.get());
372  ASSERT_TRUE(GetNeedsReportTimings(shell.get()));
373  DestroyShell(std::move(shell));
374 }
375 
376 static void CheckFrameTimings(const std::vector<FrameTiming>& timings,
377  fml::TimePoint start,
378  fml::TimePoint finish) {
379  fml::TimePoint last_frame_start;
380  for (size_t i = 0; i < timings.size(); i += 1) {
381  // Ensure that timings are sorted.
382  ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start);
383  last_frame_start = timings[i].Get(FrameTiming::kPhases[0]);
384 
385  fml::TimePoint last_phase_time;
386  for (auto phase : FrameTiming::kPhases) {
387  ASSERT_TRUE(timings[i].Get(phase) >= start);
388  ASSERT_TRUE(timings[i].Get(phase) <= finish);
389 
390  // phases should have weakly increasing time points
391  ASSERT_TRUE(last_phase_time <= timings[i].Get(phase));
392  last_phase_time = timings[i].Get(phase);
393  }
394  }
395 }
396 
397 // TODO(43192): This test is disable because of flakiness.
398 TEST_F(ShellTest, DISABLED_ReportTimingsIsCalled) {
400  auto settings = CreateSettingsForFixture();
401  std::unique_ptr<Shell> shell = CreateShell(settings);
402 
403  // Create the surface needed by rasterizer
404  PlatformViewNotifyCreated(shell.get());
405 
406  auto configuration = RunConfiguration::InferFromSettings(settings);
407  ASSERT_TRUE(configuration.IsValid());
408  configuration.SetEntrypoint("reportTimingsMain");
409  fml::AutoResetWaitableEvent reportLatch;
410  std::vector<int64_t> timestamps;
411  auto nativeTimingCallback = [&reportLatch,
412  &timestamps](Dart_NativeArguments args) {
413  Dart_Handle exception = nullptr;
414  timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
415  args, 0, exception);
416  reportLatch.Signal();
417  };
418  AddNativeCallback("NativeReportTimingsCallback",
419  CREATE_NATIVE_ENTRY(nativeTimingCallback));
420  RunEngine(shell.get(), std::move(configuration));
421 
422  // Pump many frames so we can trigger the report quickly instead of waiting
423  // for the 1 second threshold.
424  for (int i = 0; i < 200; i += 1) {
425  PumpOneFrame(shell.get());
426  }
427 
428  reportLatch.Wait();
429  DestroyShell(std::move(shell));
430 
432  ASSERT_TRUE(timestamps.size() > 0);
433  ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0);
434  std::vector<FrameTiming> timings(timestamps.size() / FrameTiming::kCount);
435 
436  for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) {
437  for (auto phase : FrameTiming::kPhases) {
438  timings[i].Set(
439  phase,
441  timestamps[i * FrameTiming::kCount + phase])));
442  }
443  }
444  CheckFrameTimings(timings, start, finish);
445 }
446 
447 TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) {
449 
450  auto settings = CreateSettingsForFixture();
451  fml::AutoResetWaitableEvent timingLatch;
452  FrameTiming timing;
453 
454  for (auto phase : FrameTiming::kPhases) {
455  timing.Set(phase, fml::TimePoint());
456  // Check that the time points are initially smaller than start, so
457  // CheckFrameTimings will fail if they're not properly set later.
458  ASSERT_TRUE(timing.Get(phase) < start);
459  }
460 
461  settings.frame_rasterized_callback = [&timing,
462  &timingLatch](const FrameTiming& t) {
463  timing = t;
464  timingLatch.Signal();
465  };
466 
467  std::unique_ptr<Shell> shell = CreateShell(settings);
468 
469  // Create the surface needed by rasterizer
470  PlatformViewNotifyCreated(shell.get());
471 
472  auto configuration = RunConfiguration::InferFromSettings(settings);
473  configuration.SetEntrypoint("onBeginFrameMain");
474 
475  int64_t frame_target_time;
476  auto nativeOnBeginFrame = [&frame_target_time](Dart_NativeArguments args) {
477  Dart_Handle exception = nullptr;
478  frame_target_time =
480  };
481  AddNativeCallback("NativeOnBeginFrame",
482  CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
483 
484  RunEngine(shell.get(), std::move(configuration));
485  PumpOneFrame(shell.get());
486 
487  // Check that timing is properly set. This implies that
488  // settings.frame_rasterized_callback is called.
489  timingLatch.Wait();
491  std::vector<FrameTiming> timings = {timing};
492  CheckFrameTimings(timings, start, finish);
493 
494  // Check that onBeginFrame, which is the frame_target_time, is after
495  // FrameTiming's build start
496  int64_t build_start =
498  ASSERT_GT(frame_target_time, build_start);
499  DestroyShell(std::move(shell));
500 }
501 
502 TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) {
503  auto settings = CreateSettingsForFixture();
504  fml::AutoResetWaitableEvent end_frame_latch;
505  bool end_frame_called = false;
506  auto end_frame_callback =
507  [&](bool should_resubmit_frame,
508  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
509  ASSERT_TRUE(raster_thread_merger.get() == nullptr);
510  ASSERT_FALSE(should_resubmit_frame);
511  end_frame_called = true;
512  end_frame_latch.Signal();
513  };
514  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
515  end_frame_callback, PostPrerollResult::kResubmitFrame, false);
516  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
517  false, external_view_embedder);
518 
519  // Create the surface needed by rasterizer
520  PlatformViewNotifyCreated(shell.get());
521 
522  auto configuration = RunConfiguration::InferFromSettings(settings);
523  configuration.SetEntrypoint("emptyMain");
524 
525  RunEngine(shell.get(), std::move(configuration));
526 
527  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
528  SkPictureRecorder recorder;
529  SkCanvas* recording_canvas =
530  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
531  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
532  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
533  auto sk_picture = recorder.finishRecordingAsPicture();
534  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
535  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
536  auto picture_layer = std::make_shared<PictureLayer>(
537  SkPoint::Make(10, 10),
538  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
539  root->Add(picture_layer);
540  };
541 
542  PumpOneFrame(shell.get(), 100, 100, builder);
543  end_frame_latch.Wait();
544 
545  ASSERT_TRUE(end_frame_called);
546 
547  DestroyShell(std::move(shell));
548 }
549 
550 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
552 #if defined(OS_FUCHSIA)
553  DISABLED_ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit
554 #else
555  ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit
556 #endif
557 ) {
558  auto settings = CreateSettingsForFixture();
559  fml::AutoResetWaitableEvent end_frame_latch;
560  bool end_frame_called = false;
561  auto end_frame_callback =
562  [&](bool should_resubmit_frame,
563  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
564  ASSERT_TRUE(raster_thread_merger.get() != nullptr);
565  ASSERT_TRUE(should_resubmit_frame);
566  end_frame_called = true;
567  end_frame_latch.Signal();
568  };
569  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
570  end_frame_callback, PostPrerollResult::kResubmitFrame, true);
571  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
572  false, external_view_embedder);
573 
574  // Create the surface needed by rasterizer
575  PlatformViewNotifyCreated(shell.get());
576 
577  auto configuration = RunConfiguration::InferFromSettings(settings);
578  configuration.SetEntrypoint("emptyMain");
579 
580  RunEngine(shell.get(), std::move(configuration));
581 
582  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
583  SkPictureRecorder recorder;
584  SkCanvas* recording_canvas =
585  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
586  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
587  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
588  auto sk_picture = recorder.finishRecordingAsPicture();
589  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
590  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
591  auto picture_layer = std::make_shared<PictureLayer>(
592  SkPoint::Make(10, 10),
593  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
594  root->Add(picture_layer);
595  };
596 
597  PumpOneFrame(shell.get(), 100, 100, builder);
598  end_frame_latch.Wait();
599 
600  ASSERT_TRUE(end_frame_called);
601 
602  DestroyShell(std::move(shell));
603 }
604 
605 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
607 #if defined(OS_FUCHSIA)
608  DISABLED_OnPlatformViewDestroyDisablesThreadMerger
609 #else
610  OnPlatformViewDestroyDisablesThreadMerger
611 #endif
612 ) {
613  auto settings = CreateSettingsForFixture();
614  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;
615  auto end_frame_callback =
616  [&](bool should_resubmit_frame,
617  fml::RefPtr<fml::RasterThreadMerger> thread_merger) {
618  raster_thread_merger = thread_merger;
619  };
620  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
621  end_frame_callback, PostPrerollResult::kSuccess, true);
622 
623  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
624  false, external_view_embedder);
625 
626  // Create the surface needed by rasterizer
627  PlatformViewNotifyCreated(shell.get());
628 
629  auto configuration = RunConfiguration::InferFromSettings(settings);
630  configuration.SetEntrypoint("emptyMain");
631 
632  RunEngine(shell.get(), std::move(configuration));
633 
634  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
635  SkPictureRecorder recorder;
636  SkCanvas* recording_canvas =
637  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
638  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
639  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
640  auto sk_picture = recorder.finishRecordingAsPicture();
641  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
642  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
643  auto picture_layer = std::make_shared<PictureLayer>(
644  SkPoint::Make(10, 10),
645  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
646  root->Add(picture_layer);
647  };
648 
649  PumpOneFrame(shell.get(), 100, 100, builder);
650 
651  auto result =
652  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000));
653  ASSERT_TRUE(result.ok());
654 
655  ASSERT_TRUE(raster_thread_merger->IsEnabled());
656 
657  ValidateDestroyPlatformView(shell.get());
658  ASSERT_TRUE(raster_thread_merger->IsEnabled());
659 
660  // Validate the platform view can be recreated and destroyed again
661  ValidateShell(shell.get());
662  ASSERT_TRUE(raster_thread_merger->IsEnabled());
663 
664  DestroyShell(std::move(shell));
665 }
666 
667 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
669 #if defined(OS_FUCHSIA)
670  DISABLED_OnPlatformViewDestroyAfterMergingThreads
671 #else
672  OnPlatformViewDestroyAfterMergingThreads
673 #endif
674 ) {
675  const size_t ThreadMergingLease = 10;
676  auto settings = CreateSettingsForFixture();
677  fml::AutoResetWaitableEvent end_frame_latch;
678  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
679 
680  auto end_frame_callback =
681  [&](bool should_resubmit_frame,
682  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
683  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
684  raster_thread_merger->MergeWithLease(ThreadMergingLease);
685 
686  ASSERT_TRUE(raster_thread_merger->IsMerged());
687  external_view_embedder->UpdatePostPrerollResult(
689  }
690  end_frame_latch.Signal();
691  };
692  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
693  end_frame_callback, PostPrerollResult::kSuccess, true);
694  // Set resubmit once to trigger thread merging.
695  external_view_embedder->UpdatePostPrerollResult(
697  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
698  false, external_view_embedder);
699 
700  // Create the surface needed by rasterizer
701  PlatformViewNotifyCreated(shell.get());
702 
703  auto configuration = RunConfiguration::InferFromSettings(settings);
704  configuration.SetEntrypoint("emptyMain");
705 
706  RunEngine(shell.get(), std::move(configuration));
707 
708  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
709  SkPictureRecorder recorder;
710  SkCanvas* recording_canvas =
711  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
712  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
713  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
714  auto sk_picture = recorder.finishRecordingAsPicture();
715  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
716  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
717  auto picture_layer = std::make_shared<PictureLayer>(
718  SkPoint::Make(10, 10),
719  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
720  root->Add(picture_layer);
721  };
722 
723  PumpOneFrame(shell.get(), 100, 100, builder);
724  // Pump one frame to trigger thread merging.
725  end_frame_latch.Wait();
726  // Pump another frame to ensure threads are merged and a regular layer tree is
727  // submitted.
728  PumpOneFrame(shell.get(), 100, 100, builder);
729  // Threads are merged here. PlatformViewNotifyDestroy should be executed
730  // successfully.
732  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
733  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
734  ValidateDestroyPlatformView(shell.get());
735 
736  // Ensure threads are unmerged after platform view destroy
738  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
739  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
740 
741  // Validate the platform view can be recreated and destroyed again
742  ValidateShell(shell.get());
743 
744  DestroyShell(std::move(shell));
745 }
746 
747 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
749 #if defined(OS_FUCHSIA)
750  DISABLED_OnPlatformViewDestroyWhenThreadsAreMerging
751 #else
752  OnPlatformViewDestroyWhenThreadsAreMerging
753 #endif
754 ) {
755  const size_t kThreadMergingLease = 10;
756  auto settings = CreateSettingsForFixture();
757  fml::AutoResetWaitableEvent end_frame_latch;
758  auto end_frame_callback =
759  [&](bool should_resubmit_frame,
760  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
761  if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
762  raster_thread_merger->MergeWithLease(kThreadMergingLease);
763  }
764  end_frame_latch.Signal();
765  };
766  // Start with a regular layer tree with `PostPrerollResult::kSuccess` so we
767  // can later check if the rasterizer is tore down using
768  // |ValidateDestroyPlatformView|
769  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
770  end_frame_callback, PostPrerollResult::kSuccess, true);
771 
772  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
773  false, external_view_embedder);
774 
775  // Create the surface needed by rasterizer
776  PlatformViewNotifyCreated(shell.get());
777 
778  auto configuration = RunConfiguration::InferFromSettings(settings);
779  configuration.SetEntrypoint("emptyMain");
780 
781  RunEngine(shell.get(), std::move(configuration));
782 
783  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
784  SkPictureRecorder recorder;
785  SkCanvas* recording_canvas =
786  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
787  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
788  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
789  auto sk_picture = recorder.finishRecordingAsPicture();
790  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
791  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
792  auto picture_layer = std::make_shared<PictureLayer>(
793  SkPoint::Make(10, 10),
794  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
795  root->Add(picture_layer);
796  };
797 
798  PumpOneFrame(shell.get(), 100, 100, builder);
799  // Pump one frame and threads aren't merged
800  end_frame_latch.Wait();
802  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
803  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
804 
805  // Pump a frame with `PostPrerollResult::kResubmitFrame` to start merging
806  // threads
807  external_view_embedder->UpdatePostPrerollResult(
809  PumpOneFrame(shell.get(), 100, 100, builder);
810 
811  // Now destroy the platform view immediately.
812  // Two things can happen here:
813  // 1. Threads haven't merged. 2. Threads has already merged.
814  // |Shell:OnPlatformViewDestroy| should be able to handle both cases.
815  ValidateDestroyPlatformView(shell.get());
816 
817  // Ensure threads are unmerged after platform view destroy
819  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
820  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
821 
822  // Validate the platform view can be recreated and destroyed again
823  ValidateShell(shell.get());
824 
825  DestroyShell(std::move(shell));
826 }
827 
828 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
830 #if defined(OS_FUCHSIA)
831  DISABLED_OnPlatformViewDestroyWithThreadMergerWhileThreadsAreUnmerged
832 #else
833  OnPlatformViewDestroyWithThreadMergerWhileThreadsAreUnmerged
834 #endif
835 ) {
836  auto settings = CreateSettingsForFixture();
837  fml::AutoResetWaitableEvent end_frame_latch;
838  auto end_frame_callback =
839  [&](bool should_resubmit_frame,
840  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
841  end_frame_latch.Signal();
842  };
843  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
844  end_frame_callback, PostPrerollResult::kSuccess, true);
845  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
846  false, external_view_embedder);
847 
848  // Create the surface needed by rasterizer
849  PlatformViewNotifyCreated(shell.get());
850 
851  auto configuration = RunConfiguration::InferFromSettings(settings);
852  configuration.SetEntrypoint("emptyMain");
853 
854  RunEngine(shell.get(), std::move(configuration));
855 
856  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
857  SkPictureRecorder recorder;
858  SkCanvas* recording_canvas =
859  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
860  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
861  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
862  auto sk_picture = recorder.finishRecordingAsPicture();
863  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
864  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
865  auto picture_layer = std::make_shared<PictureLayer>(
866  SkPoint::Make(10, 10),
867  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
868  root->Add(picture_layer);
869  };
870  PumpOneFrame(shell.get(), 100, 100, builder);
871  end_frame_latch.Wait();
872 
873  // Threads should not be merged.
875  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
876  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
877  ValidateDestroyPlatformView(shell.get());
878 
879  // Ensure threads are unmerged after platform view destroy
881  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
882  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
883 
884  // Validate the platform view can be recreated and destroyed again
885  ValidateShell(shell.get());
886 
887  DestroyShell(std::move(shell));
888 }
889 
890 TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) {
891  auto settings = CreateSettingsForFixture();
892 
893  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
894  false, nullptr);
895 
896  // Create the surface needed by rasterizer
897  PlatformViewNotifyCreated(shell.get());
898 
899  auto configuration = RunConfiguration::InferFromSettings(settings);
900  configuration.SetEntrypoint("emptyMain");
901 
902  RunEngine(shell.get(), std::move(configuration));
903 
904  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
905  SkPictureRecorder recorder;
906  SkCanvas* recording_canvas =
907  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
908  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
909  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
910  auto sk_picture = recorder.finishRecordingAsPicture();
911  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
912  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
913  auto picture_layer = std::make_shared<PictureLayer>(
914  SkPoint::Make(10, 10),
915  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
916  root->Add(picture_layer);
917  };
918  PumpOneFrame(shell.get(), 100, 100, builder);
919 
920  // Threads should not be merged.
922  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
923  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
924  ValidateDestroyPlatformView(shell.get());
925 
926  // Ensure threads are unmerged after platform view destroy
928  shell->GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId(),
929  shell->GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId()));
930 
931  // Validate the platform view can be recreated and destroyed again
932  ValidateShell(shell.get());
933 
934  DestroyShell(std::move(shell));
935 }
936 
937 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
939 #if defined(OS_FUCHSIA)
940  DISABLED_OnPlatformViewDestroyWithStaticThreadMerging
941 #else
942  OnPlatformViewDestroyWithStaticThreadMerging
943 #endif
944 ) {
945  auto settings = CreateSettingsForFixture();
946  fml::AutoResetWaitableEvent end_frame_latch;
947  auto end_frame_callback =
948  [&](bool should_resubmit_frame,
949  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
950  end_frame_latch.Signal();
951  };
952  auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
953  end_frame_callback, PostPrerollResult::kSuccess, true);
954  ThreadHost thread_host(
955  "io.flutter.test." + GetCurrentTestName() + ".",
956  ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI);
957  TaskRunners task_runners(
958  "test",
959  thread_host.platform_thread->GetTaskRunner(), // platform
960  thread_host.platform_thread->GetTaskRunner(), // raster
961  thread_host.ui_thread->GetTaskRunner(), // ui
962  thread_host.io_thread->GetTaskRunner() // io
963  );
964  auto shell = CreateShell(std::move(settings), std::move(task_runners), false,
965  external_view_embedder);
966 
967  // Create the surface needed by rasterizer
968  PlatformViewNotifyCreated(shell.get());
969 
970  auto configuration = RunConfiguration::InferFromSettings(settings);
971  configuration.SetEntrypoint("emptyMain");
972 
973  RunEngine(shell.get(), std::move(configuration));
974 
975  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
976  SkPictureRecorder recorder;
977  SkCanvas* recording_canvas =
978  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
979  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
980  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
981  auto sk_picture = recorder.finishRecordingAsPicture();
982  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
983  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
984  auto picture_layer = std::make_shared<PictureLayer>(
985  SkPoint::Make(10, 10),
986  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
987  root->Add(picture_layer);
988  };
989  PumpOneFrame(shell.get(), 100, 100, builder);
990  end_frame_latch.Wait();
991 
992  ValidateDestroyPlatformView(shell.get());
993 
994  // Validate the platform view can be recreated and destroyed again
995  ValidateShell(shell.get());
996 
997  DestroyShell(std::move(shell), std::move(task_runners));
998 }
999 
1000 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1001 // TODO(https://github.com/flutter/flutter/issues/66056): Deflake on all other
1002 // platforms
1003 TEST_F(ShellTest, DISABLED_SkipAndSubmitFrame) {
1004  auto settings = CreateSettingsForFixture();
1005  fml::AutoResetWaitableEvent end_frame_latch;
1006  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1007 
1008  auto end_frame_callback =
1009  [&](bool should_resubmit_frame,
1010  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1011  external_view_embedder->UpdatePostPrerollResult(
1013  end_frame_latch.Signal();
1014  };
1015  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1016  end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true);
1017 
1018  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1019  false, external_view_embedder);
1020 
1021  PlatformViewNotifyCreated(shell.get());
1022 
1023  auto configuration = RunConfiguration::InferFromSettings(settings);
1024  configuration.SetEntrypoint("emptyMain");
1025  RunEngine(shell.get(), std::move(configuration));
1026 
1027  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1028 
1029  PumpOneFrame(shell.get());
1030 
1031  // `EndFrame` changed the post preroll result to `kSuccess`.
1032  end_frame_latch.Wait();
1033  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1034 
1035  PumpOneFrame(shell.get());
1036  end_frame_latch.Wait();
1037  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1038 
1039  DestroyShell(std::move(shell));
1040 }
1041 
1042 // TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
1044 #if defined(OS_FUCHSIA)
1045  DISABLED_ResubmitFrame
1046 #else
1047  ResubmitFrame
1048 #endif
1049 ) {
1050  auto settings = CreateSettingsForFixture();
1051  fml::AutoResetWaitableEvent end_frame_latch;
1052  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1053 
1054  auto end_frame_callback =
1055  [&](bool should_resubmit_frame,
1056  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1057  external_view_embedder->UpdatePostPrerollResult(
1059  end_frame_latch.Signal();
1060  };
1061  external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
1062  end_frame_callback, PostPrerollResult::kResubmitFrame, true);
1063 
1064  auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
1065  false, external_view_embedder);
1066 
1067  PlatformViewNotifyCreated(shell.get());
1068 
1069  auto configuration = RunConfiguration::InferFromSettings(settings);
1070  configuration.SetEntrypoint("emptyMain");
1071  RunEngine(shell.get(), std::move(configuration));
1072 
1073  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
1074 
1075  PumpOneFrame(shell.get());
1076  // `EndFrame` changed the post preroll result to `kSuccess`.
1077  end_frame_latch.Wait();
1078  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1079 
1080  end_frame_latch.Wait();
1081  ASSERT_EQ(2, external_view_embedder->GetSubmittedFrameCount());
1082 
1083  DestroyShell(std::move(shell));
1084 }
1085 
1086 TEST(SettingsTest, FrameTimingSetsAndGetsProperly) {
1087  // Ensure that all phases are in kPhases.
1088  ASSERT_EQ(sizeof(FrameTiming::kPhases),
1090 
1091  int lastPhaseIndex = -1;
1092  FrameTiming timing;
1093  for (auto phase : FrameTiming::kPhases) {
1094  ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in order.
1095  lastPhaseIndex = phase;
1096  auto fake_time =
1098  timing.Set(phase, fake_time);
1099  ASSERT_TRUE(timing.Get(phase) == fake_time);
1100  }
1101 }
1102 
1103 #if FLUTTER_RELEASE
1104 TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) {
1105 #else
1106 TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) {
1107 #endif
1109  auto settings = CreateSettingsForFixture();
1110  std::unique_ptr<Shell> shell = CreateShell(settings);
1111 
1112  // Create the surface needed by rasterizer
1113  PlatformViewNotifyCreated(shell.get());
1114 
1115  auto configuration = RunConfiguration::InferFromSettings(settings);
1116  ASSERT_TRUE(configuration.IsValid());
1117  configuration.SetEntrypoint("reportTimingsMain");
1118 
1119  // Wait for 2 reports: the first one is the immediate callback of the first
1120  // frame; the second one will exercise the batching logic.
1121  fml::CountDownLatch reportLatch(2);
1122  std::vector<int64_t> timestamps;
1123  auto nativeTimingCallback = [&reportLatch,
1124  &timestamps](Dart_NativeArguments args) {
1125  Dart_Handle exception = nullptr;
1126  timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
1127  args, 0, exception);
1128  reportLatch.CountDown();
1129  };
1130  AddNativeCallback("NativeReportTimingsCallback",
1131  CREATE_NATIVE_ENTRY(nativeTimingCallback));
1132  RunEngine(shell.get(), std::move(configuration));
1133 
1134  PumpOneFrame(shell.get());
1135  PumpOneFrame(shell.get());
1136 
1137  reportLatch.Wait();
1138  DestroyShell(std::move(shell));
1139 
1141  fml::TimeDelta elapsed = finish - start;
1142 
1143 #if FLUTTER_RELEASE
1144  // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to
1145  // make it not too flaky.
1146  ASSERT_TRUE(elapsed >= fml::TimeDelta::FromMilliseconds(800));
1147 #else
1148  // Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to
1149  // make it not too flaky.
1150  ASSERT_TRUE(elapsed <= fml::TimeDelta::FromMilliseconds(500));
1151 #endif
1152 }
1153 
1154 TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) {
1155  auto settings = CreateSettingsForFixture();
1156  std::unique_ptr<Shell> shell = CreateShell(settings);
1157 
1158  // Create the surface needed by rasterizer
1159  PlatformViewNotifyCreated(shell.get());
1160 
1161  auto configuration = RunConfiguration::InferFromSettings(settings);
1162  ASSERT_TRUE(configuration.IsValid());
1163  configuration.SetEntrypoint("reportTimingsMain");
1164  fml::AutoResetWaitableEvent reportLatch;
1165  std::vector<int64_t> timestamps;
1166  auto nativeTimingCallback = [&reportLatch,
1167  &timestamps](Dart_NativeArguments args) {
1168  Dart_Handle exception = nullptr;
1169  timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
1170  args, 0, exception);
1171  reportLatch.Signal();
1172  };
1173  AddNativeCallback("NativeReportTimingsCallback",
1174  CREATE_NATIVE_ENTRY(nativeTimingCallback));
1175  RunEngine(shell.get(), std::move(configuration));
1176 
1177  for (int i = 0; i < 10; i += 1) {
1178  PumpOneFrame(shell.get());
1179  }
1180 
1181  reportLatch.Wait();
1182  DestroyShell(std::move(shell));
1183 
1184  // Check for the immediate callback of the first frame that doesn't wait for
1185  // the other 9 frames to be rasterized.
1186  ASSERT_EQ(timestamps.size(), FrameTiming::kCount);
1187 }
1188 
1189 TEST_F(ShellTest, ReloadSystemFonts) {
1190  auto settings = CreateSettingsForFixture();
1191 
1193  auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
1194  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1195  task_runner);
1196  auto shell = CreateShell(std::move(settings), std::move(task_runners));
1197 
1198  auto fontCollection = GetFontCollection(shell.get());
1199  std::vector<std::string> families(1, "Robotofake");
1200  auto font =
1201  fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1202  if (font == nullptr) {
1203  // The system does not have default font. Aborts this test.
1204  return;
1205  }
1206  unsigned int id = font->getId();
1207  // The result should be cached.
1208  font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1209  ASSERT_EQ(font->getId(), id);
1210  bool result = shell->ReloadSystemFonts();
1211 
1212  // The cache is cleared, and FontCollection will be assigned a new id.
1213  font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en");
1214  ASSERT_NE(font->getId(), id);
1215  ASSERT_TRUE(result);
1216  shell.reset();
1217 }
1218 
1219 TEST_F(ShellTest, WaitForFirstFrame) {
1220  auto settings = CreateSettingsForFixture();
1221  std::unique_ptr<Shell> shell = CreateShell(settings);
1222 
1223  // Create the surface needed by rasterizer
1224  PlatformViewNotifyCreated(shell.get());
1225 
1226  auto configuration = RunConfiguration::InferFromSettings(settings);
1227  configuration.SetEntrypoint("emptyMain");
1228 
1229  RunEngine(shell.get(), std::move(configuration));
1230  PumpOneFrame(shell.get());
1231  fml::Status result =
1232  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000));
1233  ASSERT_TRUE(result.ok());
1234 
1235  DestroyShell(std::move(shell));
1236 }
1237 
1238 TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) {
1239  auto settings = CreateSettingsForFixture();
1240  std::unique_ptr<Shell> shell = CreateShell(settings);
1241 
1242  // Create the surface needed by rasterizer
1243  PlatformViewNotifyCreated(shell.get());
1244 
1245  auto configuration = RunConfiguration::InferFromSettings(settings);
1246  configuration.SetEntrypoint("emptyMain");
1247 
1248  RunEngine(shell.get(), std::move(configuration));
1249  PumpOneFrame(shell.get(), {1.0, 0.0, 0.0});
1250  fml::Status result =
1251  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000));
1252  ASSERT_FALSE(result.ok());
1253  ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1254 
1255  DestroyShell(std::move(shell));
1256 }
1257 
1258 TEST_F(ShellTest, WaitForFirstFrameTimeout) {
1259  auto settings = CreateSettingsForFixture();
1260  std::unique_ptr<Shell> shell = CreateShell(settings);
1261 
1262  // Create the surface needed by rasterizer
1263  PlatformViewNotifyCreated(shell.get());
1264 
1265  auto configuration = RunConfiguration::InferFromSettings(settings);
1266  configuration.SetEntrypoint("emptyMain");
1267 
1268  RunEngine(shell.get(), std::move(configuration));
1269  fml::Status result =
1270  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10));
1271  ASSERT_FALSE(result.ok());
1272  ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
1273 
1274  DestroyShell(std::move(shell));
1275 }
1276 
1277 TEST_F(ShellTest, WaitForFirstFrameMultiple) {
1278  auto settings = CreateSettingsForFixture();
1279  std::unique_ptr<Shell> shell = CreateShell(settings);
1280 
1281  // Create the surface needed by rasterizer
1282  PlatformViewNotifyCreated(shell.get());
1283 
1284  auto configuration = RunConfiguration::InferFromSettings(settings);
1285  configuration.SetEntrypoint("emptyMain");
1286 
1287  RunEngine(shell.get(), std::move(configuration));
1288  PumpOneFrame(shell.get());
1289  fml::Status result =
1290  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000));
1291  ASSERT_TRUE(result.ok());
1292  for (int i = 0; i < 100; ++i) {
1293  result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1));
1294  ASSERT_TRUE(result.ok());
1295  }
1296 
1297  DestroyShell(std::move(shell));
1298 }
1299 
1300 /// Makes sure that WaitForFirstFrame works if we rendered a frame with the
1301 /// single-thread setup.
1302 TEST_F(ShellTest, WaitForFirstFrameInlined) {
1303  Settings settings = CreateSettingsForFixture();
1304  auto task_runner = CreateNewThread();
1305  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1306  task_runner);
1307  std::unique_ptr<Shell> shell =
1308  CreateShell(std::move(settings), std::move(task_runners));
1309 
1310  // Create the surface needed by rasterizer
1311  PlatformViewNotifyCreated(shell.get());
1312 
1313  auto configuration = RunConfiguration::InferFromSettings(settings);
1314  configuration.SetEntrypoint("emptyMain");
1315 
1316  RunEngine(shell.get(), std::move(configuration));
1317  PumpOneFrame(shell.get());
1319  task_runner->PostTask([&shell, &event] {
1320  fml::Status result =
1321  shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000));
1322  ASSERT_FALSE(result.ok());
1323  ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition);
1324  event.Signal();
1325  });
1326  ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000)));
1327 
1328  DestroyShell(std::move(shell), std::move(task_runners));
1329 }
1330 
1331 static size_t GetRasterizerResourceCacheBytesSync(const Shell& shell) {
1332  size_t bytes = 0;
1335  shell.GetTaskRunners().GetRasterTaskRunner(), [&]() {
1336  if (auto rasterizer = shell.GetRasterizer()) {
1337  bytes = rasterizer->GetResourceCacheMaxBytes().value_or(0U);
1338  }
1339  latch.Signal();
1340  });
1341  latch.Wait();
1342  return bytes;
1343 }
1344 
1345 TEST_F(ShellTest, SetResourceCacheSize) {
1346  Settings settings = CreateSettingsForFixture();
1347  auto task_runner = CreateNewThread();
1348  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1349  task_runner);
1350  std::unique_ptr<Shell> shell =
1351  CreateShell(std::move(settings), std::move(task_runners));
1352 
1353  // Create the surface needed by rasterizer
1354  PlatformViewNotifyCreated(shell.get());
1355 
1356  auto configuration = RunConfiguration::InferFromSettings(settings);
1357  configuration.SetEntrypoint("emptyMain");
1358 
1359  RunEngine(shell.get(), std::move(configuration));
1360  PumpOneFrame(shell.get());
1361 
1362  // The Vulkan and GL backends set different default values for the resource
1363  // cache size.
1364 #ifdef SHELL_ENABLE_VULKAN
1365  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1367 #else
1368  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1369  static_cast<size_t>(24 * (1 << 20)));
1370 #endif
1371 
1373  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1374  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200});
1375  });
1376  PumpOneFrame(shell.get());
1377 
1378  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 3840000U);
1379 
1380  std::string request_json = R"json({
1381  "method": "Skia.setResourceCacheMaxBytes",
1382  "args": 10000
1383  })json";
1384  std::vector<uint8_t> data(request_json.begin(), request_json.end());
1385  auto platform_message = fml::MakeRefCounted<PlatformMessage>(
1386  "flutter/skia", std::move(data), nullptr);
1387  SendEnginePlatformMessage(shell.get(), std::move(platform_message));
1388  PumpOneFrame(shell.get());
1389  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1390 
1392  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1393  shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 400});
1394  });
1395  PumpOneFrame(shell.get());
1396 
1397  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), 10000U);
1398  DestroyShell(std::move(shell), std::move(task_runners));
1399 }
1400 
1401 TEST_F(ShellTest, SetResourceCacheSizeEarly) {
1402  Settings settings = CreateSettingsForFixture();
1403  auto task_runner = CreateNewThread();
1404  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1405  task_runner);
1406  std::unique_ptr<Shell> shell =
1407  CreateShell(std::move(settings), std::move(task_runners));
1408 
1410  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1411  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200});
1412  });
1413  PumpOneFrame(shell.get());
1414 
1415  // Create the surface needed by rasterizer
1416  PlatformViewNotifyCreated(shell.get());
1417 
1418  auto configuration = RunConfiguration::InferFromSettings(settings);
1419  configuration.SetEntrypoint("emptyMain");
1420 
1421  RunEngine(shell.get(), std::move(configuration));
1422  PumpOneFrame(shell.get());
1423 
1424  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1425  static_cast<size_t>(3840000U));
1426  DestroyShell(std::move(shell), std::move(task_runners));
1427 }
1428 
1429 TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) {
1430  Settings settings = CreateSettingsForFixture();
1431  auto task_runner = CreateNewThread();
1432  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1433  task_runner);
1434  std::unique_ptr<Shell> shell =
1435  CreateShell(std::move(settings), std::move(task_runners));
1436 
1438  shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
1439  shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200});
1440  });
1441  PumpOneFrame(shell.get());
1442 
1443  // Create the surface needed by rasterizer
1444  PlatformViewNotifyCreated(shell.get());
1445 
1446  auto configuration = RunConfiguration::InferFromSettings(settings);
1447  configuration.SetEntrypoint("testSkiaResourceCacheSendsResponse");
1448 
1449  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1450  static_cast<size_t>(3840000U));
1451 
1453  AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) {
1454  latch.Signal();
1455  }));
1456 
1457  RunEngine(shell.get(), std::move(configuration));
1458  PumpOneFrame(shell.get());
1459 
1460  latch.Wait();
1461 
1462  EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
1463  static_cast<size_t>(10000U));
1464  DestroyShell(std::move(shell), std::move(task_runners));
1465 }
1466 
1467 TEST_F(ShellTest, CanCreateImagefromDecompressedBytes) {
1468  Settings settings = CreateSettingsForFixture();
1469  auto task_runner = CreateNewThread();
1470 
1471  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1472  task_runner);
1473 
1474  std::unique_ptr<Shell> shell =
1475  CreateShell(std::move(settings), std::move(task_runners));
1476 
1477  // Create the surface needed by rasterizer
1478  PlatformViewNotifyCreated(shell.get());
1479 
1480  auto configuration = RunConfiguration::InferFromSettings(settings);
1481  configuration.SetEntrypoint("canCreateImageFromDecompressedData");
1482 
1484  AddNativeCallback("NotifyWidthHeight",
1485  CREATE_NATIVE_ENTRY([&latch](auto args) {
1487  Dart_GetNativeArgument(args, 0));
1489  Dart_GetNativeArgument(args, 1));
1490  ASSERT_EQ(width, 10);
1491  ASSERT_EQ(height, 10);
1492  latch.Signal();
1493  }));
1494 
1495  RunEngine(shell.get(), std::move(configuration));
1496 
1497  latch.Wait();
1498  DestroyShell(std::move(shell), std::move(task_runners));
1499 }
1500 
1501 class MockTexture : public Texture {
1502  public:
1503  MockTexture(int64_t textureId,
1504  std::shared_ptr<fml::AutoResetWaitableEvent> latch)
1505  : Texture(textureId), latch_(latch) {}
1506 
1507  ~MockTexture() override = default;
1508 
1509  // Called from raster thread.
1510  void Paint(SkCanvas& canvas,
1511  const SkRect& bounds,
1512  bool freeze,
1513  GrDirectContext* context,
1514  SkFilterQuality filter_quality) override {}
1515 
1516  void OnGrContextCreated() override {}
1517 
1518  void OnGrContextDestroyed() override {}
1519 
1520  void MarkNewFrameAvailable() override {
1521  frames_available_++;
1522  latch_->Signal();
1523  }
1524 
1525  void OnTextureUnregistered() override {
1526  unregistered_ = true;
1527  latch_->Signal();
1528  }
1529 
1530  bool unregistered() { return unregistered_; }
1531  int frames_available() { return frames_available_; }
1532 
1533  private:
1534  bool unregistered_ = false;
1535  int frames_available_ = 0;
1536  std::shared_ptr<fml::AutoResetWaitableEvent> latch_;
1537 };
1538 
1539 TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) {
1540  Settings settings = CreateSettingsForFixture();
1541  auto configuration = RunConfiguration::InferFromSettings(settings);
1542  auto task_runner = CreateNewThread();
1543  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1544  task_runner);
1545  std::unique_ptr<Shell> shell =
1546  CreateShell(std::move(settings), std::move(task_runners));
1547 
1548  ASSERT_TRUE(ValidateShell(shell.get()));
1549  PlatformViewNotifyCreated(shell.get());
1550 
1551  RunEngine(shell.get(), std::move(configuration));
1552 
1553  std::shared_ptr<fml::AutoResetWaitableEvent> latch =
1554  std::make_shared<fml::AutoResetWaitableEvent>();
1555 
1556  std::shared_ptr<MockTexture> mockTexture =
1557  std::make_shared<MockTexture>(0, latch);
1558 
1560  shell->GetTaskRunners().GetRasterTaskRunner(), [&]() {
1561  shell->GetPlatformView()->RegisterTexture(mockTexture);
1562  shell->GetPlatformView()->MarkTextureFrameAvailable(0);
1563  });
1564  latch->Wait();
1565 
1566  EXPECT_EQ(mockTexture->frames_available(), 1);
1567 
1569  shell->GetTaskRunners().GetRasterTaskRunner(),
1570  [&]() { shell->GetPlatformView()->UnregisterTexture(0); });
1571  latch->Wait();
1572 
1573  EXPECT_EQ(mockTexture->unregistered(), true);
1574  DestroyShell(std::move(shell), std::move(task_runners));
1575 }
1576 
1577 TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) {
1578  const std::string message = "dummy isolate launch data.";
1579 
1580  Settings settings = CreateSettingsForFixture();
1581  settings.persistent_isolate_data =
1582  std::make_shared<fml::DataMapping>(message);
1583  TaskRunners task_runners("test", // label
1584  GetCurrentTaskRunner(), // platform
1585  CreateNewThread(), // raster
1586  CreateNewThread(), // ui
1587  CreateNewThread() // io
1588  );
1589 
1590  fml::AutoResetWaitableEvent message_latch;
1591  AddNativeCallback("NotifyMessage",
1592  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1593  const auto message_from_dart =
1595  Dart_GetNativeArgument(args, 0));
1596  ASSERT_EQ(message, message_from_dart);
1597  message_latch.Signal();
1598  }));
1599 
1600  std::unique_ptr<Shell> shell =
1601  CreateShell(std::move(settings), std::move(task_runners));
1602 
1603  ASSERT_TRUE(shell->IsSetup());
1604  auto configuration = RunConfiguration::InferFromSettings(settings);
1605  configuration.SetEntrypoint("canAccessIsolateLaunchData");
1606 
1608  shell->RunEngine(std::move(configuration), [&](auto result) {
1609  ASSERT_EQ(result, Engine::RunStatus::Success);
1610  });
1611 
1612  message_latch.Wait();
1613  DestroyShell(std::move(shell), std::move(task_runners));
1614 }
1615 
1616 static void LogSkData(sk_sp<SkData> data, const char* title) {
1617  FML_LOG(ERROR) << "---------- " << title;
1618  std::ostringstream ostr;
1619  for (size_t i = 0; i < data->size();) {
1620  ostr << std::hex << std::setfill('0') << std::setw(2)
1621  << static_cast<int>(data->bytes()[i]) << " ";
1622  i++;
1623  if (i % 16 == 0 || i == data->size()) {
1624  FML_LOG(ERROR) << ostr.str();
1625  ostr.str("");
1626  ostr.clear();
1627  }
1628  }
1629 }
1630 
1631 TEST_F(ShellTest, Screenshot) {
1632  auto settings = CreateSettingsForFixture();
1633  fml::AutoResetWaitableEvent firstFrameLatch;
1634  settings.frame_rasterized_callback =
1635  [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); };
1636 
1637  std::unique_ptr<Shell> shell = CreateShell(settings);
1638 
1639  // Create the surface needed by rasterizer
1640  PlatformViewNotifyCreated(shell.get());
1641 
1642  auto configuration = RunConfiguration::InferFromSettings(settings);
1643  configuration.SetEntrypoint("emptyMain");
1644 
1645  RunEngine(shell.get(), std::move(configuration));
1646 
1647  LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
1648  SkPictureRecorder recorder;
1649  SkCanvas* recording_canvas =
1650  recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80));
1651  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80),
1652  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1653  auto sk_picture = recorder.finishRecordingAsPicture();
1654  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1655  this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
1656  auto picture_layer = std::make_shared<PictureLayer>(
1657  SkPoint::Make(10, 10),
1658  flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false);
1659  root->Add(picture_layer);
1660  };
1661 
1662  PumpOneFrame(shell.get(), 100, 100, builder);
1663  firstFrameLatch.Wait();
1664 
1665  std::promise<Rasterizer::Screenshot> screenshot_promise;
1666  auto screenshot_future = screenshot_promise.get_future();
1667 
1669  shell->GetTaskRunners().GetRasterTaskRunner(),
1670  [&screenshot_promise, &shell]() {
1671  auto rasterizer = shell->GetRasterizer();
1672  screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree(
1674  });
1675 
1676  auto fixtures_dir =
1678 
1679  auto reference_png = fml::FileMapping::CreateReadOnly(
1680  fixtures_dir, "shelltest_screenshot.png");
1681 
1682  // Use MakeWithoutCopy instead of MakeWithCString because we don't want to
1683  // encode the null sentinel
1684  sk_sp<SkData> reference_data = SkData::MakeWithoutCopy(
1685  reference_png->GetMapping(), reference_png->GetSize());
1686 
1687  sk_sp<SkData> screenshot_data = screenshot_future.get().data;
1688  if (!reference_data->equals(screenshot_data.get())) {
1689  LogSkData(reference_data, "reference");
1690  LogSkData(screenshot_data, "screenshot");
1691  ASSERT_TRUE(false);
1692  }
1693 
1694  DestroyShell(std::move(shell));
1695 }
1696 
1697 TEST_F(ShellTest, CanConvertToAndFromMappings) {
1698  const size_t buffer_size = 2 << 20;
1699 
1700  uint8_t* buffer = static_cast<uint8_t*>(::malloc(buffer_size));
1701  ASSERT_NE(buffer, nullptr);
1702  ASSERT_TRUE(MemsetPatternSetOrCheck(
1703  buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer));
1704 
1705  std::unique_ptr<fml::Mapping> mapping =
1706  std::make_unique<fml::NonOwnedMapping>(
1707  buffer, buffer_size, [](const uint8_t* buffer, size_t size) {
1708  ::free(const_cast<uint8_t*>(buffer));
1709  });
1710 
1711  ASSERT_EQ(mapping->GetSize(), buffer_size);
1712 
1714  AddNativeCallback(
1715  "SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1716  auto mapping_from_dart =
1718  Dart_GetNativeArgument(args, 0));
1719  ASSERT_NE(mapping_from_dart, nullptr);
1720  ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size);
1721  ASSERT_TRUE(MemsetPatternSetOrCheck(
1722  const_cast<uint8_t*>(mapping_from_dart->GetMapping()), // buffer
1723  mapping_from_dart->GetSize(), // size
1725  ));
1726  latch.Signal();
1727  }));
1728 
1729  AddNativeCallback(
1730  "GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1732  args, mapping);
1733  }));
1734 
1735  auto settings = CreateSettingsForFixture();
1736  auto configuration = RunConfiguration::InferFromSettings(settings);
1737  configuration.SetEntrypoint("canConvertMappings");
1738  std::unique_ptr<Shell> shell = CreateShell(settings);
1739  ASSERT_NE(shell.get(), nullptr);
1740  RunEngine(shell.get(), std::move(configuration));
1741  latch.Wait();
1742  DestroyShell(std::move(shell));
1743 }
1744 
1745 // Compares local times as seen by the dart isolate and as seen by this test
1746 // fixture, to a resolution of 1 hour.
1747 //
1748 // This verifies that (1) the isolate is able to get a timezone (doesn't lock
1749 // up for example), and (2) that the host and the isolate agree on what the
1750 // timezone is.
1751 TEST_F(ShellTest, LocaltimesMatch) {
1753  std::string dart_isolate_time_str;
1754 
1755  // See fixtures/shell_test.dart, the callback NotifyLocalTime is declared
1756  // there.
1757  AddNativeCallback("NotifyLocalTime", CREATE_NATIVE_ENTRY([&](auto args) {
1758  dart_isolate_time_str =
1760  Dart_GetNativeArgument(args, 0));
1761  latch.Signal();
1762  }));
1763 
1764  auto settings = CreateSettingsForFixture();
1765  auto configuration = RunConfiguration::InferFromSettings(settings);
1766  configuration.SetEntrypoint("localtimesMatch");
1767  std::unique_ptr<Shell> shell = CreateShell(settings);
1768  ASSERT_NE(shell.get(), nullptr);
1769  RunEngine(shell.get(), std::move(configuration));
1770  latch.Wait();
1771 
1772  char timestr[200];
1773  const time_t timestamp = time(nullptr);
1774  const struct tm* local_time = localtime(&timestamp);
1775  ASSERT_NE(local_time, nullptr)
1776  << "Could not get local time: errno=" << errno << ": " << strerror(errno);
1777  // Example: "2020-02-26 14" for 2pm on February 26, 2020.
1778  const size_t format_size =
1779  strftime(timestr, sizeof(timestr), "%Y-%m-%d %H", local_time);
1780  ASSERT_NE(format_size, 0UL)
1781  << "strftime failed: host time: " << std::string(timestr)
1782  << " dart isolate time: " << dart_isolate_time_str;
1783 
1784  const std::string host_local_time_str = timestr;
1785 
1786  ASSERT_EQ(dart_isolate_time_str, host_local_time_str)
1787  << "Local times in the dart isolate and the local time seen by the test "
1788  << "differ by more than 1 hour, but are expected to be about equal";
1789 
1790  DestroyShell(std::move(shell));
1791 }
1792 
1793 TEST_F(ShellTest, CanDecompressImageFromAsset) {
1795  AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
1797  Dart_GetNativeArgument(args, 0));
1799  Dart_GetNativeArgument(args, 1));
1800  ASSERT_EQ(width, 100);
1801  ASSERT_EQ(height, 100);
1802  latch.Signal();
1803  }));
1804 
1805  AddNativeCallback(
1806  "GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) {
1807  auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png");
1809  args, fixture);
1810  }));
1811 
1812  auto settings = CreateSettingsForFixture();
1813  auto configuration = RunConfiguration::InferFromSettings(settings);
1814  configuration.SetEntrypoint("canDecompressImageFromAsset");
1815  std::unique_ptr<Shell> shell = CreateShell(settings);
1816  ASSERT_NE(shell.get(), nullptr);
1817  RunEngine(shell.get(), std::move(configuration));
1818  latch.Wait();
1819  DestroyShell(std::move(shell));
1820 }
1821 
1822 TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) {
1824  ASSERT_TRUE(base_dir.fd().is_valid());
1827 
1828  // Create 2 dummy SkSL cache file IE (base32 encoding of A), II (base32
1829  // encoding of B) with content x and y.
1830  std::vector<std::string> components = {
1831  "flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion(),
1833  auto sksl_dir = fml::CreateDirectory(base_dir.fd(), components,
1835  const std::string x = "x";
1836  const std::string y = "y";
1837  auto x_data = std::make_unique<fml::DataMapping>(
1838  std::vector<uint8_t>{x.begin(), x.end()});
1839  auto y_data = std::make_unique<fml::DataMapping>(
1840  std::vector<uint8_t>{y.begin(), y.end()});
1841  ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "IE", *x_data));
1842  ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "II", *y_data));
1843 
1844  Settings settings = CreateSettingsForFixture();
1845  std::unique_ptr<Shell> shell = CreateShell(settings);
1847  rapidjson::Document document;
1848  OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs,
1849  shell->GetTaskRunners().GetIOTaskRunner(), empty_params,
1850  &document);
1851  rapidjson::StringBuffer buffer;
1852  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1853  document.Accept(writer);
1854  DestroyShell(std::move(shell));
1855 
1856  const std::string expected_json1 =
1857  "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}";
1858  const std::string expected_json2 =
1859  "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"IE\":\"eA==\",\"II\":\"eQ==\"}}";
1860  bool json_is_expected = (expected_json1 == buffer.GetString()) ||
1861  (expected_json2 == buffer.GetString());
1862  ASSERT_TRUE(json_is_expected) << buffer.GetString() << " is not equal to "
1863  << expected_json1 << " or " << expected_json2;
1864 }
1865 
1866 TEST_F(ShellTest, RasterizerScreenshot) {
1867  Settings settings = CreateSettingsForFixture();
1868  auto configuration = RunConfiguration::InferFromSettings(settings);
1869  auto task_runner = CreateNewThread();
1870  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1871  task_runner);
1872  std::unique_ptr<Shell> shell =
1873  CreateShell(std::move(settings), std::move(task_runners));
1874 
1875  ASSERT_TRUE(ValidateShell(shell.get()));
1876  PlatformViewNotifyCreated(shell.get());
1877 
1878  RunEngine(shell.get(), std::move(configuration));
1879 
1880  auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
1881 
1882  PumpOneFrame(shell.get());
1883 
1885  shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
1886  Rasterizer::Screenshot screenshot =
1887  shell->GetRasterizer()->ScreenshotLastLayerTree(
1889  EXPECT_NE(screenshot.data, nullptr);
1890 
1891  latch->Signal();
1892  });
1893  latch->Wait();
1894  DestroyShell(std::move(shell), std::move(task_runners));
1895 }
1896 
1897 TEST_F(ShellTest, RasterizerMakeRasterSnapshot) {
1898  Settings settings = CreateSettingsForFixture();
1899  auto configuration = RunConfiguration::InferFromSettings(settings);
1900  auto task_runner = CreateNewThread();
1901  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
1902  task_runner);
1903  std::unique_ptr<Shell> shell =
1904  CreateShell(std::move(settings), std::move(task_runners));
1905 
1906  ASSERT_TRUE(ValidateShell(shell.get()));
1907  PlatformViewNotifyCreated(shell.get());
1908 
1909  RunEngine(shell.get(), std::move(configuration));
1910 
1911  auto latch = std::make_shared<fml::AutoResetWaitableEvent>();
1912 
1913  PumpOneFrame(shell.get());
1914 
1916  shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
1917  SnapshotDelegate* delegate =
1918  reinterpret_cast<Rasterizer*>(shell->GetRasterizer().get());
1919  sk_sp<SkImage> image = delegate->MakeRasterSnapshot(
1920  SkPicture::MakePlaceholder({0, 0, 50, 50}), SkISize::Make(50, 50));
1921  EXPECT_NE(image, nullptr);
1922 
1923  latch->Signal();
1924  });
1925  latch->Wait();
1926  DestroyShell(std::move(shell), std::move(task_runners));
1927 }
1928 
1929 static sk_sp<SkPicture> MakeSizedPicture(int width, int height) {
1930  SkPictureRecorder recorder;
1931  SkCanvas* recording_canvas =
1932  recorder.beginRecording(SkRect::MakeXYWH(0, 0, width, height));
1933  recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, width, height),
1934  SkPaint(SkColor4f::FromColor(SK_ColorRED)));
1935  return recorder.finishRecordingAsPicture();
1936 }
1937 
1938 TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
1939  Settings settings = CreateSettingsForFixture();
1940  std::unique_ptr<Shell> shell = CreateShell(settings);
1941 
1942  // 1. Construct a picture and a picture layer to be raster cached.
1943  sk_sp<SkPicture> picture = MakeSizedPicture(10, 10);
1944  fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
1945  GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
1946  auto picture_layer = std::make_shared<PictureLayer>(
1947  SkPoint::Make(0, 0),
1949  false, false);
1950  picture_layer->set_paint_bounds(SkRect::MakeWH(100, 100));
1951 
1952  // 2. Rasterize the picture and the picture layer in the raster cache.
1953  std::promise<bool> rasterized;
1954  shell->GetTaskRunners().GetRasterTaskRunner()->PostTask(
1955  [&shell, &rasterized, &picture, &picture_layer] {
1956  auto* compositor_context = shell->GetRasterizer()->compositor_context();
1957  auto& raster_cache = compositor_context->raster_cache();
1958  // 2.1. Rasterize the picture. Call Draw multiple times to pass the
1959  // access threshold (default to 3) so a cache can be generated.
1960  SkCanvas dummy_canvas;
1961  bool picture_cache_generated;
1962  for (int i = 0; i < 4; i += 1) {
1963  picture_cache_generated =
1964  raster_cache.Prepare(nullptr, // GrDirectContext
1965  picture.get(), SkMatrix::I(),
1966  nullptr, // SkColorSpace
1967  true, // isComplex
1968  false // willChange
1969  );
1970  raster_cache.Draw(*picture, dummy_canvas);
1971  }
1972  ASSERT_TRUE(picture_cache_generated);
1973 
1974  // 2.2. Rasterize the picture layer.
1975  Stopwatch raster_time;
1976  Stopwatch ui_time;
1977  MutatorsStack mutators_stack;
1978  TextureRegistry texture_registry;
1979  PrerollContext preroll_context = {
1980  nullptr, /* raster_cache */
1981  nullptr, /* gr_context */
1982  nullptr, /* external_view_embedder */
1983  mutators_stack, nullptr, /* color_space */
1984  kGiantRect, /* cull_rect */
1985  false, /* layer reads from surface */
1986  raster_time, ui_time, texture_registry,
1987  false, /* checkerboard_offscreen_layers */
1988  1.0f, /* frame_device_pixel_ratio */
1989  false, /* has_platform_view */
1990  };
1991  raster_cache.Prepare(&preroll_context, picture_layer.get(),
1992  SkMatrix::I());
1993  rasterized.set_value(true);
1994  });
1995  rasterized.get_future().wait();
1996 
1997  // 3. Call the service protocol and check its output.
1999  rapidjson::Document document;
2000  OnServiceProtocol(
2001  shell.get(), ServiceProtocolEnum::kEstimateRasterCacheMemory,
2002  shell->GetTaskRunners().GetRasterTaskRunner(), empty_params, &document);
2003  rapidjson::StringBuffer buffer;
2004  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2005  document.Accept(writer);
2006  std::string expected_json =
2007  "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40000,\"picture"
2008  "Bytes\":400}";
2009  std::string actual_json = buffer.GetString();
2010  ASSERT_EQ(actual_json, expected_json);
2011 
2012  DestroyShell(std::move(shell));
2013 }
2014 
2015 TEST_F(ShellTest, DiscardLayerTreeOnResize) {
2016  auto settings = CreateSettingsForFixture();
2017 
2018  SkISize wrong_size = SkISize::Make(400, 100);
2019  SkISize expected_size = SkISize::Make(400, 200);
2020 
2021  fml::AutoResetWaitableEvent end_frame_latch;
2022 
2023  auto end_frame_callback = [&](bool, fml::RefPtr<fml::RasterThreadMerger>) {
2024  end_frame_latch.Signal();
2025  };
2026 
2027  std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder =
2028  std::make_shared<ShellTestExternalViewEmbedder>(
2029  std::move(end_frame_callback), PostPrerollResult::kSuccess, true);
2030 
2031  std::unique_ptr<Shell> shell = CreateShell(
2032  settings, GetTaskRunnersForFixture(), false, external_view_embedder);
2033 
2034  // Create the surface needed by rasterizer
2035  PlatformViewNotifyCreated(shell.get());
2036 
2038  shell->GetTaskRunners().GetPlatformTaskRunner(),
2039  [&shell, &expected_size]() {
2040  shell->GetPlatformView()->SetViewportMetrics(
2041  {1.0, static_cast<double>(expected_size.width()),
2042  static_cast<double>(expected_size.height())});
2043  });
2044 
2045  auto configuration = RunConfiguration::InferFromSettings(settings);
2046  configuration.SetEntrypoint("emptyMain");
2047 
2048  RunEngine(shell.get(), std::move(configuration));
2049 
2050  fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->GetEngine();
2051 
2052  PumpOneFrame(shell.get(), static_cast<double>(wrong_size.width()),
2053  static_cast<double>(wrong_size.height()));
2054 
2055  end_frame_latch.Wait();
2056 
2057  ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
2058 
2059  PumpOneFrame(shell.get(), static_cast<double>(expected_size.width()),
2060  static_cast<double>(expected_size.height()));
2061 
2062  end_frame_latch.Wait();
2063 
2064  ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
2065  ASSERT_EQ(expected_size, external_view_embedder->GetLastSubmittedFrameSize());
2066 
2067  DestroyShell(std::move(shell));
2068 }
2069 
2070 TEST_F(ShellTest, IgnoresInvalidMetrics) {
2072  double last_device_pixel_ratio;
2073  double last_width;
2074  double last_height;
2075  auto native_report_device_pixel_ratio = [&](Dart_NativeArguments args) {
2076  auto dpr_handle = Dart_GetNativeArgument(args, 0);
2077  ASSERT_TRUE(Dart_IsDouble(dpr_handle));
2078  Dart_DoubleValue(dpr_handle, &last_device_pixel_ratio);
2079  ASSERT_FALSE(last_device_pixel_ratio == 0.0);
2080 
2081  auto width_handle = Dart_GetNativeArgument(args, 1);
2082  ASSERT_TRUE(Dart_IsDouble(width_handle));
2083  Dart_DoubleValue(width_handle, &last_width);
2084  ASSERT_FALSE(last_width == 0.0);
2085 
2086  auto height_handle = Dart_GetNativeArgument(args, 2);
2087  ASSERT_TRUE(Dart_IsDouble(height_handle));
2088  Dart_DoubleValue(height_handle, &last_height);
2089  ASSERT_FALSE(last_height == 0.0);
2090 
2091  latch.Signal();
2092  };
2093 
2094  Settings settings = CreateSettingsForFixture();
2095  auto task_runner = CreateNewThread();
2096  TaskRunners task_runners("test", task_runner, task_runner, task_runner,
2097  task_runner);
2098 
2099  AddNativeCallback("ReportMetrics",
2100  CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio));
2101 
2102  std::unique_ptr<Shell> shell =
2103  CreateShell(std::move(settings), std::move(task_runners));
2104 
2105  auto configuration = RunConfiguration::InferFromSettings(settings);
2106  configuration.SetEntrypoint("reportMetrics");
2107 
2108  RunEngine(shell.get(), std::move(configuration));
2109 
2110  task_runner->PostTask([&]() {
2111  shell->GetPlatformView()->SetViewportMetrics({0.0, 400, 200});
2112  task_runner->PostTask([&]() {
2113  shell->GetPlatformView()->SetViewportMetrics({0.8, 0.0, 200});
2114  task_runner->PostTask([&]() {
2115  shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 0.0});
2116  task_runner->PostTask([&]() {
2117  shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 200.0});
2118  });
2119  });
2120  });
2121  });
2122  latch.Wait();
2123  ASSERT_EQ(last_device_pixel_ratio, 0.8);
2124  ASSERT_EQ(last_width, 400.0);
2125  ASSERT_EQ(last_height, 200.0);
2126  latch.Reset();
2127 
2128  task_runner->PostTask([&]() {
2129  shell->GetPlatformView()->SetViewportMetrics({1.2, 600, 300});
2130  });
2131  latch.Wait();
2132  ASSERT_EQ(last_device_pixel_ratio, 1.2);
2133  ASSERT_EQ(last_width, 600.0);
2134  ASSERT_EQ(last_height, 300.0);
2135 
2136  DestroyShell(std::move(shell), std::move(task_runners));
2137 }
2138 
2139 TEST_F(ShellTest, OnServiceProtocolSetAssetBundlePathWorks) {
2140  Settings settings = CreateSettingsForFixture();
2141  std::unique_ptr<Shell> shell = CreateShell(settings);
2142  RunConfiguration configuration =
2144  configuration.SetEntrypoint("canAccessResourceFromAssetDir");
2145 
2146  // Verify isolate can load a known resource with the
2147  // default asset directory - kernel_blob.bin
2149 
2150  // Callback used to signal whether the resource was loaded successfully.
2151  bool can_access_resource = false;
2152  auto native_can_access_resource = [&can_access_resource,
2153  &latch](Dart_NativeArguments args) {
2154  Dart_Handle exception = nullptr;
2155  can_access_resource =
2157  latch.Signal();
2158  };
2159  AddNativeCallback("NotifyCanAccessResource",
2160  CREATE_NATIVE_ENTRY(native_can_access_resource));
2161 
2162  // Callback used to delay the asset load until after the service
2163  // protocol method has finished.
2164  auto native_notify_set_asset_bundle_path =
2165  [&shell](Dart_NativeArguments args) {
2166  // Update the asset directory to a bonus path.
2168  params["assetDirectory"] = "assetDirectory";
2169  rapidjson::Document document;
2170  OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath,
2171  shell->GetTaskRunners().GetUITaskRunner(), params,
2172  &document);
2173  rapidjson::StringBuffer buffer;
2174  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2175  document.Accept(writer);
2176  };
2177  AddNativeCallback("NotifySetAssetBundlePath",
2178  CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path));
2179 
2180  RunEngine(shell.get(), std::move(configuration));
2181 
2182  latch.Wait();
2183  ASSERT_TRUE(can_access_resource);
2184 
2185  DestroyShell(std::move(shell));
2186 }
2187 
2188 TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) {
2189  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2190  // Make sure the shell launch does not kick off the creation of the VM
2191  // instance by already creating one upfront.
2192  auto vm_settings = CreateSettingsForFixture();
2193  auto vm_ref = DartVMRef::Create(vm_settings);
2194  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2195 
2196  auto settings = vm_settings;
2197  fml::AutoResetWaitableEvent isolate_create_latch;
2198  settings.root_isolate_create_callback = [&](const auto& isolate) {
2199  isolate_create_latch.Signal();
2200  };
2201  auto shell = CreateShell(settings);
2202  ASSERT_TRUE(ValidateShell(shell.get()));
2203  auto configuration = RunConfiguration::InferFromSettings(settings);
2204  ASSERT_TRUE(configuration.IsValid());
2205  RunEngine(shell.get(), std::move(configuration));
2206  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2207  DestroyShell(std::move(shell));
2208  isolate_create_latch.Wait();
2209 }
2210 
2211 } // namespace testing
2212 } // 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:26
G_BEGIN_DECLS FlValue * args
const char * GetFlutterEngineVersion()
Definition: version.cc:11
MockTexture(int64_t textureId, std::shared_ptr< fml::AutoResetWaitableEvent > latch)
static void PlatformViewNotifyCreated(Shell *shell)
Definition: shell_test.cc:42
static constexpr TimeDelta FromSeconds(int64_t seconds)
Definition: time_delta.h:49
std::map< std::string_view, std::string_view > ServiceProtocolMap
static const size_t kGrCacheMaxByteSize
static bool ValidateShell(Shell *shell)
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:56
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition: shell.cc:583
fml::TimePoint Set(Phase phase, fml::TimePoint value)
Definition: settings.h:39
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:12
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:52
static void TestDartVmFlags(std::vector< const char *> &flags)
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
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
Settings SettingsFromCommandLine(const fml::CommandLine &command_line)
Definition: switches.cc:216
TimeDelta ToEpochDelta() const
Definition: time_point.h:40
#define FML_LOG(severity)
Definition: logging.h:65
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition: time_point.h:36
static bool IsInstanceRunning()
fml::RefPtr< fml::TaskRunner > GetRasterTaskRunner() const
Definition: task_runners.cc:42
static DartVMRef Create(Settings settings, fml::RefPtr< DartSnapshot > vm_snapshot=nullptr, fml::RefPtr< DartSnapshot > isolate_snapshot=nullptr)
static constexpr SkRect kGiantRect
Definition: layer.h:38
bool is_valid() const
Definition: unique_object.h:89
static void PlatformViewNotifyDestroyed(Shell *shell)
Definition: shell_test.cc:52
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:94
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition: message_loop.cc:56
static void ValidateDestroyPlatformView(Shell *shell)
static constexpr Phase kPhases[kCount]
Definition: settings.h:35
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:279
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 GPU task runner.
Definition: shell.cc:587
Specifies all the configuration required by the runtime library to launch the root isolate...
static void LogSkData(sk_sp< SkData > data, const char *title)
FlutterPointerPhase phase
Definition: fl_view.cc:78
static constexpr TimeDelta FromMicroseconds(int64_t micros)
Definition: time_delta.h:43
#define CREATE_NATIVE_ENTRY(native_entry)
void Paint(SkCanvas &canvas, const SkRect &bounds, bool freeze, GrDirectContext *context, SkFilterQuality filter_quality) override
bool ok() const
Definition: status.h:71
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...
static size_t GetRasterizerResourceCacheBytesSync(const Shell &shell)
int32_t height
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
virtual sk_sp< SkImage > MakeRasterSnapshot(sk_sp< SkPicture > picture, SkISize picture_size)=0
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:199
int32_t width
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
std::vector< std::string > dart_flags
Definition: settings.h:96
GdkEventButton * event
Definition: fl_view.cc:62
fml::WeakPtr< PlatformView > GetPlatformView()
Platform views may only be accessed on the platform task runner.
Definition: shell.cc:597
const char * GetSkiaVersion()
Definition: version.cc:15
static sk_sp< SkPicture > MakeSizedPicture(int width, int height)
static void ResetCacheForProcess()
fml::StatusCode code() const
Definition: status.h:63
static std::string CreateFlagsString(std::vector< const char *> &flags)
std::unique_ptr< fml::Thread > io_thread
Definition: thread_host.h:28
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
fml::TimePoint Get(Phase phase) const
Definition: settings.h:38
bool IsSetup() const
Used by embedders to check if all shell subcomponents are initialized. It is the embedder&#39;s responsib...
Definition: shell.cc:529
int32_t id
std::shared_ptr< const fml::Mapping > persistent_isolate_data
Definition: settings.h:229
~MockTexture() override=default
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:188
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)
static std::unique_ptr< Shell > Create(TaskRunners task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition: shell.cc:239
std::unique_ptr< fml::Thread > raster_thread
Definition: thread_host.h:27
static TimePoint Now()
Definition: time_point.cc:26
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:25
static bool RunsOnTheSameThread(TaskQueueId queue_a, TaskQueueId queue_b)
TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithNoMutations)