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#include "flutter/shell/common/engine.h"
6
7#include <cstring>
8
9#include "flutter/runtime/dart_vm_lifecycle.h"
10#include "flutter/shell/common/thread_host.h"
11#include "flutter/testing/fixture_test.h"
12#include "flutter/testing/testing.h"
13#include "gmock/gmock.h"
14#include "rapidjson/document.h"
15#include "rapidjson/stringbuffer.h"
16#include "rapidjson/writer.h"
17
18///\note Deprecated MOCK_METHOD macros used until this issue is resolved:
19// https://github.com/google/googletest/issues/2490
20
21namespace flutter {
22
23namespace {
24constexpr int64_t kImplicitViewId = 0ll;
25
26class MockDelegate : public Engine::Delegate {
27 public:
28 MOCK_METHOD2(OnEngineUpdateSemantics,
29 void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates));
30 MOCK_METHOD1(OnEngineHandlePlatformMessage,
31 void(std::unique_ptr<PlatformMessage>));
32 MOCK_METHOD0(OnPreEngineRestart, void());
33 MOCK_METHOD0(OnRootIsolateCreated, void());
34 MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
35 MOCK_METHOD1(SetNeedsReportTimings, void(bool));
36 MOCK_METHOD1(ComputePlatformResolvedLocale,
37 std::unique_ptr<std::vector<std::string>>(
38 const std::vector<std::string>&));
39 MOCK_METHOD1(RequestDartDeferredLibrary, void(intptr_t));
40 MOCK_METHOD0(GetCurrentTimePoint, fml::TimePoint());
41 MOCK_CONST_METHOD0(GetPlatformMessageHandler,
42 const std::shared_ptr<PlatformMessageHandler>&());
43};
44
45class MockResponse : public PlatformMessageResponse {
46 public:
47 MOCK_METHOD1(Complete, void(std::unique_ptr<fml::Mapping> data));
48 MOCK_METHOD0(CompleteEmpty, void());
49};
50
51class MockRuntimeDelegate : public RuntimeDelegate {
52 public:
53 MOCK_METHOD0(ImplicitViewEnabled, bool());
54 MOCK_METHOD0(DefaultRouteName, std::string());
55 MOCK_METHOD1(ScheduleFrame, void(bool));
56 MOCK_METHOD2(Render, void(std::unique_ptr<flutter::LayerTree>, float));
57 MOCK_METHOD2(UpdateSemantics,
58 void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates));
59 MOCK_METHOD1(HandlePlatformMessage, void(std::unique_ptr<PlatformMessage>));
60 MOCK_METHOD0(GetFontCollection, FontCollection&());
61 MOCK_METHOD0(GetAssetManager, std::shared_ptr<AssetManager>());
62 MOCK_METHOD0(OnRootIsolateCreated, void());
63 MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
64 MOCK_METHOD1(SetNeedsReportTimings, void(bool));
65 MOCK_METHOD1(ComputePlatformResolvedLocale,
66 std::unique_ptr<std::vector<std::string>>(
67 const std::vector<std::string>&));
68 MOCK_METHOD1(RequestDartDeferredLibrary, void(intptr_t));
69 MOCK_CONST_METHOD0(GetPlatformMessageHandler,
70 std::weak_ptr<PlatformMessageHandler>());
71};
72
73class MockRuntimeController : public RuntimeController {
74 public:
75 MockRuntimeController(RuntimeDelegate& client,
76 const TaskRunners& p_task_runners)
77 : RuntimeController(client, p_task_runners) {}
78 MOCK_METHOD0(IsRootIsolateRunning, bool());
79 MOCK_METHOD1(DispatchPlatformMessage, bool(std::unique_ptr<PlatformMessage>));
80 MOCK_METHOD3(LoadDartDeferredLibraryError,
81 void(intptr_t, const std::string, bool));
82 MOCK_CONST_METHOD0(GetDartVM, DartVM*());
83 MOCK_METHOD1(NotifyIdle, bool(fml::TimeDelta));
84};
85
86std::unique_ptr<PlatformMessage> MakePlatformMessage(
87 const std::string& channel,
88 const std::map<std::string, std::string>& values,
89 const fml::RefPtr<PlatformMessageResponse>& response) {
90 rapidjson::Document document;
91 auto& allocator = document.GetAllocator();
92 document.SetObject();
93
94 for (const auto& pair : values) {
95 rapidjson::Value key(pair.first.c_str(), strlen(s: pair.first.c_str()),
96 allocator);
97 rapidjson::Value value(pair.second.c_str(), strlen(s: pair.second.c_str()),
98 allocator);
99 document.AddMember(name&: key, value, allocator);
100 }
101
102 rapidjson::StringBuffer buffer;
103 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
104 document.Accept(handler&: writer);
105 const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
106
107 std::unique_ptr<PlatformMessage> message = std::make_unique<PlatformMessage>(
108 args: channel, args: fml::MallocMapping::Copy(begin: data, length: buffer.GetSize()), args: response);
109 return message;
110}
111
112class EngineTest : public testing::FixtureTest {
113 public:
114 EngineTest()
115 : thread_host_("EngineTest",
116 ThreadHost::Type::Platform | ThreadHost::Type::IO |
117 ThreadHost::Type::UI | ThreadHost::Type::RASTER),
118 task_runners_({
119 "EngineTest",
120 thread_host_.platform_thread->GetTaskRunner(), // platform
121 thread_host_.raster_thread->GetTaskRunner(), // raster
122 thread_host_.ui_thread->GetTaskRunner(), // ui
123 thread_host_.io_thread->GetTaskRunner() // io
124 }) {}
125
126 void PostUITaskSync(const std::function<void()>& function) {
127 fml::AutoResetWaitableEvent latch;
128 task_runners_.GetUITaskRunner()->PostTask(task: [&] {
129 function();
130 latch.Signal();
131 });
132 latch.Wait();
133 }
134
135 protected:
136 void SetUp() override {
137 settings_ = CreateSettingsForFixture();
138 dispatcher_maker_ = [](PointerDataDispatcher::Delegate&) {
139 return nullptr;
140 };
141 }
142
143 MockDelegate delegate_;
144 PointerDataDispatcherMaker dispatcher_maker_;
145 ThreadHost thread_host_;
146 TaskRunners task_runners_;
147 Settings settings_;
148 std::unique_ptr<Animator> animator_;
149 fml::WeakPtr<IOManager> io_manager_;
150 std::unique_ptr<RuntimeController> runtime_controller_;
151 std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner_;
152 fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_;
153};
154} // namespace
155
156TEST_F(EngineTest, Create) {
157 PostUITaskSync(function: [this] {
158 auto engine = std::make_unique<Engine>(
159 /*delegate=*/args&: delegate_,
160 /*dispatcher_maker=*/args&: dispatcher_maker_,
161 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
162 /*task_runners=*/args&: task_runners_,
163 /*settings=*/args&: settings_,
164 /*animator=*/args: std::move(animator_),
165 /*io_manager=*/args&: io_manager_,
166 /*font_collection=*/args: std::make_shared<FontCollection>(),
167 /*runtime_controller=*/args: std::move(runtime_controller_),
168 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
169 EXPECT_TRUE(engine);
170 });
171}
172
173TEST_F(EngineTest, DispatchPlatformMessageUnknown) {
174 PostUITaskSync(function: [this] {
175 MockRuntimeDelegate client;
176 auto mock_runtime_controller =
177 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
178 EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning())
179 .WillRepeatedly(action: ::testing::Return(value: false));
180 auto engine = std::make_unique<Engine>(
181 /*delegate=*/args&: delegate_,
182 /*dispatcher_maker=*/args&: dispatcher_maker_,
183 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
184 /*task_runners=*/args&: task_runners_,
185 /*settings=*/args&: settings_,
186 /*animator=*/args: std::move(animator_),
187 /*io_manager=*/args&: io_manager_,
188 /*font_collection=*/args: std::make_shared<FontCollection>(),
189 /*runtime_controller=*/args: std::move(mock_runtime_controller),
190 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
191
192 fml::RefPtr<PlatformMessageResponse> response =
193 fml::MakeRefCounted<MockResponse>();
194 std::unique_ptr<PlatformMessage> message =
195 std::make_unique<PlatformMessage>(args: "foo", args&: response);
196 engine->DispatchPlatformMessage(message: std::move(message));
197 });
198}
199
200TEST_F(EngineTest, DispatchPlatformMessageInitialRoute) {
201 PostUITaskSync(function: [this] {
202 MockRuntimeDelegate client;
203 auto mock_runtime_controller =
204 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
205 EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning())
206 .WillRepeatedly(action: ::testing::Return(value: false));
207 auto engine = std::make_unique<Engine>(
208 /*delegate=*/args&: delegate_,
209 /*dispatcher_maker=*/args&: dispatcher_maker_,
210 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
211 /*task_runners=*/args&: task_runners_,
212 /*settings=*/args&: settings_,
213 /*animator=*/args: std::move(animator_),
214 /*io_manager=*/args&: io_manager_,
215 /*font_collection=*/args: std::make_shared<FontCollection>(),
216 /*runtime_controller=*/args: std::move(mock_runtime_controller),
217 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
218
219 fml::RefPtr<PlatformMessageResponse> response =
220 fml::MakeRefCounted<MockResponse>();
221 std::map<std::string, std::string> values{
222 {"method", "setInitialRoute"},
223 {"args", "test_initial_route"},
224 };
225 std::unique_ptr<PlatformMessage> message =
226 MakePlatformMessage(channel: "flutter/navigation", values, response);
227 engine->DispatchPlatformMessage(message: std::move(message));
228 EXPECT_EQ(engine->InitialRoute(), "test_initial_route");
229 });
230}
231
232TEST_F(EngineTest, DispatchPlatformMessageInitialRouteIgnored) {
233 PostUITaskSync(function: [this] {
234 MockRuntimeDelegate client;
235 auto mock_runtime_controller =
236 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
237 EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning())
238 .WillRepeatedly(action: ::testing::Return(value: true));
239 EXPECT_CALL(*mock_runtime_controller, DispatchPlatformMessage(::testing::_))
240 .WillRepeatedly(action: ::testing::Return(value: true));
241 auto engine = std::make_unique<Engine>(
242 /*delegate=*/args&: delegate_,
243 /*dispatcher_maker=*/args&: dispatcher_maker_,
244 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
245 /*task_runners=*/args&: task_runners_,
246 /*settings=*/args&: settings_,
247 /*animator=*/args: std::move(animator_),
248 /*io_manager=*/args&: io_manager_,
249 /*font_collection=*/args: std::make_shared<FontCollection>(),
250 /*runtime_controller=*/args: std::move(mock_runtime_controller),
251 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
252
253 fml::RefPtr<PlatformMessageResponse> response =
254 fml::MakeRefCounted<MockResponse>();
255 std::map<std::string, std::string> values{
256 {"method", "setInitialRoute"},
257 {"args", "test_initial_route"},
258 };
259 std::unique_ptr<PlatformMessage> message =
260 MakePlatformMessage(channel: "flutter/navigation", values, response);
261 engine->DispatchPlatformMessage(message: std::move(message));
262 EXPECT_EQ(engine->InitialRoute(), "");
263 });
264}
265
266TEST_F(EngineTest, SpawnSharesFontLibrary) {
267 PostUITaskSync(function: [this] {
268 MockRuntimeDelegate client;
269 auto mock_runtime_controller =
270 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
271 auto vm_ref = DartVMRef::Create(settings: settings_);
272 EXPECT_CALL(*mock_runtime_controller, GetDartVM())
273 .WillRepeatedly(action: ::testing::Return(value: vm_ref.get()));
274 auto engine = std::make_unique<Engine>(
275 /*delegate=*/args&: delegate_,
276 /*dispatcher_maker=*/args&: dispatcher_maker_,
277 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
278 /*task_runners=*/args&: task_runners_,
279 /*settings=*/args&: settings_,
280 /*animator=*/args: std::move(animator_),
281 /*io_manager=*/args&: io_manager_,
282 /*font_collection=*/args: std::make_shared<FontCollection>(),
283 /*runtime_controller=*/args: std::move(mock_runtime_controller),
284 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
285
286 auto spawn =
287 engine->Spawn(delegate&: delegate_, dispatcher_maker: dispatcher_maker_, settings: settings_, animator: nullptr,
288 initial_route: std::string(), io_manager: io_manager_, snapshot_delegate: snapshot_delegate_, gpu_disabled_switch: nullptr);
289 EXPECT_TRUE(spawn != nullptr);
290 EXPECT_EQ(&engine->GetFontCollection(), &spawn->GetFontCollection());
291 });
292}
293
294TEST_F(EngineTest, SpawnWithCustomInitialRoute) {
295 PostUITaskSync(function: [this] {
296 MockRuntimeDelegate client;
297 auto mock_runtime_controller =
298 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
299 auto vm_ref = DartVMRef::Create(settings: settings_);
300 EXPECT_CALL(*mock_runtime_controller, GetDartVM())
301 .WillRepeatedly(action: ::testing::Return(value: vm_ref.get()));
302 auto engine = std::make_unique<Engine>(
303 /*delegate=*/args&: delegate_,
304 /*dispatcher_maker=*/args&: dispatcher_maker_,
305 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
306 /*task_runners=*/args&: task_runners_,
307 /*settings=*/args&: settings_,
308 /*animator=*/args: std::move(animator_),
309 /*io_manager=*/args&: io_manager_,
310 /*font_collection=*/args: std::make_shared<FontCollection>(),
311 /*runtime_controller=*/args: std::move(mock_runtime_controller),
312 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
313
314 auto spawn =
315 engine->Spawn(delegate&: delegate_, dispatcher_maker: dispatcher_maker_, settings: settings_, animator: nullptr, initial_route: "/foo",
316 io_manager: io_manager_, snapshot_delegate: snapshot_delegate_, gpu_disabled_switch: nullptr);
317 EXPECT_TRUE(spawn != nullptr);
318 ASSERT_EQ("/foo", spawn->InitialRoute());
319 });
320}
321
322TEST_F(EngineTest, SpawnResetsViewportMetrics) {
323 PostUITaskSync(function: [this] {
324 MockRuntimeDelegate client;
325 auto mock_runtime_controller =
326 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
327 auto vm_ref = DartVMRef::Create(settings: settings_);
328 EXPECT_CALL(*mock_runtime_controller, GetDartVM())
329 .WillRepeatedly(action: ::testing::Return(value: vm_ref.get()));
330 ViewportMetrics old_viewport_metrics = ViewportMetrics();
331 const double kViewWidth = 768;
332 const double kViewHeight = 1024;
333 old_viewport_metrics.physical_width = kViewWidth;
334 old_viewport_metrics.physical_height = kViewHeight;
335 mock_runtime_controller->SetViewportMetrics(view_id: kImplicitViewId,
336 metrics: old_viewport_metrics);
337 auto engine = std::make_unique<Engine>(
338 /*delegate=*/args&: delegate_,
339 /*dispatcher_maker=*/args&: dispatcher_maker_,
340 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
341 /*task_runners=*/args&: task_runners_,
342 /*settings=*/args&: settings_,
343 /*animator=*/args: std::move(animator_),
344 /*io_manager=*/args&: io_manager_,
345 /*font_collection=*/args: std::make_shared<FontCollection>(),
346 /*runtime_controller=*/args: std::move(mock_runtime_controller),
347 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
348
349 auto& old_platform_data = engine->GetRuntimeController()->GetPlatformData();
350 EXPECT_EQ(old_platform_data.viewport_metrics.physical_width, kViewWidth);
351 EXPECT_EQ(old_platform_data.viewport_metrics.physical_height, kViewHeight);
352
353 auto spawn =
354 engine->Spawn(delegate&: delegate_, dispatcher_maker: dispatcher_maker_, settings: settings_, animator: nullptr,
355 initial_route: std::string(), io_manager: io_manager_, snapshot_delegate: snapshot_delegate_, gpu_disabled_switch: nullptr);
356 EXPECT_TRUE(spawn != nullptr);
357 auto& new_viewport_metrics =
358 spawn->GetRuntimeController()->GetPlatformData().viewport_metrics;
359 EXPECT_EQ(new_viewport_metrics.physical_width, 0);
360 EXPECT_EQ(new_viewport_metrics.physical_height, 0);
361 });
362}
363
364TEST_F(EngineTest, SpawnWithCustomSettings) {
365 PostUITaskSync(function: [this] {
366 MockRuntimeDelegate client;
367 auto mock_runtime_controller =
368 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
369 auto vm_ref = DartVMRef::Create(settings: settings_);
370 EXPECT_CALL(*mock_runtime_controller, GetDartVM())
371 .WillRepeatedly(action: ::testing::Return(value: vm_ref.get()));
372 auto engine = std::make_unique<Engine>(
373 /*delegate=*/args&: delegate_,
374 /*dispatcher_maker=*/args&: dispatcher_maker_,
375 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
376 /*task_runners=*/args&: task_runners_,
377 /*settings=*/args&: settings_,
378 /*animator=*/args: std::move(animator_),
379 /*io_manager=*/args&: io_manager_,
380 /*font_collection=*/args: std::make_shared<FontCollection>(),
381 /*runtime_controller=*/args: std::move(mock_runtime_controller),
382 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
383
384 Settings custom_settings = settings_;
385 custom_settings.persistent_isolate_data =
386 std::make_shared<fml::DataMapping>(args: "foo");
387 auto spawn =
388 engine->Spawn(delegate&: delegate_, dispatcher_maker: dispatcher_maker_, settings: custom_settings, animator: nullptr,
389 initial_route: std::string(), io_manager: io_manager_, snapshot_delegate: snapshot_delegate_, gpu_disabled_switch: nullptr);
390 EXPECT_TRUE(spawn != nullptr);
391 auto new_persistent_isolate_data =
392 const_cast<RuntimeController*>(spawn->GetRuntimeController())
393 ->GetPersistentIsolateData();
394 EXPECT_EQ(custom_settings.persistent_isolate_data->GetMapping(),
395 new_persistent_isolate_data->GetMapping());
396 EXPECT_EQ(custom_settings.persistent_isolate_data->GetSize(),
397 new_persistent_isolate_data->GetSize());
398 });
399}
400
401TEST_F(EngineTest, PassesLoadDartDeferredLibraryErrorToRuntime) {
402 PostUITaskSync(function: [this] {
403 intptr_t error_id = 123;
404 const std::string error_message = "error message";
405 MockRuntimeDelegate client;
406 auto mock_runtime_controller =
407 std::make_unique<MockRuntimeController>(args&: client, args&: task_runners_);
408 EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning())
409 .WillRepeatedly(action: ::testing::Return(value: true));
410 EXPECT_CALL(*mock_runtime_controller,
411 LoadDartDeferredLibraryError(error_id, error_message, true))
412 .Times(n: 1);
413 auto engine = std::make_unique<Engine>(
414 /*delegate=*/args&: delegate_,
415 /*dispatcher_maker=*/args&: dispatcher_maker_,
416 /*image_decoder_task_runner=*/args&: image_decoder_task_runner_,
417 /*task_runners=*/args&: task_runners_,
418 /*settings=*/args&: settings_,
419 /*animator=*/args: std::move(animator_),
420 /*io_manager=*/args&: io_manager_,
421 /*font_collection=*/args: std::make_shared<FontCollection>(),
422 /*runtime_controller=*/args: std::move(mock_runtime_controller),
423 /*gpu_disabled_switch=*/args: std::make_shared<fml::SyncSwitch>());
424
425 engine->LoadDartDeferredLibraryError(loading_unit_id: error_id, error_message, transient: true);
426 });
427}
428
429} // namespace flutter
430

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