1 /* 2 * Copyright (c) 2021-2022 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 #ifndef FOUNDATION_ACE_NAPI_TEST_UNITTEST_TEST_H 17 #define FOUNDATION_ACE_NAPI_TEST_UNITTEST_TEST_H 18 19 #include "ark_native_engine.h" 20 #include "event_handler.h" 21 #include "napi/native_api.h" 22 #include "native_engine.h" 23 #include "test_common.h" 24 25 class NativeEngineTest : public testing::Test { 26 public: 27 NativeEngineTest(); 28 virtual ~NativeEngineTest(); 29 static void* Run(void* args); 30 31 protected: 32 NativeEngine* engine_; 33 NativeEngine* multiContextEngine_; 34 NativeEngine* moduleNameEngine_; 35 std::shared_ptr<OHOS::AppExecFwk::EventHandler> eventHandler_ = nullptr; 36 }; 37 38 class DeathTest { 39 public: DeathTest()40 DeathTest() {}; 41 void Run(); 42 43 virtual void TestBody() = 0; 44 virtual void AssertResult() = 0; 45 46 static constexpr size_t MAX_ALLOWED_CHILD_PROCESS_STACK_SIZE = 8 * (1 << 20); // 8MB for max stack size 47 48 protected: 49 std::string coutResult_; 50 std::string cerrResult_; 51 bool isExit_ {false}; 52 int exitCode_ {0}; 53 bool isSignal_ {false}; 54 int signal_ {0}; 55 56 private: 57 static size_t GetCurrentStackSize(); 58 static int RunInChild(void* arg); 59 static void RedirectHilog(const LogType, const LogLevel level, const unsigned int, const char*, const char* msg); 60 static std::string ReadFd(int fd); 61 62 int cerrPipe_[2]; 63 int coutPipe_[2]; 64 }; 65 66 class BasicDeathTest final : public DeathTest { 67 public: 68 typedef void (*TestFunc)(); 69 typedef void (*AssertFunc)(std::string log, std::string err); BasicDeathTest(TestFunc testFunc,AssertFunc assertFunc)70 BasicDeathTest(TestFunc testFunc, AssertFunc assertFunc) : test_(testFunc), assert_(assertFunc) {} TestBody()71 void TestBody() override 72 { 73 if (test_ != nullptr) { 74 test_(); 75 } 76 } AssertResult()77 void AssertResult() override 78 { 79 if (assert_ != nullptr) { 80 assert_(coutResult_, cerrResult_); 81 } 82 } 83 84 private: 85 TestFunc test_ { nullptr }; 86 AssertFunc assert_ { nullptr }; 87 }; 88 89 class NativeEngineProxy { 90 public: 91 NativeEngineProxy(const NativeEngineProxy&) = delete; 92 NativeEngineProxy(NativeEngineProxy&&) noexcept = delete; 93 NativeEngineProxy& operator=(const NativeEngineProxy&) = delete; 94 NativeEngineProxy& operator=(NativeEngineProxy&&) noexcept = delete; 95 NativeEngineProxy()96 NativeEngineProxy() 97 { 98 // Setup 99 panda::RuntimeOption option; 100 option.SetGcType(panda::RuntimeOption::GC_TYPE::GEN_GC); 101 const int64_t poolSize = 0x1000000; // 16M 102 option.SetGcPoolSize(poolSize); 103 option.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::ERROR); 104 option.SetDebuggerLibraryPath(""); 105 vm_ = panda::JSNApi::CreateJSVM(option); 106 if (vm_ == nullptr) { 107 return; 108 } 109 110 engine_ = new ArkNativeEngine(vm_, nullptr); 111 engine_->SetCleanEnv([this] { 112 panda::JSNApi::DestroyJSVM(vm_); 113 vm_ = nullptr; 114 }); 115 loop_ = engine_->GetUVLoop(); 116 napi_open_handle_scope(reinterpret_cast<napi_env>(engine_), &scope_); 117 } 118 NativeEngineProxy(NativeEngine * engine)119 explicit NativeEngineProxy(NativeEngine* engine) : isContextEngine_(true) 120 { 121 napi_create_ark_context(reinterpret_cast<napi_env>(engine), reinterpret_cast<napi_env*>(&engine_)); 122 vm_ = const_cast<EcmaVM*>(engine->GetEcmaVm()); 123 napi_open_handle_scope(reinterpret_cast<napi_env>(engine_), &scope_); 124 loop_ = engine->GetUVLoop(); 125 } 126 ~NativeEngineProxy()127 ~NativeEngineProxy() 128 { 129 napi_close_handle_scope(reinterpret_cast<napi_env>(engine_), scope_); 130 scope_ = nullptr; 131 if (!isContextEngine_) { 132 delete engine_; 133 } else { 134 engine_->DestroyContext(); 135 } 136 engine_ = nullptr; 137 vm_ = nullptr; 138 } 139 RunLoop(uv_run_mode mode)140 void RunLoop(uv_run_mode mode) const 141 { 142 uv_run(loop_, mode); 143 } 144 145 inline ArkNativeEngine* operator->() const 146 { 147 return engine_; 148 } 149 150 inline ArkNativeEngine* operator*() const 151 { 152 return engine_; 153 } 154 napi_env()155 inline operator napi_env() const 156 { 157 return reinterpret_cast<napi_env>(engine_); 158 } 159 160 private: 161 EcmaVM* vm_ { nullptr }; 162 ArkNativeEngine* engine_ { nullptr }; 163 napi_handle_scope scope_ = nullptr; 164 uv_loop_t* loop_ { nullptr }; 165 const bool isContextEngine_ { false }; 166 }; 167 168 class LoggerCollector { 169 public: 170 struct LogInfo { 171 LogType type; 172 LogLevel level; 173 unsigned int domain; 174 std::string tag; 175 std::string msg; 176 }; 177 178 explicit LoggerCollector(LogLevel level = LogLevel::LOG_ERROR, unsigned int domain = 0) level_(level)179 : level_(level), domain_(domain) 180 { 181 Restart(); 182 } 183 ~LoggerCollector()184 ~LoggerCollector() 185 { 186 Pause(); 187 } 188 GetLastLogInfo()189 const LogInfo& GetLastLogInfo() 190 { 191 return lastMessageInfo_; 192 } 193 Pause()194 void Pause() 195 { 196 if (prev_) { 197 prev_->next_ = next_; 198 } 199 if (next_) { 200 next_->prev_ = prev_; 201 } 202 if (last_ == this) { 203 last_ = prev_; 204 } 205 prev_ = nullptr; 206 next_ = nullptr; 207 if (last_ == nullptr) { 208 LOG_SetCallback(nullptr); 209 } 210 } 211 Restart()212 void Restart() 213 { 214 if (last_ != nullptr) { 215 last_->next_ = this; 216 } else { 217 LOG_SetCallback(OnCollect); 218 } 219 prev_ = last_; 220 last_ = this; 221 } 222 223 int IndexOf(const char* msg, int index = 0) 224 { 225 std::string str = stream_.str(); 226 if (index >= str.size()) { 227 return -1; 228 } 229 auto pos = str.find(msg, index); 230 if (pos == std::string::npos) { 231 return -1; 232 } 233 return pos; 234 } 235 236 int Includes(const char* msg, int index = 0) 237 { 238 return IndexOf(msg, index) >= 0; 239 } 240 Clear()241 void Clear() 242 { 243 stream_.flush(); 244 } 245 246 private: Collect(const LogType type,const LogLevel level,const unsigned int domain,const char * tag,const char * msg)247 void Collect(const LogType type, const LogLevel level, const unsigned int domain, const char* tag, const char* msg) 248 { 249 if (domain_ != 0 && domain_ != domain) { 250 return; 251 } 252 if (level < level_) { 253 return; 254 } 255 std::lock_guard<std::mutex> lock(mutex_); 256 lastMessageInfo_.type = type; 257 lastMessageInfo_.level = level; 258 lastMessageInfo_.domain = domain; 259 lastMessageInfo_.tag = tag; 260 lastMessageInfo_.msg = msg; 261 stream_ << msg; 262 } 263 264 static void OnCollect(const LogType type,const LogLevel level,const unsigned int domain,const char * tag,const char * msg)265 OnCollect(const LogType type, const LogLevel level, const unsigned int domain, const char* tag, const char* msg) 266 { 267 auto collector = last_; 268 while (collector) { 269 collector->Collect(type, level, domain, tag, msg); 270 collector = collector->prev_; 271 } 272 }; 273 274 const LogLevel level_; 275 const unsigned int domain_; 276 // write stream and lastMessageInfo should under lock 277 std::mutex mutex_; 278 std::ostringstream stream_; 279 LogInfo lastMessageInfo_; 280 LoggerCollector* prev_ { nullptr }; 281 LoggerCollector* next_ { nullptr }; 282 static LoggerCollector* last_; 283 }; 284 285 class UVLoopRunner { 286 public: UVLoopRunner(uv_loop_t * loop)287 explicit UVLoopRunner(uv_loop_t* loop) : loop_(loop) 288 { 289 uv_walk( 290 loop, 291 [](uv_handle_t* handle, void* data) { 292 // Unref all handles to allow loop auto-exit. 293 if (uv_has_ref(handle) != 0) { 294 uv_unref(handle); 295 reinterpret_cast<UVLoopRunner*>(data)->handles_.insert(handle); 296 } 297 }, 298 this); 299 } UVLoopRunner(NativeEngine * engine)300 explicit UVLoopRunner(NativeEngine* engine) : UVLoopRunner(GetUVLoop(engine)) {} UVLoopRunner(napi_env * env)301 explicit UVLoopRunner(napi_env* env) : UVLoopRunner(reinterpret_cast<NativeEngine*>(env)) {} 302 ~UVLoopRunner()303 ~UVLoopRunner() 304 { 305 uv_walk( 306 loop_, 307 [](uv_handle_t* handle, void* data) { 308 // Restore ref count for all saved handles. 309 if (uv_has_ref(handle) == 0) { 310 auto* that = reinterpret_cast<UVLoopRunner*>(data); 311 if (that->handles_.find(handle) != that->handles_.end()) { 312 uv_ref(handle); 313 } 314 } 315 }, 316 this); 317 handles_.clear(); 318 } 319 320 void Run(uv_run_mode mode = UV_RUN_DEFAULT) 321 { 322 uv_run(loop_, mode); 323 } 324 GetUVLoop(NativeEngine * engine)325 static uv_loop_t* GetUVLoop(NativeEngine* engine) 326 { 327 if (engine->IsMainEnvContext()) { 328 return engine->GetUVLoop(); 329 } else { 330 return engine->GetParent()->GetUVLoop(); 331 } 332 } 333 334 private: 335 uv_loop_t* loop_ { nullptr }; 336 std::unordered_set<uv_handle_t*> handles_ {}; 337 }; 338 339 class NapiAsyncWorkTestData { 340 public: NapiAsyncWorkTestData(napi_env env,const char * name)341 NapiAsyncWorkTestData(napi_env env, const char* name) : env_(env) 342 { 343 napi_value jsName = nullptr; 344 napi_create_string_utf8((env), name, NAPI_AUTO_LENGTH, &jsName); 345 napi_create_async_work((env), nullptr, jsName, OnExecute, OnComplete, this, &(work_)); 346 } 347 ~NapiAsyncWorkTestData()348 ~NapiAsyncWorkTestData() 349 { 350 if (work_ != nullptr) { 351 napi_delete_async_work(env_, work_); 352 } 353 } 354 Execute(napi_env env)355 virtual void Execute(napi_env env) {} Complete(napi_env env,napi_status status)356 virtual void Complete(napi_env env, napi_status status) {} 357 Queue()358 napi_status Queue() 359 { 360 return napi_queue_async_work(env_, work_); 361 } 362 Queue(napi_qos_t qos)363 napi_status Queue(napi_qos_t qos) 364 { 365 return napi_queue_async_work_with_qos(env_, work_, qos); 366 } 367 Cancel()368 napi_status Cancel() 369 { 370 LoggerCollector collector; 371 napi_status status = napi_cancel_async_work(env_, work_); 372 if (status != napi_ok) { 373 return status; 374 } 375 if (collector.Includes("uv_cancel failed")) { 376 return napi_generic_failure; 377 } 378 return napi_ok; 379 } 380 381 protected: 382 napi_env env_; 383 384 private: OnExecute(napi_env env,void * data)385 static void OnExecute(napi_env env, void* data) 386 { 387 auto* that = reinterpret_cast<NapiAsyncWorkTestData*>(data); 388 that->Execute(env); 389 } 390 OnComplete(napi_env env,napi_status status,void * data)391 static void OnComplete(napi_env env, napi_status status, void* data) 392 { 393 auto* that = reinterpret_cast<NapiAsyncWorkTestData*>(data); 394 napi_delete_async_work(env, that->work_); 395 that->work_ = nullptr; 396 that->Complete(env, status); 397 } 398 399 napi_async_work work_ { nullptr }; 400 }; 401 402 #endif /* FOUNDATION_ACE_NAPI_TEST_UNITTEST_TEST_H */ 403