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#include "ecmascript/base/json_stringifier.h"
17#include "ecmascript/js_array.h"
18#include "ecmascript/tests/test_helper.h"
19
20using namespace panda::ecmascript;
21using namespace panda::ecmascript::base;
22
23namespace panda::test {
24class JsonStringifierTest : public BaseTestWithScope<false> {
25};
26
27static JSTaggedValue CreateBaseJSObject(JSThread *thread, const CString keyCStr)
28{
29    EcmaVM *ecmaVM = thread->GetEcmaVM();
30    JSHandle<GlobalEnv> globalEnv = ecmaVM->GetGlobalEnv();
31    ObjectFactory *factory = ecmaVM->GetFactory();
32    JSHandle<JSTaggedValue> objectFunc(globalEnv->GetObjectFunction());
33
34    JSHandle<JSObject> jsObject(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
35    EXPECT_TRUE(*jsObject != nullptr);
36
37    JSHandle<JSTaggedValue> handleKey1(factory->NewFromASCII(&keyCStr[0]));
38    JSHandle<JSTaggedValue> handleValue1(thread, JSTaggedValue(1)); // 1 : test case
39    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), handleKey1, handleValue1);
40
41    CString str2 = "x";
42    JSHandle<JSTaggedValue> handleKey2(factory->NewFromASCII(str2));
43    JSHandle<JSTaggedValue> handleValue2(thread, JSTaggedValue(3.6)); // 3.6 : test case
44    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), handleKey2, handleValue2);
45
46    CString str3 = "y";
47    JSHandle<JSTaggedValue> handleKey3(factory->NewFromASCII(str3));
48    JSHandle<JSTaggedValue> handleValue3(factory->NewFromASCII("abc"));
49    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), handleKey3, handleValue3);
50
51    return jsObject.GetTaggedValue();
52}
53
54static JSTaggedValue TestForStringfy1([[maybe_unused]] EcmaRuntimeCallInfo *argv)
55{
56    // false: test case
57    return JSTaggedValue(JSTaggedValue::False());
58}
59
60static JSTaggedValue TestForStringfy2([[maybe_unused]] EcmaRuntimeCallInfo *argv)
61{
62    // 10.12: test case
63    return JSTaggedValue(10.12);
64}
65
66/**
67 * @tc.name: Stringify_001
68 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
69 *           the first parameter of the ECMAObject,the second parameter is JSFunction,the third parameter
70 *           is Undefined.if the second parameter is JSFunction,the return value is the parameter stringification
71 *           after through "call" function.
72 * @tc.type: FUNC
73 * @tc.require:
74 */
75HWTEST_F_L0(JsonStringifierTest, Stringify_001)
76{
77    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
78    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
79
80    JSHandle<JSTaggedValue> handleObj = JSHandle<JSTaggedValue>(thread, CreateBaseJSObject(thread, "z"));
81    JSHandle<JSFunction> handleFunc1 = factory->NewJSFunction(env, reinterpret_cast<void *>(TestForStringfy1));
82    JSHandle<JSFunction> handleFunc2 = factory->NewJSFunction(env, reinterpret_cast<void *>(TestForStringfy2));
83    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
84    JSHandle<JSTaggedValue> handleReplacer1(thread, handleFunc1.GetTaggedValue());
85    JSHandle<JSTaggedValue> handleReplacer2(thread, handleFunc2.GetTaggedValue());
86    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue::Undefined());
87
88    JsonStringifier stringifier1(thread);
89    JSHandle<JSTaggedValue> resultString1 = stringifier1.Stringify(handleValue, handleReplacer1, handleGap);
90    EXPECT_TRUE(resultString1->IsString());
91    JSHandle<EcmaString> handleEcmaStr1(resultString1);
92    EXPECT_STREQ("false", EcmaStringAccessor(handleEcmaStr1).ToCString().c_str());
93
94    JsonStringifier stringifier2(thread);
95    JSHandle<JSTaggedValue> resultString2 = stringifier2.Stringify(handleValue, handleReplacer2, handleGap);
96    EXPECT_TRUE(resultString2->IsString());
97    JSHandle<EcmaString> handleEcmaStr2(resultString2);
98    EXPECT_STREQ("10.12", EcmaStringAccessor(handleEcmaStr2).ToCString().c_str());
99}
100
101/**
102 * @tc.name: Stringify_002
103 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
104 *           the first parameter of the ECMAObject,the second parameter is Undefined,the third parameter
105 *           is Number.This situation will stringize parameters through "SerializeJSONObject" function.
106 * @tc.type: FUNC
107 * @tc.require:
108 */
109HWTEST_F_L0(JsonStringifierTest, Stringify_002)
110{
111    JsonStringifier stringifier(thread);
112
113    JSHandle<JSTaggedValue> handleObj = JSHandle<JSTaggedValue>(thread, CreateBaseJSObject(thread, "z"));
114    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
115    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
116    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue(static_cast<int32_t>(10)));
117
118    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
119    EXPECT_TRUE(resultString->IsString());
120    JSHandle<EcmaString> handleEcmaStr(resultString);
121    EXPECT_STREQ("{\n          \"z\": 1,\n          \"x\": 3.6,\n          \"y\": \"abc\"\n}",
122                                                     EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
123}
124
125/**
126 * @tc.name: Stringify_003
127 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
128 *           the first parameter of the ECMAObject,the second parameter is Undefined,the third parameter
129 *           is String,This situation will stringize parameters through "SerializeJSONObject" function.
130 * @tc.type: FUNC
131 * @tc.require:
132 */
133HWTEST_F_L0(JsonStringifierTest, Stringify_003)
134{
135    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
136    JsonStringifier stringifier(thread);
137
138    JSHandle<JSTaggedValue> handleObj = JSHandle<JSTaggedValue>(thread, CreateBaseJSObject(thread, "z"));
139    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII("tttt"));
140    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
141
142    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
143    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
144    JSHandle<JSTaggedValue> handleGap(thread, handleStr.GetTaggedValue());
145
146    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
147    EXPECT_TRUE(resultString->IsString());
148    JSHandle<EcmaString> resultStr =
149        factory->NewFromASCII("{\ntttt\"z\": 1,\ntttt\"x\": 3.6,\ntttt\"y\": \"abc\"\n}");
150    EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultStr, JSHandle<EcmaString>(resultString)), 0);
151}
152
153/**
154 * @tc.name: Stringify_004
155 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
156 *           the first parameter of the ECMAObject,the second parameter is JSArray,the third parameter
157 *           is String.This situation will stringize parameters through "SerializeJSONObject" function.
158 * @tc.type: FUNC
159 * @tc.require:
160 */
161HWTEST_F_L0(JsonStringifierTest, Stringify_004)
162{
163    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
164    JsonStringifier stringifier(thread);
165
166    JSHandle<JSTaggedValue> handleObj1 = JSHandle<JSTaggedValue>(thread, CreateBaseJSObject(thread, "z"));
167
168    JSArray *handleArr =
169        JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
170    JSHandle<JSObject> handleObj2(thread, handleArr);
171    JSHandle<JSTaggedValue> handleKey0(thread, JSTaggedValue(0));
172    JSHandle<JSTaggedValue> handleValue0(factory->NewFromASCII("z"));
173    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObj2), handleKey0, handleValue0);
174
175    JSHandle<JSTaggedValue> handleKey1(thread, JSTaggedValue(1));
176    JSHandle<JSTaggedValue> handleValue1(factory->NewFromASCII("x"));
177    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObj2), handleKey1, handleValue1);
178
179    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII("tttt"));
180    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
181
182    JSHandle<JSTaggedValue> handleValue(thread, handleObj1.GetTaggedValue());
183    JSHandle<JSTaggedValue> handleReplacer(thread, handleObj2.GetTaggedValue());
184    JSHandle<JSTaggedValue> handleGap(thread, handleStr.GetTaggedValue());
185
186    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
187    EXPECT_TRUE(resultString->IsString());
188    JSHandle<EcmaString> handleEcmaStr(resultString);
189    EXPECT_STREQ("{\ntttt\"z\": 1,\ntttt\"x\": 3.6\n}", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
190}
191
192/**
193 * @tc.name: Stringify_005
194 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
195 *           the first parameter of the ECMAObject,the second parameter is Undefined,the third parameter
196 *           is Undefined.This situation will stringize the first parameter through "SerializeJSONObject" function.
197 * @tc.type: FUNC
198 * @tc.require:
199 */
200HWTEST_F_L0(JsonStringifierTest, Stringify_005)
201{
202    JsonStringifier stringifier(thread);
203    JSHandle<JSTaggedValue> handleObj = JSHandle<JSTaggedValue>(thread, CreateBaseJSObject(thread, "z"));
204
205    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
206    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
207    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue::Undefined());
208
209    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
210    EXPECT_TRUE(resultString->IsString());
211    JSHandle<EcmaString> handleEcmaStr(resultString);
212    EXPECT_STREQ("{\"z\":1,\"x\":3.6,\"y\":\"abc\"}", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
213}
214
215/**
216 * @tc.name: Stringify_006
217 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
218 *           the first parameter of the JSArray,the second parameter is Undefined,the third parameter
219 *           is String,This situation will stringize parameters through "SerializeJSArray" function.
220 * @tc.type: FUNC
221 * @tc.require:
222 */
223HWTEST_F_L0(JsonStringifierTest, Stringify_006)
224{
225    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
226    JsonStringifier stringifier(thread);
227
228    JSArray *handleArr =
229        JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
230    JSHandle<JSObject> handleObj(thread, handleArr);
231
232    JSHandle<JSTaggedValue> handleKey0(thread, JSTaggedValue(0));
233    JSHandle<JSTaggedValue> handleValue0(factory->NewFromASCII("json"));
234    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObj), handleKey0, handleValue0);
235
236    JSHandle<JSTaggedValue> handleKey1(thread, JSTaggedValue(1));
237    PropertyDescriptor handleDesc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(100)), true, true, true);
238    JSArray::DefineOwnProperty(thread, handleObj, handleKey1, handleDesc);
239
240    JSHandle<JSTaggedValue> handleKey2(thread, JSTaggedValue(2));
241    JSHandle<JSTaggedValue> handleValue2(factory->NewFromASCII("abc"));
242    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObj), handleKey2, handleValue2);
243
244    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII("tttt"));
245    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
246
247    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
248    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
249    JSHandle<JSTaggedValue> handleGap(thread, handleStr.GetTaggedValue());
250
251    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
252    EXPECT_TRUE(resultString->IsString());
253    JSHandle<EcmaString> handleEcmaStr(resultString);
254    EXPECT_STREQ("[\ntttt\"json\",\ntttt100,\ntttt\"abc\"\n]", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
255}
256
257/**
258 * @tc.name: Stringify_007
259 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
260 *           the first parameter of the JSObject,the second parameter is Undefined,the third parameter
261 *           is Undefined.This situation will stringize the first parameter through "SerializeJSArray" function.
262 * @tc.type: FUNC
263 * @tc.require:
264 */
265HWTEST_F_L0(JsonStringifierTest, Stringify_007)
266{
267    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
268    JsonStringifier stringifier(thread);
269
270    JSArray *handleArr =
271        JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
272    JSHandle<JSObject> handleObj(thread, handleArr);
273
274    JSHandle<JSTaggedValue> handleKey0(thread, JSTaggedValue(0));
275    PropertyDescriptor handleDesc0(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(1)), true, true, true);
276    JSArray::DefineOwnProperty(thread, handleObj, handleKey0, handleDesc0);
277
278    JSHandle<JSTaggedValue> handleKey1(thread, JSTaggedValue(1));
279    PropertyDescriptor handleDesc1(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(3.6)), true, true, true);
280    JSArray::DefineOwnProperty(thread, handleObj, handleKey1, handleDesc1);
281
282    JSHandle<JSTaggedValue> handleKey2(thread, JSTaggedValue(2));
283    JSHandle<JSTaggedValue> handleValue2(factory->NewFromASCII("abc"));
284    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObj), handleKey2, handleValue2);
285
286    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
287    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
288    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue::Undefined());
289
290    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
291    EXPECT_TRUE(resultString->IsString());
292    JSHandle<EcmaString> handleEcmaStr(resultString);
293    EXPECT_STREQ("[1,3.6,\"abc\"]", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
294}
295
296/**
297 * @tc.name: Stringify_008
298 * @tc.desc: Check whether the result returned through "Stringify" function is within expectations
299 *           the first parameter of the JSObject,the second parameter is Undefined,the third parameter
300 *           is Undefined.This situation will stringize the first parameter through "SerializePrimitiveRef"
301 *           function.
302 * @tc.type: FUNC
303 * @tc.require:
304 */
305HWTEST_F_L0(JsonStringifierTest, Stringify_008)
306{
307    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
308    JsonStringifier stringifier(thread);
309
310    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
311    JSHandle<JSTaggedValue> handleStr(factory->NewFromASCII("\"\\\b\f\n\r\t"));
312    JSHandle<JSPrimitiveRef> handlePrimitiveRef = factory->NewJSString(handleStr, undefined);
313    JSHandle<JSObject> handleObj(thread, handlePrimitiveRef.GetTaggedValue());
314
315    JSHandle<JSTaggedValue> handleValue(thread, handleObj.GetTaggedValue());
316    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
317    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue::Undefined());
318
319    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
320    EXPECT_TRUE(resultString->IsString());
321    JSHandle<EcmaString> handleEcmaStr(resultString);
322    EXPECT_STREQ("\"\\\"\\\\\\b\\f\\n\\r\\t\"", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
323}
324
325static void* Detach(void *param1, void *param2, void *hint, void *detachData)
326{
327    GTEST_LOG_(INFO) << "detach is running";
328    if (param1 == nullptr && param2 == nullptr) {
329        GTEST_LOG_(INFO) << "detach: two params is nullptr";
330    }
331    if (hint == nullptr && detachData) {
332        GTEST_LOG_(INFO) << "detach: hint is nullptr";
333    }
334    return nullptr;
335}
336
337static void* Attach([[maybe_unused]] void *enginePointer, [[maybe_unused]] void *buffer, [[maybe_unused]] void *hint,
338                    [[maybe_unused]] void *attachData)
339{
340    GTEST_LOG_(INFO) << "attach is running";
341    return nullptr;
342}
343
344static panda::JSNApi::NativeBindingInfo* CreateNativeBindingInfo(void* attach, void* detach)
345{
346    GTEST_LOG_(INFO) << "CreateNativeBindingInfo";
347    auto info = panda::JSNApi::NativeBindingInfo::CreateNewInstance();
348    info->attachFunc = attach;
349    info->detachFunc = detach;
350    return info;
351}
352
353HWTEST_F_L0(JsonStringifierTest, Stringify_009)
354{
355    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
356    JsonStringifier stringifier(thread);
357
358    EcmaVM *ecmaVM = thread->GetEcmaVM();
359    JSHandle<GlobalEnv> globalEnv = ecmaVM->GetGlobalEnv();
360    JSHandle<JSTaggedValue> objectFunc(globalEnv->GetObjectFunction());
361    JSHandle<JSObject> jsObject(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
362    EXPECT_TRUE(*jsObject != nullptr);
363
364    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("key1"));
365    auto info = CreateNativeBindingInfo(reinterpret_cast<void*>(Attach), reinterpret_cast<void*>(Detach));
366    JSHandle<JSTaggedValue> value1(factory->NewJSNativePointer(reinterpret_cast<void*>(info)));
367    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), key1, value1);
368
369    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("key2"));
370    JSHandle<JSTaggedValue> value2(factory->NewFromASCII("abc"));
371    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(jsObject), key2, value2);
372
373    JSHandle<JSTaggedValue> handleValue(thread, jsObject.GetTaggedValue());
374    JSHandle<JSTaggedValue> handleReplacer(thread, JSTaggedValue::Undefined());
375    JSHandle<JSTaggedValue> handleGap(thread, JSTaggedValue::Undefined());
376
377    JSHandle<JSTaggedValue> resultString = stringifier.Stringify(handleValue, handleReplacer, handleGap);
378    EXPECT_TRUE(resultString->IsString());
379    JSHandle<EcmaString> handleEcmaStr(resultString);
380    EXPECT_STREQ("{\"key1\":{},\"key2\":\"abc\"}", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
381}
382}  // namespace panda::test
383