1 /*
2 * Copyright (c) 2023 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_TESTCASES_JS_CONTAINER_TEST_H
17 #define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_CONTAINER_TEST_H
18
19 #include "test/utils/test_util.h"
20
21 namespace panda::ecmascript::tooling::test {
22 class JsContainerTest : public TestEvents {
23 public:
JsContainerTest()24 JsContainerTest()
25 {
26 breakpoint = [this](const JSPtLocation &location) {
27 ASSERT_TRUE(location.GetMethodId().IsValid());
28 ASSERT_LOCATION_EQ(location, location_);
29 ++breakpointCounter_;
30 debugger_->NotifyPaused(location, PauseReason::INSTRUMENTATION);
31 TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location);
32 return true;
33 };
34
35 loadModule = [this](std::string_view moduleName) {
36 std::string pandaFile = DEBUGGER_ABC_DIR "container.abc";
37 std::string sourceFile = DEBUGGER_JS_DIR "container.js";
38 static_cast<JsContainerTestChannel *>(channel_)->Initial(vm_, runtime_);
39 runtime_->Enable();
40 // 348: breakpointer line
41 location_ = TestUtil::GetLocation(sourceFile.c_str(), 348, 0, pandaFile.c_str());
42 ASSERT_TRUE(location_.GetMethodId().IsValid());
43 TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE);
44 ASSERT_EQ(moduleName, pandaFile);
45 ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile));
46 auto condFuncRef = FunctionRef::Undefined(vm_);
47 auto ret = debugInterface_->SetBreakpoint(location_, condFuncRef);
48
49 ASSERT_TRUE(ret);
50 return true;
51 };
52
53 scenario = [this]() {
54 TestUtil::WaitForLoadModule();
55 TestUtil::Continue();
56 TestUtil::WaitForBreakpoint(location_);
57 TestUtil::Continue();
58 auto ret = debugInterface_->RemoveBreakpoint(location_);
59 ASSERT_TRUE(ret);
60 ASSERT_EXITED();
61 return true;
62 };
63
64 vmDeath = [this]() {
65 ASSERT_EQ(breakpointCounter_, 1U); // 1: break point counter
66 return true;
67 };
68
69 channel_ = new JsContainerTestChannel();
70 }
71
GetEntryPoint()72 std::pair<std::string, std::string> GetEntryPoint() override
73 {
74 std::string pandaFile = DEBUGGER_ABC_DIR "container.abc";
75 return {pandaFile, entryPoint_};
76 }
~JsContainerTest()77 ~JsContainerTest()
78 {
79 delete channel_;
80 channel_ = nullptr;
81 }
82
83 private:
84 class JsContainerTestChannel : public TestChannel {
85 public:
86 JsContainerTestChannel() = default;
87 ~JsContainerTestChannel() = default;
Initial(const EcmaVM * vm,RuntimeImpl * runtime)88 void Initial(const EcmaVM *vm, RuntimeImpl *runtime)
89 {
90 vm_ = vm;
91 runtime_ = runtime;
92 }
93
SendNotification(const PtBaseEvents & events)94 void SendNotification(const PtBaseEvents &events) override
95 {
96 const static std::vector<std::function<bool(const PtBaseEvents &events)>> eventList = {
97 [](const PtBaseEvents &events) -> bool {
98 std::string sourceFile = DEBUGGER_JS_DIR "container.js";
99 auto parsed = static_cast<const ScriptParsed *>(&events);
100 std::string str = parsed->ToJson()->Stringify();
101 std::cout << "JsModuleVariableTestChannel: SendNotification 0:\n" << str << std::endl;
102
103 ASSERT_EQ(parsed->GetName(), "Debugger.scriptParsed");
104 ASSERT_EQ(parsed->GetUrl(), sourceFile);
105 return true;
106 },
107 [this](const PtBaseEvents &events) -> bool {
108 auto paused = static_cast<const Paused *>(&events);
109 std::string str = paused->ToJson()->Stringify();
110 std::cout << "JsModuleVariableTestChannel: SendNotification 1:\n" << str << std::endl;
111
112 ASSERT_EQ(paused->GetName(), "Debugger.paused");
113 auto frame = paused->GetCallFrames()->at(0).get();
114 ASSERT_EQ(frame->GetFunctionName(), "foo");
115 auto scopes = frame->GetScopeChain();
116 ASSERT_EQ(scopes->size(), 2U); // 2: contain local and global
117 for (uint32_t i = 0; i < scopes->size(); i++) {
118 auto scope = scopes->at(i).get();
119 if (scope->GetType() != Scope::Type::Module()) {
120 continue;
121 }
122 GetProperties(scope);
123 }
124 return true;
125 }
126 };
127
128 ASSERT_TRUE(eventList[index_](events));
129 index_++;
130 }
131
GetProperties(panda::ecmascript::tooling::Scope * scope)132 void GetProperties(panda::ecmascript::tooling::Scope *scope)
133 {
134 auto localId = scope->GetObject()->GetObjectId();
135 GetPropertiesParams params;
136 params.SetObjectId(localId).SetOwnProperties(true);
137 std::vector<std::unique_ptr<PropertyDescriptor>> outPropertyDesc;
138 runtime_->GetProperties(params, &outPropertyDesc, {}, {}, {});
139 for (const auto &property : outPropertyDesc) {
140 std::cout << "=====================================" << std::endl;
141 std::cout << property->GetName() << std::endl;
142 auto value = property->GetValue();
143 std::vector<std::string> infos;
144 PushValueInfo(value, infos);
145 if (value->GetType() == RemoteObject::TypeName::Object) {
146 std::vector<std::unique_ptr<PropertyDescriptor>> outPropertyDescInner;
147 ASSERT_TRUE(value->HasObjectId());
148 params.SetObjectId(value->GetObjectId()).SetOwnProperties(true);
149 runtime_->GetProperties(params, &outPropertyDescInner, {}, {}, {});
150 if (outPropertyDescInner.size() == 0) {
151 infos.push_back("none");
152 }
153 for (const auto &propertyInner : outPropertyDescInner) {
154 std::cout << "###########################################" << std::endl;
155 std::cout << propertyInner->GetName() << std::endl;
156 infos.push_back(propertyInner->GetName());
157 auto innerValue = propertyInner->GetValue();
158 PushValueInfo(innerValue, infos);
159 }
160 }
161 ASSERT_EQ(infos.size(), variableMap_.at(property->GetName()).size());
162 for (uint32_t j = 0; j < infos.size(); j++) {
163 ASSERT_EQ(infos[j], variableMap_.at(property->GetName())[j]);
164 }
165 }
166 }
167
168 private:
169 NO_COPY_SEMANTIC(JsContainerTestChannel);
170 NO_MOVE_SEMANTIC(JsContainerTestChannel);
171
PushValueInfo(RemoteObject * value,std::vector<std::string> & infos)172 void PushValueInfo(RemoteObject *value, std::vector<std::string> &infos)
173 {
174 std::cout << "type: " << value->GetType() << std::endl;
175 infos.push_back(value->GetType());
176 if (value->HasObjectId()) {
177 std::cout << "id: " << value->GetObjectId() << std::endl;
178 }
179 if (value->HasSubType()) {
180 std::cout << "sub type: " << value->GetSubType() << std::endl;
181 infos.push_back(value->GetSubType());
182 }
183 if (value->HasClassName()) {
184 std::cout << "class name: " << value->GetClassName() << std::endl;
185 infos.push_back(value->GetClassName());
186 }
187 if (value->HasDescription()) {
188 std::cout << "desc: " << value->GetDescription() << std::endl;
189 infos.push_back(value->GetDescription());
190 }
191 if (value->HasValue()) {
192 std::cout << "type: " <<
193 value->GetValue()->Typeof(vm_)->ToString() << std::endl;
194 std::cout << "tostring: " <<
195 value->GetValue()->ToString(vm_)->ToString() << std::endl;
196 infos.push_back(value->GetValue()->ToString(vm_)->ToString());
197 }
198 }
199
200 /*
201 * Expect map type: map<name, value list>
202 * value list (optional):
203 * type
204 * subType
205 * className
206 * description
207 * value tostring
208 *
209 * if is object value, will push back key and value.
210 *
211 * for example:
212 * var abc = 1
213 * { "abc", { "number", "1", "1" } }
214 * var obj = { "key": "2" }
215 * { "obj0", { "object", "Object", "Object", "[object Object]", "key", "string", "2", "2" } }
216 */
217 const std::map<std::string, std::vector<std::string>> variableMap_ = {
218 { "nop", { "undefined" } },
219 { "foo", { "function", "Function", "function foo( { [js code] }",
220 "Cannot get source code of funtion" } },
221 { "ArrayList", { "function", "Function", "function ArrayList( { [native code] }",
222 "function ArrayList() { [native code] }" } },
223 { "Deque", { "function", "Function", "function Deque( { [native code] }",
224 "function Deque() { [native code] }" } },
225 { "HashMap", { "function", "Function", "function HashMap( { [native code] }",
226 "function HashMap() { [native code] }" } },
227 { "HashSet", { "function", "Function", "function HashSet( { [native code] }",
228 "function HashSet() { [native code] }" } },
229 { "LightWeightMap", { "function", "Function", "function LightWeightMap( { [native code] }",
230 "function LightWeightMap() { [native code] }" } },
231 { "LightWeightSet", { "function", "Function", "function LightWeightSet( { [native code] }",
232 "function LightWeightSet() { [native code] }" } },
233 { "LinkedList", { "function", "Function", "function LinkedList( { [native code] }",
234 "function LinkedList() { [native code] }" } },
235 { "List", { "function", "Function", "function List( { [native code] }",
236 "function List() { [native code] }" } },
237 { "PlainArray", { "function", "Function", "function PlainArray( { [native code] }",
238 "function PlainArray() { [native code] }" } },
239 { "Queue", { "function", "Function", "function Queue( { [native code] }",
240 "function Queue() { [native code] }" } },
241 { "Stack", { "function", "Function", "function Stack( { [native code] }",
242 "function Stack() { [native code] }" } },
243 { "TreeMap", { "function", "Function", "function TreeMap( { [native code] }",
244 "function TreeMap() { [native code] }" } },
245 { "TreeSet", { "function", "Function", "function TreeSet( { [native code] }",
246 "function TreeSet() { [native code] }" } },
247 { "Vector", { "function", "Function", "function Vector( { [native code] }",
248 "function Vector() { [native code] }" } },
249 { "arrayList", { "object", "Object", "ArrayList", "[object ArrayList]", "size", "number", "6", "6",
250 "[[ArrayList]]", "object", "array", "Array", "Array(6)", "8,15,3,10,288,188" } },
251 { "deque", { "object", "Object", "Deque", "[object Deque]", "size", "number", "10", "10", "[[Deque]]",
252 "object", "array", "Array", "Array(10)", "1888,5,3,13,1888,17,888,387,666,177" } },
253 { "hashMap", { "object", "Object", "HashMap", "[object HashMap]", "size", "number", "1", "1",
254 "[[HashMap]]", "object", "array", "Array", "Array(1)", "[object Object]" } },
255 { "hashSet", { "object", "Object", "HashSet", "[object HashSet]", "size", "number", "14",
256 "14", "[[HashSet]]", "object", "array", "Array", "Array(14)",
257 "17,1888,0,99,one,four,433,5,18,112,8,38,537,three" } },
258 { "lightWeightMap", { "object", "Object", "LightWeightMap", "0:0", "size", "number", "1", "1",
259 "[[LightWeightMap]]", "object", "array", "Array", "Array(1)", "[object Object]" } },
260 { "lightWeightSet", { "object", "Object", "LightWeightSet", "4,66,388,566,578,593,855,100,one,four,three",
261 "size", "number", "11", "11", "[[LightWeightSet]]", "object", "array", "Array",
262 "Array(11)", "4,66,388,566,578,593,855,100,one,four,three" } },
263 { "linkedList", { "object", "Object", "LinkedList", "[object Object]", "size", "number",
264 "9", "9", "[[LinkedList]]", "object", "array", "Array", "Array(9)",
265 "588,388,one,88,3989,1888,888,187,two" } },
266 { "list", { "object", "Object", "List", "[object Object]", "size", "number",
267 "12", "12", "[[List]]", "object", "array", "Array", "Array(12)",
268 "18,changge,2,527,0,changge,three,88,changge,18,100,322" } },
269 { "plainArray", { "object", "Object", "PlainArray", "8:8", "size", "number", "1", "1",
270 "[[PlainArray]]", "object", "array", "Array", "Array(1)", "[object Object]" } },
271 { "queue", { "object", "Object", "Queue", "[object Queue]", "size", "number", "8", "8", "[[Queue]]",
272 "object", "array", "Array", "Array(8)", "3,2888,1,555,857,eleven,999,male" } },
273 { "stack", { "object", "Object", "Stack", "[object Stack]", "size", "number", "7", "7", "[[Stack]]",
274 "object", "array", "Array", "Array(7)", "5,18,3,28,1,one,888" } },
275 { "treeMap", { "object", "Object", "TreeMap", "[object TreeMap]", "size", "number", "1", "1",
276 "[[TreeMap]]", "object", "array", "Array", "Array(1)", "[object Object]" } },
277 { "treeSet", { "object", "Object", "TreeSet", "[object TreeSet]", "size", "number",
278 "9", "9", "[[TreeSet]]", "object", "array", "Array", "Array(9)",
279 "5,18,99,377,588,888,1388,11199,array" } },
280 { "vector", { "object", "Object", "Vector", "857,33,change,male,999,change,999,one", "size",
281 "number", "8", "8", "[[Vector]]", "object", "array", "Array", "Array(8)",
282 "857,33,change,male,999,change,999,one" } },
283 { "boolean0", { "boolean", "false", "false" } },
284 { "boolean1", { "boolean", "true", "true" } },
285 { "boolean2", { "boolean", "false", "false" } },
286 { "boolean3", { "boolean", "true", "true" } },
287 { "number0", { "number", "1888", "1888" } },
288 { "number1", { "number", "177", "177" } },
289 { "number2", { "number", "-1", "-1" } },
290 { "number3", { "number", "-1", "-1" } },
291 { "number4", { "number", "222", "222" } },
292 { "number5", { "number", "-2", "-2" } },
293 { "number6", { "number", "566", "566" } },
294 { "number7", { "number", "588", "588" } },
295 { "number8", { "number", "588", "588" } },
296 { "number9", { "number", "0", "0" } },
297 { "number10", { "number", "-1", "-1" } },
298 { "number11", { "number", "88", "88" } },
299 { "number12", { "number", "18", "18" } },
300 { "number13", { "number", "322", "322" } },
301 { "number14", { "number", "8", "8" } },
302 { "number15", { "number", "4", "4" } },
303 { "number16", { "number", "8", "8" } },
304 { "number17", { "number", "5", "5" } },
305 { "number18", { "number", "188", "188" } },
306 { "number19", { "number", "8", "8" } },
307 { "number20", { "number", "388", "388" } },
308 { "number21", { "number", "0", "0" } },
309 { "number22", { "number", "5", "5" } },
310 { "number23", { "number", "18", "18" } },
311 { "number24", { "number", "1388", "1388" } },
312 { "number25", { "number", "6", "6" } },
313 { "number26", { "number", "0", "0" } },
314 { "number27", { "number", "857", "857" } },
315 { "string0", { "string", "two", "two" } },
316 { "string1", { "string", "change", "change" } },
317 { "string2", { "string", "three", "three" } },
318 { "string3", { "string", "array", "array" } },
319 { "string4", { "string", "male", "male" } },
320 { "string5", { "string", "one", "one" } },
321 };
322
323 int32_t index_ {0};
324 const EcmaVM *vm_ {nullptr};
325 RuntimeImpl *runtime_ {nullptr};
326 };
327
328 std::string entryPoint_ = "_GLOBAL::func_main_0";
329 JSPtLocation location_ {nullptr, JSPtLocation::EntityId(0), 0};
330 size_t breakpointCounter_ = 0;
331 };
332
GetJsContainerTest()333 std::unique_ptr<TestEvents> GetJsContainerTest()
334 {
335 return std::make_unique<JsContainerTest>();
336 }
337 } // namespace panda::ecmascript::tooling::test
338
339 #endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_CONTAINER_TEST_H
340