• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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