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 "flutter/shell/common/shell_test.h"
8
9#include "flutter/flow/frame_timings.h"
10#include "flutter/flow/layers/layer_tree.h"
11#include "flutter/flow/layers/transform_layer.h"
12#include "flutter/fml/build_config.h"
13#include "flutter/fml/make_copyable.h"
14#include "flutter/fml/mapping.h"
15#include "flutter/runtime/dart_vm.h"
16#include "flutter/shell/common/shell_test_platform_view.h"
17#include "flutter/shell/common/vsync_waiter_fallback.h"
18#include "flutter/testing/testing.h"
19
20namespace flutter {
21namespace testing {
22
23constexpr int64_t kImplicitViewId = 0;
24
25ShellTest::ShellTest()
26 : thread_host_("io.flutter.test." + GetCurrentTestName() + ".",
27 ThreadHost::Type::Platform | ThreadHost::Type::IO |
28 ThreadHost::Type::UI | ThreadHost::Type::RASTER) {}
29
30void ShellTest::SendPlatformMessage(Shell* shell,
31 std::unique_ptr<PlatformMessage> message) {
32 shell->OnPlatformViewDispatchPlatformMessage(message: std::move(message));
33}
34
35void ShellTest::SendEnginePlatformMessage(
36 Shell* shell,
37 std::unique_ptr<PlatformMessage> message) {
38 fml::AutoResetWaitableEvent latch;
39 fml::TaskRunner::RunNowOrPostTask(
40 runner: shell->GetTaskRunners().GetPlatformTaskRunner(),
41 task: fml::MakeCopyable(
42 lambda: [shell, &latch, message = std::move(message)]() mutable {
43 if (auto engine = shell->weak_engine_) {
44 engine->HandlePlatformMessage(message: std::move(message));
45 }
46 latch.Signal();
47 }));
48 latch.Wait();
49}
50
51void ShellTest::PlatformViewNotifyCreated(Shell* shell) {
52 fml::AutoResetWaitableEvent latch;
53 fml::TaskRunner::RunNowOrPostTask(
54 runner: shell->GetTaskRunners().GetPlatformTaskRunner(), task: [shell, &latch]() {
55 shell->GetPlatformView()->NotifyCreated();
56 latch.Signal();
57 });
58 latch.Wait();
59}
60
61void ShellTest::PlatformViewNotifyDestroyed(Shell* shell) {
62 fml::AutoResetWaitableEvent latch;
63 fml::TaskRunner::RunNowOrPostTask(
64 runner: shell->GetTaskRunners().GetPlatformTaskRunner(), task: [shell, &latch]() {
65 shell->GetPlatformView()->NotifyDestroyed();
66 latch.Signal();
67 });
68 latch.Wait();
69}
70
71void ShellTest::RunEngine(Shell* shell, RunConfiguration configuration) {
72 fml::AutoResetWaitableEvent latch;
73 fml::TaskRunner::RunNowOrPostTask(
74 runner: shell->GetTaskRunners().GetPlatformTaskRunner(),
75 task: [shell, &latch, &configuration]() {
76 shell->RunEngine(run_configuration: std::move(configuration),
77 result_callback: [&latch](Engine::RunStatus run_status) {
78 ASSERT_EQ(run_status, Engine::RunStatus::Success);
79 latch.Signal();
80 });
81 });
82 latch.Wait();
83}
84
85void ShellTest::RestartEngine(Shell* shell, RunConfiguration configuration) {
86 std::promise<bool> restarted;
87 fml::TaskRunner::RunNowOrPostTask(
88 runner: shell->GetTaskRunners().GetUITaskRunner(),
89 task: [shell, &restarted, &configuration]() {
90 restarted.set_value(shell->engine_->Restart(configuration: std::move(configuration)));
91 });
92 ASSERT_TRUE(restarted.get_future().get());
93}
94
95void ShellTest::VSyncFlush(Shell* shell, bool& will_draw_new_frame) {
96 fml::AutoResetWaitableEvent latch;
97 fml::TaskRunner::RunNowOrPostTask(
98 runner: shell->GetTaskRunners().GetPlatformTaskRunner(),
99 task: [shell, &will_draw_new_frame, &latch] {
100 // The following UI task ensures that all previous UI tasks are flushed.
101 fml::AutoResetWaitableEvent ui_latch;
102 shell->GetTaskRunners().GetUITaskRunner()->PostTask(
103 task: [&ui_latch, &will_draw_new_frame]() {
104 will_draw_new_frame = true;
105 ui_latch.Signal();
106 });
107
108 ShellTestPlatformView* test_platform_view =
109 static_cast<ShellTestPlatformView*>(shell->GetPlatformView().get());
110 do {
111 test_platform_view->SimulateVSync();
112 } while (ui_latch.WaitWithTimeout(timeout: fml::TimeDelta::FromMilliseconds(millis: 1)));
113
114 latch.Signal();
115 });
116 latch.Wait();
117}
118
119void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) {
120 flutter::ViewportMetrics viewport_metrics = {
121 1, // device pixel ratio
122 width, // physical width
123 height, // physical height
124 0, // padding top
125 0, // padding right
126 0, // padding bottom
127 0, // padding left
128 0, // view inset top
129 0, // view inset right
130 0, // view inset bottom
131 0, // view inset left
132 0, // gesture inset top
133 0, // gesture inset right
134 0, // gesture inset bottom
135 0, // gesture inset left
136 22, // physical touch slop
137 std::vector<double>(), // display features bounds
138 std::vector<int>(), // display features type
139 std::vector<int>(), // display features state
140 0 // Display ID
141 };
142 // Set viewport to nonempty, and call Animator::BeginFrame to make the layer
143 // tree pipeline nonempty. Without either of this, the layer tree below
144 // won't be rasterized.
145 fml::AutoResetWaitableEvent latch;
146 shell->GetTaskRunners().GetUITaskRunner()->PostTask(
147 task: [&latch, engine = shell->weak_engine_, viewport_metrics]() {
148 if (engine) {
149 engine->SetViewportMetrics(view_id: kImplicitViewId, metrics: viewport_metrics);
150 const auto frame_begin_time = fml::TimePoint::Now();
151 const auto frame_end_time =
152 frame_begin_time + fml::TimeDelta::FromSecondsF(seconds: 1.0 / 60.0);
153 std::unique_ptr<FrameTimingsRecorder> recorder =
154 std::make_unique<FrameTimingsRecorder>();
155 recorder->RecordVsync(vsync_start: frame_begin_time, vsync_target: frame_end_time);
156 engine->animator_->BeginFrame(frame_timings_recorder: std::move(recorder));
157 }
158 latch.Signal();
159 });
160 latch.Wait();
161}
162
163void ShellTest::NotifyIdle(Shell* shell, fml::TimeDelta deadline) {
164 fml::AutoResetWaitableEvent latch;
165 shell->GetTaskRunners().GetUITaskRunner()->PostTask(
166 task: [&latch, engine = shell->weak_engine_, deadline]() {
167 if (engine) {
168 engine->NotifyIdle(deadline);
169 }
170 latch.Signal();
171 });
172 latch.Wait();
173}
174
175void ShellTest::PumpOneFrame(Shell* shell,
176 double width,
177 double height,
178 LayerTreeBuilder builder) {
179 PumpOneFrame(shell, viewport_metrics: {1.0, width, height, 22, 0}, std::move(builder));
180}
181
182void ShellTest::PumpOneFrame(Shell* shell,
183 const flutter::ViewportMetrics& viewport_metrics,
184 LayerTreeBuilder builder) {
185 // Set viewport to nonempty, and call Animator::BeginFrame to make the layer
186 // tree pipeline nonempty. Without either of this, the layer tree below
187 // won't be rasterized.
188 fml::AutoResetWaitableEvent latch;
189 shell->GetTaskRunners().GetUITaskRunner()->PostTask(
190 task: [&latch, engine = shell->weak_engine_, viewport_metrics]() {
191 engine->SetViewportMetrics(view_id: kImplicitViewId, metrics: viewport_metrics);
192 const auto frame_begin_time = fml::TimePoint::Now();
193 const auto frame_end_time =
194 frame_begin_time + fml::TimeDelta::FromSecondsF(seconds: 1.0 / 60.0);
195 std::unique_ptr<FrameTimingsRecorder> recorder =
196 std::make_unique<FrameTimingsRecorder>();
197 recorder->RecordVsync(vsync_start: frame_begin_time, vsync_target: frame_end_time);
198 engine->animator_->BeginFrame(frame_timings_recorder: std::move(recorder));
199 latch.Signal();
200 });
201 latch.Wait();
202
203 latch.Reset();
204 // Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized|
205 fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->weak_engine_;
206 shell->GetTaskRunners().GetUITaskRunner()->PostTask(
207 task: [&latch, runtime_delegate, &builder, viewport_metrics]() {
208 SkMatrix identity;
209 identity.setIdentity();
210 auto root_layer = std::make_shared<TransformLayer>(args&: identity);
211 auto layer_tree = std::make_unique<LayerTree>(
212 args: LayerTree::Config{.root_layer = root_layer},
213 args: SkISize::Make(w: viewport_metrics.physical_width,
214 h: viewport_metrics.physical_height));
215 float device_pixel_ratio =
216 static_cast<float>(viewport_metrics.device_pixel_ratio);
217 if (builder) {
218 builder(root_layer);
219 }
220 runtime_delegate->Render(layer_tree: std::move(layer_tree), device_pixel_ratio);
221 latch.Signal();
222 });
223 latch.Wait();
224}
225
226void ShellTest::DispatchFakePointerData(Shell* shell) {
227 auto packet = std::make_unique<PointerDataPacket>(args: 1);
228 DispatchPointerData(shell, packet: std::move(packet));
229}
230
231void ShellTest::DispatchPointerData(Shell* shell,
232 std::unique_ptr<PointerDataPacket> packet) {
233 fml::AutoResetWaitableEvent latch;
234 shell->GetTaskRunners().GetPlatformTaskRunner()->PostTask(
235 task: [&latch, shell, &packet]() {
236 // Goes through PlatformView to ensure packet is corrected converted.
237 shell->GetPlatformView()->DispatchPointerDataPacket(packet: std::move(packet));
238 latch.Signal();
239 });
240 latch.Wait();
241}
242
243int ShellTest::UnreportedTimingsCount(Shell* shell) {
244 return shell->unreported_timings_.size();
245}
246
247void ShellTest::SetNeedsReportTimings(Shell* shell, bool value) {
248 shell->SetNeedsReportTimings(value);
249}
250
251bool ShellTest::GetNeedsReportTimings(Shell* shell) {
252 return shell->needs_report_timings_;
253}
254
255void ShellTest::StorePersistentCache(PersistentCache* cache,
256 const SkData& key,
257 const SkData& value) {
258 cache->store(key, data: value);
259}
260
261void ShellTest::OnServiceProtocol(
262 Shell* shell,
263 ServiceProtocolEnum some_protocol,
264 const fml::RefPtr<fml::TaskRunner>& task_runner,
265 const ServiceProtocol::Handler::ServiceProtocolMap& params,
266 rapidjson::Document* response) {
267 std::promise<bool> finished;
268 fml::TaskRunner::RunNowOrPostTask(runner: task_runner, task: [shell, some_protocol, params,
269 response, &finished]() {
270 switch (some_protocol) {
271 case ServiceProtocolEnum::kGetSkSLs:
272 shell->OnServiceProtocolGetSkSLs(params, response);
273 break;
274 case ServiceProtocolEnum::kEstimateRasterCacheMemory:
275 shell->OnServiceProtocolEstimateRasterCacheMemory(params, response);
276 break;
277 case ServiceProtocolEnum::kSetAssetBundlePath:
278 shell->OnServiceProtocolSetAssetBundlePath(params, response);
279 break;
280 case ServiceProtocolEnum::kRunInView:
281 shell->OnServiceProtocolRunInView(params, response);
282 break;
283 case ServiceProtocolEnum::kRenderFrameWithRasterStats:
284 shell->OnServiceProtocolRenderFrameWithRasterStats(params, response);
285 break;
286 }
287 finished.set_value(true);
288 });
289 finished.get_future().wait();
290}
291
292std::shared_ptr<txt::FontCollection> ShellTest::GetFontCollection(
293 Shell* shell) {
294 return shell->weak_engine_->GetFontCollection().GetFontCollection();
295}
296
297Settings ShellTest::CreateSettingsForFixture() {
298 Settings settings;
299 settings.leak_vm = false;
300 settings.task_observer_add = [](intptr_t key, const fml::closure& handler) {
301 fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback: handler);
302 };
303 settings.task_observer_remove = [](intptr_t key) {
304 fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
305 };
306 settings.isolate_create_callback = [this]() {
307 native_resolver_->SetNativeResolverForIsolate();
308 };
309#if OS_FUCHSIA
310 settings.verbose_logging = true;
311#endif
312 SetSnapshotsAndAssets(settings);
313 return settings;
314}
315
316TaskRunners ShellTest::GetTaskRunnersForFixture() {
317 return {
318 "test",
319 thread_host_.platform_thread->GetTaskRunner(), // platform
320 thread_host_.raster_thread->GetTaskRunner(), // raster
321 thread_host_.ui_thread->GetTaskRunner(), // ui
322 thread_host_.io_thread->GetTaskRunner() // io
323 };
324}
325
326fml::TimePoint ShellTest::GetLatestFrameTargetTime(Shell* shell) const {
327 return shell->GetLatestFrameTargetTime();
328}
329
330std::unique_ptr<Shell> ShellTest::CreateShell(
331 const Settings& settings,
332 std::optional<TaskRunners> task_runners) {
333 return CreateShell(config: {
334 .settings = settings,
335 .task_runners = std::move(task_runners),
336 });
337}
338
339std::unique_ptr<Shell> ShellTest::CreateShell(const Config& config) {
340 TaskRunners task_runners = config.task_runners.has_value()
341 ? config.task_runners.value()
342 : GetTaskRunnersForFixture();
343 Shell::CreateCallback<PlatformView> platform_view_create_callback =
344 config.platform_view_create_callback;
345 if (!platform_view_create_callback) {
346 platform_view_create_callback = ShellTestPlatformViewBuilder({});
347 }
348
349 Shell::CreateCallback<Rasterizer> rasterizer_create_callback =
350 [](Shell& shell) { return std::make_unique<Rasterizer>(args&: shell); };
351
352 return Shell::Create(platform_data: flutter::PlatformData(), //
353 task_runners, //
354 settings: config.settings, //
355 on_create_platform_view: platform_view_create_callback, //
356 on_create_rasterizer: rasterizer_create_callback, //
357 is_gpu_disabled: config.is_gpu_disabled //
358 );
359}
360
361void ShellTest::DestroyShell(std::unique_ptr<Shell> shell) {
362 DestroyShell(shell: std::move(shell), task_runners: GetTaskRunnersForFixture());
363}
364
365void ShellTest::DestroyShell(std::unique_ptr<Shell> shell,
366 const TaskRunners& task_runners) {
367 fml::AutoResetWaitableEvent latch;
368 fml::TaskRunner::RunNowOrPostTask(runner: task_runners.GetPlatformTaskRunner(),
369 task: [&shell, &latch]() mutable {
370 shell.reset();
371 latch.Signal();
372 });
373 latch.Wait();
374}
375
376size_t ShellTest::GetLiveTrackedPathCount(
377 const std::shared_ptr<VolatilePathTracker>& tracker) {
378 return std::count_if(
379 first: tracker->paths_.begin(), last: tracker->paths_.end(),
380 pred: [](const std::weak_ptr<VolatilePathTracker::TrackedPath>& path) {
381 return path.lock();
382 });
383}
384
385} // namespace testing
386} // namespace flutter
387

source code of flutter_engine/flutter/shell/common/shell_test.cc