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 | // FLUTTER_NOLINT |
5 | |
6 | #include "flutter/runtime/dart_vm_lifecycle.h" |
7 | #include "flutter/shell/common/engine.h" |
8 | #include "flutter/shell/common/thread_host.h" |
9 | #include "flutter/testing/testing.h" |
10 | #include "gmock/gmock.h" |
11 | #include "rapidjson/document.h" |
12 | #include "rapidjson/stringbuffer.h" |
13 | #include "rapidjson/writer.h" |
14 | |
15 | ///\note Deprecated MOCK_METHOD macros used until this issue is resolved: |
16 | // https://github.com/google/googletest/issues/2490 |
17 | |
18 | namespace flutter { |
19 | |
20 | namespace { |
21 | class MockDelegate : public Engine::Delegate { |
22 | MOCK_METHOD2(OnEngineUpdateSemantics, |
23 | void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates)); |
24 | MOCK_METHOD1(OnEngineHandlePlatformMessage, |
25 | void(fml::RefPtr<PlatformMessage>)); |
26 | MOCK_METHOD0(OnPreEngineRestart, void()); |
27 | MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t)); |
28 | MOCK_METHOD1(SetNeedsReportTimings, void(bool)); |
29 | MOCK_METHOD1(ComputePlatformResolvedLocale, |
30 | std::unique_ptr<std::vector<std::string>>( |
31 | const std::vector<std::string>&)); |
32 | }; |
33 | |
34 | class MockResponse : public PlatformMessageResponse { |
35 | public: |
36 | MOCK_METHOD1(Complete, void(std::unique_ptr<fml::Mapping> data)); |
37 | MOCK_METHOD0(CompleteEmpty, void()); |
38 | }; |
39 | |
40 | class MockRuntimeDelegate : public RuntimeDelegate { |
41 | public: |
42 | MOCK_METHOD0(DefaultRouteName, std::string()); |
43 | MOCK_METHOD1(ScheduleFrame, void(bool)); |
44 | MOCK_METHOD1(Render, void(std::unique_ptr<flutter::LayerTree>)); |
45 | MOCK_METHOD2(UpdateSemantics, |
46 | void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates)); |
47 | MOCK_METHOD1(HandlePlatformMessage, void(fml::RefPtr<PlatformMessage>)); |
48 | MOCK_METHOD0(GetFontCollection, FontCollection&()); |
49 | MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t)); |
50 | MOCK_METHOD1(SetNeedsReportTimings, void(bool)); |
51 | MOCK_METHOD1(ComputePlatformResolvedLocale, |
52 | std::unique_ptr<std::vector<std::string>>( |
53 | const std::vector<std::string>&)); |
54 | }; |
55 | |
56 | class MockRuntimeController : public RuntimeController { |
57 | public: |
58 | MockRuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners) |
59 | : RuntimeController(client, p_task_runners) {} |
60 | MOCK_CONST_METHOD0(IsRootIsolateRunning, bool()); |
61 | MOCK_METHOD1(DispatchPlatformMessage, bool(fml::RefPtr<PlatformMessage>)); |
62 | }; |
63 | |
64 | fml::RefPtr<PlatformMessage> MakePlatformMessage( |
65 | const std::string& channel, |
66 | const std::map<std::string, std::string>& values, |
67 | fml::RefPtr<PlatformMessageResponse> response) { |
68 | rapidjson::Document document; |
69 | auto& allocator = document.GetAllocator(); |
70 | document.SetObject(); |
71 | |
72 | for (const auto& pair : values) { |
73 | rapidjson::Value key(pair.first.c_str(), strlen(pair.first.c_str()), |
74 | allocator); |
75 | rapidjson::Value value(pair.second.c_str(), strlen(pair.second.c_str()), |
76 | allocator); |
77 | document.AddMember(key, value, allocator); |
78 | } |
79 | |
80 | rapidjson::StringBuffer buffer; |
81 | rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); |
82 | document.Accept(writer); |
83 | const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString()); |
84 | |
85 | fml::RefPtr<PlatformMessage> message = fml::MakeRefCounted<PlatformMessage>( |
86 | channel, std::vector<uint8_t>(data, data + buffer.GetSize()), response); |
87 | return message; |
88 | } |
89 | |
90 | class EngineTest : public ::testing::Test { |
91 | public: |
92 | EngineTest() |
93 | : thread_host_("EngineTest" , |
94 | ThreadHost::Type::Platform | ThreadHost::Type::IO | |
95 | ThreadHost::Type::UI | ThreadHost::Type::GPU), |
96 | task_runners_({ |
97 | "EngineTest" , |
98 | thread_host_.platform_thread->GetTaskRunner(), // platform |
99 | thread_host_.raster_thread->GetTaskRunner(), // raster |
100 | thread_host_.ui_thread->GetTaskRunner(), // ui |
101 | thread_host_.io_thread->GetTaskRunner() // io |
102 | }) {} |
103 | |
104 | void PostUITaskSync(const std::function<void()>& function) { |
105 | fml::AutoResetWaitableEvent latch; |
106 | task_runners_.GetUITaskRunner()->PostTask([&] { |
107 | function(); |
108 | latch.Signal(); |
109 | }); |
110 | latch.Wait(); |
111 | } |
112 | |
113 | protected: |
114 | void SetUp() override { |
115 | dispatcher_maker_ = [](PointerDataDispatcher::Delegate&) { |
116 | return nullptr; |
117 | }; |
118 | } |
119 | |
120 | MockDelegate delegate_; |
121 | PointerDataDispatcherMaker dispatcher_maker_; |
122 | ThreadHost thread_host_; |
123 | TaskRunners task_runners_; |
124 | Settings settings_; |
125 | std::unique_ptr<Animator> animator_; |
126 | fml::WeakPtr<IOManager> io_manager_; |
127 | std::unique_ptr<RuntimeController> runtime_controller_; |
128 | std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner_; |
129 | }; |
130 | } // namespace |
131 | |
132 | TEST_F(EngineTest, Create) { |
133 | PostUITaskSync([this] { |
134 | auto engine = std::make_unique<Engine>( |
135 | /*delegate=*/delegate_, |
136 | /*dispatcher_maker=*/dispatcher_maker_, |
137 | /*image_decoder_task_runner=*/image_decoder_task_runner_, |
138 | /*task_runners=*/task_runners_, |
139 | /*settings=*/settings_, |
140 | /*animator=*/std::move(animator_), |
141 | /*io_manager=*/io_manager_, |
142 | /*runtime_controller=*/std::move(runtime_controller_)); |
143 | EXPECT_TRUE(engine); |
144 | }); |
145 | } |
146 | |
147 | TEST_F(EngineTest, DispatchPlatformMessageUnknown) { |
148 | PostUITaskSync([this] { |
149 | MockRuntimeDelegate client; |
150 | auto mock_runtime_controller = |
151 | std::make_unique<MockRuntimeController>(client, task_runners_); |
152 | EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning()) |
153 | .WillRepeatedly(::testing::Return(false)); |
154 | auto engine = std::make_unique<Engine>( |
155 | /*delegate=*/delegate_, |
156 | /*dispatcher_maker=*/dispatcher_maker_, |
157 | /*image_decoder_task_runner=*/image_decoder_task_runner_, |
158 | /*task_runners=*/task_runners_, |
159 | /*settings=*/settings_, |
160 | /*animator=*/std::move(animator_), |
161 | /*io_manager=*/io_manager_, |
162 | /*runtime_controller=*/std::move(mock_runtime_controller)); |
163 | |
164 | fml::RefPtr<PlatformMessageResponse> response = |
165 | fml::MakeRefCounted<MockResponse>(); |
166 | fml::RefPtr<PlatformMessage> message = |
167 | fml::MakeRefCounted<PlatformMessage>("foo" , response); |
168 | engine->DispatchPlatformMessage(message); |
169 | }); |
170 | } |
171 | |
172 | TEST_F(EngineTest, DispatchPlatformMessageInitialRoute) { |
173 | PostUITaskSync([this] { |
174 | MockRuntimeDelegate client; |
175 | auto mock_runtime_controller = |
176 | std::make_unique<MockRuntimeController>(client, task_runners_); |
177 | EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning()) |
178 | .WillRepeatedly(::testing::Return(false)); |
179 | auto engine = std::make_unique<Engine>( |
180 | /*delegate=*/delegate_, |
181 | /*dispatcher_maker=*/dispatcher_maker_, |
182 | /*image_decoder_task_runner=*/image_decoder_task_runner_, |
183 | /*task_runners=*/task_runners_, |
184 | /*settings=*/settings_, |
185 | /*animator=*/std::move(animator_), |
186 | /*io_manager=*/io_manager_, |
187 | /*runtime_controller=*/std::move(mock_runtime_controller)); |
188 | |
189 | fml::RefPtr<PlatformMessageResponse> response = |
190 | fml::MakeRefCounted<MockResponse>(); |
191 | std::map<std::string, std::string> values{ |
192 | {"method" , "setInitialRoute" }, |
193 | {"args" , "test_initial_route" }, |
194 | }; |
195 | fml::RefPtr<PlatformMessage> message = |
196 | MakePlatformMessage("flutter/navigation" , values, response); |
197 | engine->DispatchPlatformMessage(message); |
198 | EXPECT_EQ(engine->InitialRoute(), "test_initial_route" ); |
199 | }); |
200 | } |
201 | |
202 | TEST_F(EngineTest, DispatchPlatformMessageInitialRouteIgnored) { |
203 | PostUITaskSync([this] { |
204 | MockRuntimeDelegate client; |
205 | auto mock_runtime_controller = |
206 | std::make_unique<MockRuntimeController>(client, task_runners_); |
207 | EXPECT_CALL(*mock_runtime_controller, IsRootIsolateRunning()) |
208 | .WillRepeatedly(::testing::Return(true)); |
209 | EXPECT_CALL(*mock_runtime_controller, DispatchPlatformMessage(::testing::_)) |
210 | .WillRepeatedly(::testing::Return(true)); |
211 | auto engine = std::make_unique<Engine>( |
212 | /*delegate=*/delegate_, |
213 | /*dispatcher_maker=*/dispatcher_maker_, |
214 | /*image_decoder_task_runner=*/image_decoder_task_runner_, |
215 | /*task_runners=*/task_runners_, |
216 | /*settings=*/settings_, |
217 | /*animator=*/std::move(animator_), |
218 | /*io_manager=*/io_manager_, |
219 | /*runtime_controller=*/std::move(mock_runtime_controller)); |
220 | |
221 | fml::RefPtr<PlatformMessageResponse> response = |
222 | fml::MakeRefCounted<MockResponse>(); |
223 | std::map<std::string, std::string> values{ |
224 | {"method" , "setInitialRoute" }, |
225 | {"args" , "test_initial_route" }, |
226 | }; |
227 | fml::RefPtr<PlatformMessage> message = |
228 | MakePlatformMessage("flutter/navigation" , values, response); |
229 | engine->DispatchPlatformMessage(message); |
230 | EXPECT_EQ(engine->InitialRoute(), "" ); |
231 | }); |
232 | } |
233 | |
234 | } // namespace flutter |
235 | |