• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
17 #define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
18 
19 #include "test/utils/test_events.h"
20 #include "test/utils/test_extractor.h"
21 
22 #include "agent/debugger_impl.h"
23 
24 #include "ecmascript/jspandafile/js_pandafile_manager.h"
25 #include "ecmascript/debugger/js_debugger.h"
26 #include "os/mutex.h"
27 
28 namespace panda::ecmascript::tooling::test {
29 template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
30 using CUnorderedMap = panda::ecmascript::CUnorderedMap<Key, T, Hash, KeyEqual>;
31 using TestMap = CUnorderedMap<std::string, std::unique_ptr<TestEvents>>;
32 
33 class TestUtil {
34 public:
RegisterTest(const std::string & testName,std::unique_ptr<TestEvents> test)35     static void RegisterTest(const std::string &testName, std::unique_ptr<TestEvents> test)
36     {
37         testMap_.insert({testName, std::move(test)});
38     }
39 
GetTest(const std::string & name)40     static TestEvents *GetTest(const std::string &name)
41     {
42         auto iter = std::find_if(testMap_.begin(), testMap_.end(), [&name](auto &it) {
43             return it.first == name;
44         });
45         if (iter != testMap_.end()) {
46             return iter->second.get();
47         }
48         LOG_DEBUGGER(FATAL) << "Test " << name << " not found";
49         return nullptr;
50     }
51 
WaitForBreakpoint(JSPtLocation location)52     static void WaitForBreakpoint(JSPtLocation location)
53     {
54         auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; };
55         auto onSuccess = []() REQUIRES(eventMutex_) {
56             // Need to reset location, because we might want to stop at the same point
57             lastEventLocation_ = JSPtLocation(nullptr, EntityId(0), 0);
58         };
59 
60         WaitForEvent(DebugEvent::BREAKPOINT, predicate, onSuccess);
61     }
62 
WaitForDropframe()63     static bool WaitForDropframe()
64     {
65         auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::DROPFRAME; };
66         return WaitForEvent(DebugEvent::DROPFRAME, predicate, [] {});
67     }
68 
WaitForCheckComplete()69     static bool WaitForCheckComplete()
70     {
71         auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::CHECK_COMPLETE; };
72         return WaitForEvent(DebugEvent::CHECK_COMPLETE, predicate, [] {});
73     }
74 
WaitForExit()75     static bool WaitForExit()
76     {
77         return WaitForEvent(DebugEvent::VM_DEATH,
78             []() REQUIRES(eventMutex_) {
79                 return lastEvent_ == DebugEvent::VM_DEATH;
80             }, [] {});
81     }
82 
WaitForStepComplete(JSPtLocation location)83     static void WaitForStepComplete(JSPtLocation location)
84     {
85         auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; };
86         auto onSuccess = []() REQUIRES(eventMutex_) {
87             // Need to reset location, because we might want to stop at the same point
88             lastEventLocation_ = JSPtLocation(nullptr, EntityId(0), 0);
89         };
90 
91         WaitForEvent(DebugEvent::STEP_COMPLETE, predicate, onSuccess);
92     }
93 
WaitForException()94     static bool WaitForException()
95     {
96         auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::EXCEPTION; };
97         return WaitForEvent(DebugEvent::EXCEPTION, predicate, [] {});
98     }
99 
WaitForInit()100     static bool WaitForInit()
101     {
102         return WaitForEvent(DebugEvent::VM_START,
103             []() REQUIRES(eventMutex_) {
104                 return initialized_;
105             }, [] {});
106     }
107 
WaitForLoadModule()108     static bool WaitForLoadModule()
109     {
110         auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::LOAD_MODULE; };
111         return WaitForEvent(DebugEvent::LOAD_MODULE, predicate, [] {});
112     }
113 
114     static void Event(DebugEvent event, JSPtLocation location = JSPtLocation(nullptr, EntityId(0), 0))
115     {
116         LOG_DEBUGGER(DEBUG) << "Occurred event " << event;
117         os::memory::LockHolder holder(eventMutex_);
118         lastEvent_ = event;
119         lastEventLocation_ = location;
120         if (event == DebugEvent::VM_START) {
121             initialized_ = true;
122         }
123         eventCv_.Signal();
124     }
125 
Reset()126     static void Reset()
127     {
128         os::memory::LockHolder lock(eventMutex_);
129         initialized_ = false;
130         lastEvent_ = DebugEvent::VM_START;
131     }
132 
GetTests()133     static TestMap &GetTests()
134     {
135         return testMap_;
136     }
137 
IsTestFinished()138     static bool IsTestFinished()
139     {
140         os::memory::LockHolder lock(eventMutex_);
141         return lastEvent_ == DebugEvent::VM_DEATH;
142     }
143 
GetLocation(const char * sourceFile,int32_t line,int32_t column,const char * pandaFile)144     static JSPtLocation GetLocation(const char *sourceFile, int32_t line, int32_t column, const char *pandaFile)
145     {
146         auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->FindJSPandaFile(pandaFile);
147         if (jsPandaFile == nullptr) {
148             LOG_DEBUGGER(FATAL) << "cannot find: " << pandaFile;
149             UNREACHABLE();
150         }
151         TestExtractor extractor(jsPandaFile.get());
152         auto [id, offset] = extractor.GetBreakpointAddress({jsPandaFile.get(), line, column});
153         return JSPtLocation(jsPandaFile.get(), id, offset, sourceFile);
154     }
155 
GetSourceLocation(const JSPtLocation & location,const char * pandaFile)156     static SourceLocation GetSourceLocation(const JSPtLocation &location, const char *pandaFile)
157     {
158         auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->FindJSPandaFile(pandaFile);
159         if (jsPandaFile == nullptr) {
160             LOG_DEBUGGER(FATAL) << "cannot find: " << pandaFile;
161             UNREACHABLE();
162         }
163         TestExtractor extractor(jsPandaFile.get());
164         return extractor.GetSourceLocation(jsPandaFile.get(), location.GetMethodId(), location.GetBytecodeOffset());
165     }
166 
167     static bool SuspendUntilContinue(DebugEvent reason, JSPtLocation location = JSPtLocation(nullptr, EntityId(0), 0))
168     {
169         os::memory::LockHolder lock(suspendMutex_);
170         suspended_ = true;
171 
172         // Notify the debugger thread about the suspend event
173         Event(reason, location);
174 
175         // Wait for continue
176         while (suspended_) {
177             constexpr uint64_t TIMEOUT_MSEC = 300000U;
178             bool timeExceeded = suspendCv_.TimedWait(&suspendMutex_, TIMEOUT_MSEC);
179             if (timeExceeded) {
180                 LOG_DEBUGGER(FATAL) << "Time limit exceeded while suspend";
181                 return false;
182             }
183         }
184 
185         return true;
186     }
187 
Continue()188     static bool Continue()
189     {
190         os::memory::LockHolder lock(suspendMutex_);
191         suspended_ = false;
192         suspendCv_.Signal();
193         return true;
194     }
195 
196 private:
197     template<class Predicate, class OnSuccessAction>
WaitForEvent(DebugEvent event,Predicate predicate,OnSuccessAction action)198     static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action)
199     {
200         os::memory::LockHolder holder(eventMutex_);
201         while (!predicate()) {
202             if (lastEvent_ == DebugEvent::VM_DEATH) {
203                 return false;
204             }
205             constexpr uint64_t TIMEOUT_MSEC = 300000U;
206             bool timeExceeded = eventCv_.TimedWait(&eventMutex_, TIMEOUT_MSEC);
207             if (timeExceeded) {
208                 LOG_DEBUGGER(FATAL) << "Time limit exceeded while waiting " << event;
209                 return false;
210             }
211         }
212         action();
213         return true;
214     }
215 
216     static TestMap testMap_;
217     static os::memory::Mutex eventMutex_;
218     static os::memory::ConditionVariable eventCv_ GUARDED_BY(eventMutex_);
219     static DebugEvent lastEvent_ GUARDED_BY(eventMutex_);
220     static JSPtLocation lastEventLocation_ GUARDED_BY(eventMutex_);
221     static os::memory::Mutex suspendMutex_;
222     static os::memory::ConditionVariable suspendCv_ GUARDED_BY(suspendMutex_);
223     static bool suspended_ GUARDED_BY(suspendMutex_);
224     static bool initialized_ GUARDED_BY(eventMutex_);
225 };
226 
227 std::ostream &operator<<(std::ostream &out, std::nullptr_t);
228 
229 #define ASSERT_FAIL_(val1, val2, strval1, strval2, msg)                                    \
230     do {                                                                                   \
231         std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl;   \
232         std::cerr << "Expected that " strval1 " is " << (msg) << " " strval2 << std::endl; \
233         std::cerr << "\t" strval1 ": " << (val1) << std::endl;                             \
234         std::cerr << "\t" strval2 ": " << (val2) << std::endl;                             \
235         std::abort();                                                                      \
236     } while (0)
237 
238 #define ASSERT_TRUE(cond)                                       \
239     do {                                                        \
240         auto res = (cond);                                      \
241         if (!res) {                                             \
242             ASSERT_FAIL_(res, true, #cond, "true", "equal to"); \
243         }                                                       \
244     } while (0)
245 
246 #define ASSERT_FALSE(cond)                                        \
247     do {                                                          \
248         auto res = (cond);                                        \
249         if (res) {                                                \
250             ASSERT_FAIL_(res, false, #cond, "false", "equal to"); \
251         }                                                         \
252     } while (0)
253 
254 #define ASSERT_EQ(lhs, rhs)                                   \
255     do {                                                      \
256         auto res1 = (lhs);                                    \
257         decltype(res1) res2 = (rhs);                          \
258         if (res1 != res2) {                                   \
259             ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
260         }                                                     \
261     } while (0)
262 
263 #define ASSERT_NE(lhs, rhs)                                       \
264     do {                                                          \
265         auto res1 = (lhs);                                        \
266         decltype(res1) res2 = (rhs);                              \
267         if (res1 == res2) {                                       \
268             ASSERT_FAIL_(res1, res2, #lhs, #rhs, "not equal to"); \
269         }                                                         \
270     } while (0)
271 
272 #define ASSERT_STREQ(lhs, rhs)                                \
273     do {                                                      \
274         auto res1 = (lhs);                                    \
275         decltype(res1) res2 = (rhs);                          \
276         if (::strcmp(res1, res2) != 0) {                      \
277             ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
278         }                                                     \
279     } while (0)
280 
281 #define ASSERT_SUCCESS(api_call)                                                                    \
282     do {                                                                                            \
283         auto error = api_call;                                                                      \
284         if (error) {                                                                                \
285             ASSERT_FAIL_(error.value().GetMessage(), "Success", "API call result", "Expected", ""); \
286         }                                                                                           \
287     } while (0)
288 
289 #define ASSERT_EXITED()                                                                               \
290     do {                                                                                              \
291         bool res = TestUtil::WaitForExit();                                                           \
292         if (!res) {                                                                                   \
293             ASSERT_FAIL_(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", ""); \
294         }                                                                                             \
295     } while (0)
296 
297 #define ASSERT_LOCATION_EQ(lhs, rhs)                                                 \
298     do {                                                                             \
299         ASSERT_EQ((lhs).GetJsPandaFile(), (rhs).GetJsPandaFile());                   \
300         ASSERT_EQ((lhs).GetMethodId().GetOffset(), (rhs).GetMethodId().GetOffset()); \
301         ASSERT_EQ((lhs).GetBytecodeOffset(), (rhs).GetBytecodeOffset());             \
302     } while (0)
303 }  // namespace panda::ecmascript::tooling::test
304 
305 #endif  // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
306