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
18namespace flutter {
19namespace testing {
20
21class 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
35TEST_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
69TEST_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
109TEST_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
125TEST_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
141TEST_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
165TEST_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
189TEST_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
220class 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
246TEST_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
282TEST_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