1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "test.h"
17
18 #include "native_engine/impl/ark/ark_native_engine.h"
19 #include "utils/log.h"
20
21 #include <unistd.h>
22
23 using ArkIdleMonitor = panda::ecmascript::ArkIdleMonitor;
24 using panda::RuntimeOption;
25
26 struct ThreadArgs {
27 NativeEngine* engine = nullptr;
28 bool initialState = false;
29 bool suspendState = false;
30 bool resumeState = false;
31 };
32
33 static NativeEngine* g_nativeEngine = nullptr;
34 LoggerCollector* LoggerCollector::last_ = nullptr;
35
NativeEngineTest()36 NativeEngineTest::NativeEngineTest()
37 {
38 engine_ = g_nativeEngine;
39 }
40
~NativeEngineTest()41 NativeEngineTest::~NativeEngineTest()
42 {}
43
Run(void * args)44 void *NativeEngineTest::Run(void *args)
45 {
46 ThreadArgs* threadArgs = reinterpret_cast<ThreadArgs*>(args);
47 NativeEngine* engine = threadArgs->engine;
48 threadArgs->initialState = engine->IsSuspended();
49 engine->SuspendVM();
50 threadArgs->suspendState = engine->IsSuspended();
51 engine->ResumeVM();
52 sleep(1);
53 threadArgs->resumeState = engine->IsSuspended();
54 return nullptr;
55 }
56
Run()57 void DeathTest::Run()
58 {
59 size_t stackSize = GetCurrentStackSize();
60 ASSERT_NE(pipe(coutPipe_), -1);
61 ASSERT_NE(pipe(cerrPipe_), -1);
62
63 // Malloc stack of child process
64 ASSERT_FALSE(stackSize < 0 || stackSize > MAX_ALLOWED_CHILD_PROCESS_STACK_SIZE);
65 char* childStack = new char[stackSize];
66 ASSERT_NE(childStack, nullptr);
67
68 pid_t childPid = clone(RunInChild, childStack + stackSize, SIGCHLD, (void*)this);
69 if (childPid != -1) {
70 int status = 0;
71 ASSERT_EQ(waitpid(childPid, &status, 0), childPid);
72
73 isExit_ = WIFEXITED(status);
74 exitCode_ = WEXITSTATUS(status);
75 isSignal_ = WIFSIGNALED(status);
76 signal_ = WTERMSIG(status);
77
78 coutResult_ = ReadFd(coutPipe_[0]);
79 cerrResult_ = ReadFd(cerrPipe_[0]);
80 AssertResult();
81 }
82
83 close(coutPipe_[0]);
84 close(cerrPipe_[0]);
85
86 free(childStack);
87 ASSERT_NE(childPid, -1);
88 }
89
GetCurrentStackSize()90 size_t DeathTest::GetCurrentStackSize()
91 {
92 // Get stack size of current thread
93 pthread_attr_t attr;
94 size_t stackSize = 0;
95 pthread_getattr_np(pthread_self(), &attr);
96 pthread_attr_getstacksize(&attr, &stackSize);
97 pthread_attr_destroy(&attr);
98 return stackSize;
99 }
100
RunInChild(void * arg)101 int DeathTest::RunInChild(void* arg)
102 {
103 // block to generate cppcrash log
104 if (signal(SIGABRT, SIG_IGN) == SIG_ERR) {
105 std::cerr << "Failed to register abort signal handler." << std::endl;
106 }
107
108 DeathTest* that = reinterpret_cast<DeathTest*>(arg);
109 // close unused write pipe port
110 close(that->coutPipe_[0]);
111 close(that->cerrPipe_[0]);
112 // redirect cout/cerr to pipe
113 dup2(that->coutPipe_[1], STDOUT_FILENO);
114 dup2(that->cerrPipe_[1], STDERR_FILENO);
115
116 // redirect hilog to stdout
117 LOG_SetCallback(RedirectHilog);
118
119 // execute test case.
120 that->TestBody();
121
122 // close read pipe port
123 close(that->coutPipe_[1]);
124 close(that->cerrPipe_[1]);
125 return 0;
126 }
127
RedirectHilog(const LogType,const LogLevel level,const unsigned int,const char *,const char * msg)128 void DeathTest::RedirectHilog(const LogType, const LogLevel level, const unsigned int, const char*, const char* msg)
129 {
130 if (level >= LogLevel::LOG_WARN) {
131 std::cerr << msg << std::endl;
132 } else {
133 std::cout << msg << std::endl;
134 }
135 }
136
ReadFd(int fd)137 std::string DeathTest::ReadFd(int fd)
138 {
139 std::string result;
140 // set pipe to non-block mode
141 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
142 std::cerr << "Failed to set fd to non-block mode" << std::endl;
143 return result;
144 }
145 constexpr int bufferSize = 64;
146 char buffer[bufferSize];
147 ssize_t readSize = 0;
148 while ((readSize = read(fd, buffer, sizeof(buffer))) > 0) {
149 result += std::string(buffer, readSize);
150 };
151 return result;
152 }
153
main(int argc,char ** argv)154 int main(int argc, char** argv)
155 {
156 testing::GTEST_FLAG(output) = "xml:./";
157 testing::InitGoogleTest(&argc, argv);
158
159 // Setup
160 RuntimeOption option;
161 option.SetGcType(RuntimeOption::GC_TYPE::GEN_GC);
162 const int64_t poolSize = 0x1000000; // 16M
163 option.SetGcPoolSize(poolSize);
164 option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR);
165 option.SetDebuggerLibraryPath("");
166 EcmaVM* vm = panda::JSNApi::CreateJSVM(option);
167 if (vm == nullptr) {
168 return 0;
169 }
170
171 g_nativeEngine = new ArkNativeEngine(vm, nullptr);
172
173 int ret = testing::UnitTest::GetInstance()->Run();
174
175 g_nativeEngine->Loop(LOOP_NOWAIT);
176
177 delete g_nativeEngine;
178 g_nativeEngine = nullptr;
179 panda::JSNApi::DestroyJSVM(vm);
180 vm = nullptr;
181
182 return ret;
183 }
184
185 HWTEST_F(NativeEngineTest, SuspendVM001, testing::ext::TestSize.Level0)
186 {
187 pthread_t tids;
188 struct ThreadArgs *args = new ThreadArgs;
189 args->engine = engine_;
190 int res = pthread_create(&tids, NULL, Run, (void*)args);
191 if (res != 0) {
192 std::cout << "thread create failed";
193 return;
194 }
195 for (int i = 0; i < 3; ++i) { // 3:Loop 3 times
196 std::this_thread::sleep_for(std::chrono::milliseconds(100));
197 engine_->CheckSafepoint();
198 }
199 ASSERT_TRUE(!args->initialState);
200 ASSERT_TRUE(args->suspendState);
201 ASSERT_TRUE(!args->resumeState);
202 delete args;
203 args = nullptr;
204 }
205
206 HWTEST_F(NativeEngineTest, CreateRuntimeFunc001, testing::ext::TestSize.Level0)
207 {
__anon64adc9040102() 208 std::thread t([this]() {
209 auto result = engine_->CreateRuntime(true);
210 ASSERT_TRUE(result);
211 delete reinterpret_cast<NativeEngine *>(result);
212 });
213 t.join();
214 }
215
216 HWTEST_F(NativeEngineTest, ExecuteTranslateBySourceMapFunc001, testing::ext::TestSize.Level0)
217 {
218 std::string stack = engine_->ExecuteTranslateBySourceMap("test1/test2/test3/test.ts");
219 ASSERT_EQ(stack, "test1/test2/test3/test.ts");
220 }
221
222 HWTEST_F(NativeEngineTest, NapiErrorManagerTest001, testing::ext::TestSize.Level0)
223 {
224 auto onWorkerErrorCallback =
__anon64adc9040202(napi_env env, napi_value exception, std::string vm_name, uint32_t type) 225 [] (napi_env env, napi_value exception, std::string vm_name, uint32_t type) -> bool {
226 EXPECT_EQ(type, 2);
227 EXPECT_EQ(vm_name, "test");
228 return true;
229 };
230 auto onMainThreadErrorCallback =
231 [] (napi_env env, std::string summary, std::string name,
__anon64adc9040302(napi_env env, std::string summary, std::string name, std::string message, std::string stack) 232 std::string message, std::string stack) -> bool {
233 EXPECT_EQ(name, "name");
234 EXPECT_EQ(message, "message");
235 return true;
236 };
__anon64adc9040402() 237 auto hasOnErrorCb = [] () -> bool {
238 return true;
239 };
240 NapiErrorManager::GetInstance()->RegisterHasOnErrorCallback(hasOnErrorCb);
241 NapiErrorManager::GetInstance()->RegisterOnErrorCallback(onWorkerErrorCallback, onMainThreadErrorCallback);
242 ASSERT_NE(NapiErrorManager::GetInstance()->GetOnWorkerErrorCallback(), nullptr);
243 ASSERT_NE(NapiErrorManager::GetInstance()->GetOnMainThreadErrorCallback(), nullptr);
244 napi_value exception = nullptr;
245 NapiErrorManager::GetInstance()->NotifyUncaughtException(reinterpret_cast<napi_env>(engine_),
246 exception, "test", 2);
247 ASSERT_EQ(NapiErrorManager::GetInstance()->NotifyUncaughtException(reinterpret_cast<napi_env>(engine_),
248 "", "name", "message", ""), true);
249 }
250
251 HWTEST_F(NativeEngineTest, NapiErrorManagerTest002, testing::ext::TestSize.Level0)
252 {
253 auto allUnhandledRejectionCallbackTest =
__anon64adc9040502(napi_env env, napi_value* exception, std::string vm_name, uint32_t type) 254 [] (napi_env env, napi_value* exception, std::string vm_name, uint32_t type) -> bool {
255 EXPECT_EQ(type, 0);
256 EXPECT_EQ(vm_name, "test");
257 return true;
258 };
__anon64adc9040602() 259 auto hasAllUnhandledRejectionCb = [] () -> bool {
260 return false;
261 };
262
263 NapiErrorManager::GetInstance()->RegisterHasAllUnhandledRejectionCallback(hasAllUnhandledRejectionCb);
264 napi_value* exception = nullptr;
265 NapiErrorManager::GetInstance()->RegisterAllUnhandledRejectionCallback(allUnhandledRejectionCallbackTest);
266 ASSERT_NE(NapiErrorManager::GetInstance()->GetAllUnhandledRejectionCallback(), nullptr);
267 NapiErrorManager::GetInstance()->NotifyUnhandledRejection(reinterpret_cast<napi_env>(engine_),
268 exception, "test", 0);
269 }
270
271 HWTEST_F(NativeEngineTest, NapiErrorManagerTest003, testing::ext::TestSize.Level0)
272 {
__anon64adc9040702() 273 auto hasOnErrorCb = [] () -> bool {
274 return true;
275 };
276
277 NapiErrorManager::GetInstance()->RegisterHasOnErrorCallback(hasOnErrorCb);
278 auto callback = NapiErrorManager::GetInstance()->GetHasErrorCallback();
279 ASSERT_NE(callback, nullptr);
280 ASSERT_EQ(callback(), true);
281 }
282
283 HWTEST_F(NativeEngineTest, NapiErrorManagerTest004, testing::ext::TestSize.Level0)
284 {
__anon64adc9040802() 285 auto hasAllUnhandledRejectionCb = [] () -> bool {
286 return false;
287 };
288
289 NapiErrorManager::GetInstance()->RegisterHasAllUnhandledRejectionCallback(hasAllUnhandledRejectionCb);
290 auto callback = NapiErrorManager::GetInstance()->GetHasAllUnhandledRejectionCallback();
291 ASSERT_NE(callback, nullptr);
292 ASSERT_EQ(callback(), false);
293 }
294
295 HWTEST_F(NativeEngineTest, FinalizersCallbackTest001, testing::ext::TestSize.Level0)
296 {
297 ASSERT_NE(engine_, nullptr);
298 napi_env env = (napi_env)engine_;
299 const EcmaVM *vm = reinterpret_cast<ArkNativeEngine*>(engine_)->GetEcmaVm();
300
301 const char *str = "FinalizersCallbackTest001";
302 size_t size = 2 * ArkNativeEngine::FINALIZERS_PACK_PENDING_NATIVE_BINDING_SIZE_THRESHOLD;
303 static bool finalizersCallbackDone[2] = {false, false};
304
305 for (int i = 0; i < 2; ++i) {
306 {
307 panda::LocalScope scope(vm);
308 napi_value object = nullptr;
309 napi_create_object(env, &object);
__anon64adc9040902(napi_env env, void *data, void *hint) 310 napi_wrap_with_size(env, object, (void*)str, [](napi_env env, void *data, void *hint) {
311 bool *result = reinterpret_cast<bool*>(hint);
312 ASSERT_FALSE(*result);
313 *result = true;
314 }, reinterpret_cast<void*>(&finalizersCallbackDone[i]), nullptr, size);
315 }
316 panda::JSNApi::TriggerGC(vm, panda::ecmascript::GCReason::OTHER, panda::JSNApi::TRIGGER_GC_TYPE::FULL_GC);
317 }
318
319 ASSERT_FALSE(finalizersCallbackDone[0]);
320 ASSERT_TRUE(finalizersCallbackDone[1]);
321 }
322
323 /**
324 * @tc.name: SetMainThreadEcmaVMTest
325 * @tc.desc: Test interface of ArkIdleMonitor
326 * @tc.type: FUNC
327 */
328 HWTEST_F(NativeEngineTest, SetMainThreadEcmaVMTest001, testing::ext::TestSize.Level0)
329 {
330 ASSERT_NE(engine_, nullptr);
331 EcmaVM *vm = const_cast<EcmaVM*>(reinterpret_cast<ArkNativeEngine*>(engine_)->GetEcmaVm());
332
333 auto arkIdleMonitor = ArkIdleMonitor::GetInstance();
334 arkIdleMonitor->SetMainThreadEcmaVM(vm);
335
336 int64_t oldIdleDuration = arkIdleMonitor->GetTotalIdleDuration();
337 for (int i = 0; i < 10; i++) {
338 arkIdleMonitor->NotifyLooperIdleStart(i * 500, 20);
339 ASSERT_EQ(arkIdleMonitor->IsIdleState(), true);
340 arkIdleMonitor->NotifyLooperIdleEnd(i * 500 + 250);
341 ASSERT_EQ(arkIdleMonitor->IsIdleState(), false);
342 }
343
344 arkIdleMonitor->SetMainThreadEcmaVM(nullptr);
345
346 for (int i = 10; i < 20; i++) {
347 arkIdleMonitor->NotifyLooperIdleStart(i * 500, 20);
348 ASSERT_EQ(arkIdleMonitor->IsIdleState(), true);
349 arkIdleMonitor->NotifyLooperIdleEnd(i * 500 + 250);
350 ASSERT_EQ(arkIdleMonitor->IsIdleState(), false);
351 }
352
353 int64_t newIdleDuration = arkIdleMonitor->GetTotalIdleDuration();
354 ASSERT_TRUE(newIdleDuration > oldIdleDuration);
355 ASSERT_TRUE(arkIdleMonitor->GetIdleNotifyCount() > 0);
356 }
357
358 /**
359 * @tc.name: NotifyChangeBackgroundStateTest
360 * @tc.desc: Test interface of ArkIdleMonitor
361 * @tc.type: FUNC
362 */
363 HWTEST_F(NativeEngineTest, NotifyChangeBackgroundStateTest001, testing::ext::TestSize.Level0)
364 {
365 ASSERT_NE(engine_, nullptr);
366 EcmaVM *vm = const_cast<EcmaVM*>(reinterpret_cast<ArkNativeEngine*>(engine_)->GetEcmaVm());
367
368 auto arkIdleMonitor = ArkIdleMonitor::GetInstance();
369 arkIdleMonitor->SetMainThreadEcmaVM(vm);
370 arkIdleMonitor->SetStartTimerCallback();
371 arkIdleMonitor->NotifyChangeBackgroundState(true);
372 ASSERT_TRUE(arkIdleMonitor->IsInBackground() == true);
373 arkIdleMonitor->NotifyChangeBackgroundState(false);
374 ASSERT_TRUE(arkIdleMonitor->IsInBackground() == false);
375 }
376
377 /**
378 * @tc.name: RegisterWorkerEnvTest
379 * @tc.desc: Test interface of ArkIdleMonitor
380 * @tc.type: FUNC
381 */
382 HWTEST_F(NativeEngineTest, RegisterWorkerEnvTest001, testing::ext::TestSize.Level0)
383 {
384 ASSERT_NE(engine_, nullptr);
385 EcmaVM *vm = const_cast<EcmaVM*>(reinterpret_cast<ArkNativeEngine*>(engine_)->GetEcmaVm());
386
387 auto arkIdleMonitor = ArkIdleMonitor::GetInstance();
388 arkIdleMonitor->SetMainThreadEcmaVM(vm);
389
__anon64adc9040a02() 390 std::thread t([&arkIdleMonitor]() {
391 RuntimeOption option;
392 option.SetGcType(RuntimeOption::GC_TYPE::GEN_GC);
393 const int64_t poolSize = 0x1000000; // 16M
394 option.SetGcPoolSize(poolSize);
395 option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR);
396 option.SetDebuggerLibraryPath("");
397 auto workerVM1 = panda::JSNApi::CreateJSVM(option);
398 auto workerVM2 = panda::JSNApi::CreateJSVM(option);
399 ArkNativeEngine *workerEngine1 = new ArkNativeEngine(workerVM1, nullptr);
400 ArkNativeEngine *workerEngine2 = new ArkNativeEngine(workerVM2, nullptr);
401 napi_env wokerEnv1 = reinterpret_cast<napi_env>(workerEngine1);
402 napi_env wokerEnv2 = reinterpret_cast<napi_env>(workerEngine2);
403
404 arkIdleMonitor->UnregisterWorkerEnv(wokerEnv1);
405 arkIdleMonitor->UnregisterWorkerEnv(wokerEnv2);
406
407 arkIdleMonitor->RegisterWorkerEnv(wokerEnv1);
408 arkIdleMonitor->RegisterWorkerEnv(wokerEnv2);
409
410 arkIdleMonitor->UnregisterWorkerEnv(wokerEnv1);
411 arkIdleMonitor->UnregisterWorkerEnv(wokerEnv2);
412
413 delete workerEngine1;
414 delete workerEngine2;
415 });
416 t.join();
417 }
418
419 /**
420 * @tc.name: SetRawHeapTrimLevel
421 * @tc.desc: Test interface of SetRawHeapTrimLevel
422 * @tc.type: FUNC
423 */
424 HWTEST_F(NativeEngineTest, SetRawHeapTrimLevelTest001, testing::ext::TestSize.Level0)
425 {
426 ASSERT_NE(engine_, nullptr);
427 EcmaVM *vm = const_cast<EcmaVM*>(reinterpret_cast<ArkNativeEngine*>(engine_)->GetEcmaVm());
428
429 auto arkIdleMonitor = ArkIdleMonitor::GetInstance();
430 arkIdleMonitor->SetMainThreadEcmaVM(vm);
431
432 engine_->SetRawHeapTrimLevel(1); // test value
433 }
434