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/fml/mapping.h" |
6 | #include "flutter/fml/synchronization/count_down_latch.h" |
7 | #include "flutter/fml/synchronization/waitable_event.h" |
8 | #include "flutter/fml/thread.h" |
9 | #include "flutter/runtime/dart_isolate.h" |
10 | #include "flutter/runtime/dart_vm.h" |
11 | #include "flutter/runtime/dart_vm_lifecycle.h" |
12 | #include "flutter/testing/dart_isolate_runner.h" |
13 | #include "flutter/testing/fixture_test.h" |
14 | #include "flutter/testing/testing.h" |
15 | #include "third_party/tonic/converter/dart_converter.h" |
16 | #include "third_party/tonic/scopes/dart_isolate_scope.h" |
17 | |
18 | namespace flutter { |
19 | namespace testing { |
20 | |
21 | class DartIsolateTest : public FixtureTest { |
22 | public: |
23 | DartIsolateTest() {} |
24 | |
25 | void Wait() { latch_.Wait(); } |
26 | |
27 | void Signal() { latch_.Signal(); } |
28 | |
29 | private: |
30 | fml::AutoResetWaitableEvent latch_; |
31 | |
32 | FML_DISALLOW_COPY_AND_ASSIGN(DartIsolateTest); |
33 | }; |
34 | |
35 | TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { |
36 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
37 | auto settings = CreateSettingsForFixture(); |
38 | auto vm_ref = DartVMRef::Create(settings); |
39 | ASSERT_TRUE(vm_ref); |
40 | auto vm_data = vm_ref.GetVMData(); |
41 | ASSERT_TRUE(vm_data); |
42 | TaskRunners task_runners(GetCurrentTestName(), // |
43 | GetCurrentTaskRunner(), // |
44 | GetCurrentTaskRunner(), // |
45 | GetCurrentTaskRunner(), // |
46 | GetCurrentTaskRunner() // |
47 | ); |
48 | auto weak_isolate = DartIsolate::CreateRootIsolate( |
49 | vm_data->GetSettings(), // settings |
50 | vm_data->GetIsolateSnapshot(), // isolate snapshot |
51 | std::move(task_runners), // task runners |
52 | nullptr, // window |
53 | {}, // snapshot delegate |
54 | {}, // io manager |
55 | {}, // unref queue |
56 | {}, // image decoder |
57 | "main.dart" , // advisory uri |
58 | "main" , // advisory entrypoint, |
59 | nullptr, // flags |
60 | settings.isolate_create_callback, // isolate create callback |
61 | settings.isolate_shutdown_callback // isolate shutdown callback |
62 | ); |
63 | auto root_isolate = weak_isolate.lock(); |
64 | ASSERT_TRUE(root_isolate); |
65 | ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); |
66 | ASSERT_TRUE(root_isolate->Shutdown()); |
67 | } |
68 | |
69 | TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { |
70 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
71 | auto settings = CreateSettingsForFixture(); |
72 | auto vm_ref = DartVMRef::Create(settings); |
73 | ASSERT_TRUE(vm_ref); |
74 | auto vm_data = vm_ref.GetVMData(); |
75 | ASSERT_TRUE(vm_data); |
76 | TaskRunners task_runners(GetCurrentTestName(), // |
77 | GetCurrentTaskRunner(), // |
78 | GetCurrentTaskRunner(), // |
79 | GetCurrentTaskRunner(), // |
80 | GetCurrentTaskRunner() // |
81 | ); |
82 | auto weak_isolate = DartIsolate::CreateRootIsolate( |
83 | vm_data->GetSettings(), // settings |
84 | vm_data->GetIsolateSnapshot(), // isolate snapshot |
85 | std::move(task_runners), // task runners |
86 | nullptr, // window |
87 | {}, // snapshot delegate |
88 | {}, // io manager |
89 | {}, // unref queue |
90 | {}, // image decoder |
91 | "main.dart" , // advisory uri |
92 | "main" , // advisory entrypoint |
93 | nullptr, // flags |
94 | settings.isolate_create_callback, // isolate create callback |
95 | settings.isolate_shutdown_callback // isolate shutdown callback |
96 | ); |
97 | auto root_isolate = weak_isolate.lock(); |
98 | ASSERT_TRUE(root_isolate); |
99 | ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); |
100 | size_t destruction_callback_count = 0; |
101 | root_isolate->AddIsolateShutdownCallback([&destruction_callback_count]() { |
102 | ASSERT_NE(Dart_CurrentIsolate(), nullptr); |
103 | destruction_callback_count++; |
104 | }); |
105 | ASSERT_TRUE(root_isolate->Shutdown()); |
106 | ASSERT_EQ(destruction_callback_count, 1u); |
107 | } |
108 | |
109 | TEST_F(DartIsolateTest, IsolateCanLoadAndRunDartCode) { |
110 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
111 | const auto settings = CreateSettingsForFixture(); |
112 | auto vm_ref = DartVMRef::Create(settings); |
113 | TaskRunners task_runners(GetCurrentTestName(), // |
114 | GetCurrentTaskRunner(), // |
115 | GetCurrentTaskRunner(), // |
116 | GetCurrentTaskRunner(), // |
117 | GetCurrentTaskRunner() // |
118 | ); |
119 | auto isolate = RunDartCodeInIsolate(vm_ref, settings, task_runners, "main" , |
120 | {}, GetFixturesPath()); |
121 | ASSERT_TRUE(isolate); |
122 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
123 | } |
124 | |
125 | TEST_F(DartIsolateTest, IsolateCannotLoadAndRunUnknownDartEntrypoint) { |
126 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
127 | const auto settings = CreateSettingsForFixture(); |
128 | auto vm_ref = DartVMRef::Create(settings); |
129 | TaskRunners task_runners(GetCurrentTestName(), // |
130 | GetCurrentTaskRunner(), // |
131 | GetCurrentTaskRunner(), // |
132 | GetCurrentTaskRunner(), // |
133 | GetCurrentTaskRunner() // |
134 | ); |
135 | auto isolate = |
136 | RunDartCodeInIsolate(vm_ref, settings, task_runners, "thisShouldNotExist" , |
137 | {}, GetFixturesPath()); |
138 | ASSERT_FALSE(isolate); |
139 | } |
140 | |
141 | TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) { |
142 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
143 | const auto settings = CreateSettingsForFixture(); |
144 | auto vm_ref = DartVMRef::Create(settings); |
145 | TaskRunners task_runners(GetCurrentTestName(), // |
146 | GetCurrentTaskRunner(), // |
147 | GetCurrentTaskRunner(), // |
148 | GetCurrentTaskRunner(), // |
149 | GetCurrentTaskRunner() // |
150 | ); |
151 | auto isolate = RunDartCodeInIsolate(vm_ref, settings, task_runners, "main" , |
152 | {}, GetFixturesPath()); |
153 | |
154 | ASSERT_TRUE(isolate); |
155 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
156 | ASSERT_TRUE(isolate->RunInIsolateScope([]() -> bool { |
157 | if (tonic::LogIfError(::Dart_Invoke(Dart_RootLibrary(), |
158 | tonic::ToDart("sayHi" ), 0, nullptr))) { |
159 | return false; |
160 | } |
161 | return true; |
162 | })); |
163 | } |
164 | |
165 | TEST_F(DartIsolateTest, CanRegisterNativeCallback) { |
166 | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
167 | AddNativeCallback("NotifyNative" , |
168 | CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) { |
169 | FML_LOG(ERROR) << "Hello from Dart!" ; |
170 | Signal(); |
171 | }))); |
172 | const auto settings = CreateSettingsForFixture(); |
173 | auto vm_ref = DartVMRef::Create(settings); |
174 | auto thread = CreateNewThread(); |
175 | TaskRunners task_runners(GetCurrentTestName(), // |
176 | thread, // |
177 | thread, // |
178 | thread, // |
179 | thread // |
180 | ); |
181 | auto isolate = |
182 | RunDartCodeInIsolate(vm_ref, settings, task_runners, |
183 | "canRegisterNativeCallback" , {}, GetFixturesPath()); |
184 | ASSERT_TRUE(isolate); |
185 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
186 | Wait(); |
187 | } |
188 | |
189 | TEST_F(DartIsolateTest, CanSaveCompilationTrace) { |
190 | if (DartVM::IsRunningPrecompiledCode()) { |
191 | // Can only save compilation traces in JIT modes. |
192 | GTEST_SKIP(); |
193 | return; |
194 | } |
195 | AddNativeCallback("NotifyNative" , |
196 | CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) { |
197 | ASSERT_TRUE(tonic::DartConverter<bool>::FromDart( |
198 | Dart_GetNativeArgument(args, 0))); |
199 | Signal(); |
200 | }))); |
201 | |
202 | const auto settings = CreateSettingsForFixture(); |
203 | auto vm_ref = DartVMRef::Create(settings); |
204 | auto thread = CreateNewThread(); |
205 | TaskRunners task_runners(GetCurrentTestName(), // |
206 | thread, // |
207 | thread, // |
208 | thread, // |
209 | thread // |
210 | ); |
211 | auto isolate = RunDartCodeInIsolate(vm_ref, settings, task_runners, |
212 | "testCanSaveCompilationTrace" , {}, |
213 | GetFixturesPath()); |
214 | ASSERT_TRUE(isolate); |
215 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
216 | |
217 | Wait(); |
218 | } |
219 | |
220 | class DartSecondaryIsolateTest : public FixtureTest { |
221 | public: |
222 | DartSecondaryIsolateTest() : latch_(3) {} |
223 | |
224 | void LatchCountDown() { latch_.CountDown(); } |
225 | |
226 | void LatchWait() { latch_.Wait(); } |
227 | |
228 | void ChildShutdownSignal() { child_shutdown_latch_.Signal(); } |
229 | |
230 | void ChildShutdownWait() { child_shutdown_latch_.Wait(); } |
231 | |
232 | void RootIsolateShutdownSignal() { root_isolate_shutdown_latch_.Signal(); } |
233 | |
234 | bool RootIsolateIsSignaled() { |
235 | return root_isolate_shutdown_latch_.IsSignaledForTest(); |
236 | } |
237 | |
238 | private: |
239 | fml::CountDownLatch latch_; |
240 | fml::AutoResetWaitableEvent child_shutdown_latch_; |
241 | fml::AutoResetWaitableEvent root_isolate_shutdown_latch_; |
242 | |
243 | FML_DISALLOW_COPY_AND_ASSIGN(DartSecondaryIsolateTest); |
244 | }; |
245 | |
246 | TEST_F(DartSecondaryIsolateTest, CanLaunchSecondaryIsolates) { |
247 | AddNativeCallback("NotifyNative" , |
248 | CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) { |
249 | LatchCountDown(); |
250 | }))); |
251 | AddNativeCallback( |
252 | "PassMessage" , CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) { |
253 | auto message = tonic::DartConverter<std::string>::FromDart( |
254 | Dart_GetNativeArgument(args, 0)); |
255 | ASSERT_EQ("Hello from code is secondary isolate." , message); |
256 | LatchCountDown(); |
257 | }))); |
258 | auto settings = CreateSettingsForFixture(); |
259 | settings.root_isolate_shutdown_callback = [this]() { |
260 | RootIsolateShutdownSignal(); |
261 | }; |
262 | settings.isolate_shutdown_callback = [this]() { ChildShutdownSignal(); }; |
263 | auto vm_ref = DartVMRef::Create(settings); |
264 | auto thread = CreateNewThread(); |
265 | TaskRunners task_runners(GetCurrentTestName(), // |
266 | thread, // |
267 | thread, // |
268 | thread, // |
269 | thread // |
270 | ); |
271 | auto isolate = RunDartCodeInIsolate(vm_ref, settings, task_runners, |
272 | "testCanLaunchSecondaryIsolate" , {}, |
273 | GetFixturesPath()); |
274 | ASSERT_TRUE(isolate); |
275 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
276 | ChildShutdownWait(); // wait for child isolate to shutdown first |
277 | ASSERT_FALSE(RootIsolateIsSignaled()); |
278 | LatchWait(); // wait for last NotifyNative called by main isolate |
279 | // root isolate will be auto-shutdown |
280 | } |
281 | |
282 | TEST_F(DartIsolateTest, CanRecieveArguments) { |
283 | AddNativeCallback("NotifyNative" , |
284 | CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) { |
285 | ASSERT_TRUE(tonic::DartConverter<bool>::FromDart( |
286 | Dart_GetNativeArgument(args, 0))); |
287 | Signal(); |
288 | }))); |
289 | |
290 | const auto settings = CreateSettingsForFixture(); |
291 | auto vm_ref = DartVMRef::Create(settings); |
292 | auto thread = CreateNewThread(); |
293 | TaskRunners task_runners(GetCurrentTestName(), // |
294 | thread, // |
295 | thread, // |
296 | thread, // |
297 | thread // |
298 | ); |
299 | auto isolate = RunDartCodeInIsolate(vm_ref, settings, task_runners, |
300 | "testCanRecieveArguments" , {"arg1" }, |
301 | GetFixturesPath()); |
302 | ASSERT_TRUE(isolate); |
303 | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); |
304 | |
305 | Wait(); |
306 | } |
307 | |
308 | } // namespace testing |
309 | } // namespace flutter |
310 | |