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