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/builtins/builtins_collator.h"
17 
18 #include "ecmascript/builtins/builtins_array.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/js_collator.h"
21 #include "ecmascript/tests/test_helper.h"
22 
23 using namespace panda::ecmascript;
24 using namespace panda::ecmascript::builtins;
25 
26 namespace panda::test {
27 using BuiltinsArray = ecmascript::builtins::BuiltinsArray;
28 class BuiltinsCollatorTest : public BaseTestWithScope<true> {
29 };
30 
CollatorConstructor(JSThread *thread, std::vector<JSTaggedValue>& args, JSHandle<JSFunction>& target)31 static JSTaggedValue CollatorConstructor(JSThread *thread, std::vector<JSTaggedValue>& args,
32     JSHandle<JSFunction>& target)
33 {
34     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*target), 8);
35     ecmaRuntimeCallInfo->SetFunction(target.GetTaggedValue());
36     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
37     for (size_t i = 0; i < args.size(); i++) {
38         ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
39     }
40 
41     auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
42     JSTaggedValue result = BuiltinsCollator::CollatorConstructor(ecmaRuntimeCallInfo);
43     TestHelper::TearDownFrame(thread, prev);
44     return result;
45 }
46 
47 enum class AlgorithmType {
48     COLLATOR_SUPPORTED_LOCALES_OF,
49     COLLATOR_RESOLVED_OPTIONS,
50     COLLATOR_COMPARE,
51     ARRAY_SORT,
52     ARRAY_JOIN,
53     ARRAY_TOSTR,
54 };
55 
CollatorAlgorithm(JSThread *thread, std::vector<JSTaggedValue>& args, int32_t maxArgLen, AlgorithmType type, JSTaggedValue thisValue = JSTaggedValue::Undefined())56 static JSTaggedValue CollatorAlgorithm(JSThread *thread, std::vector<JSTaggedValue>& args, int32_t maxArgLen,
57     AlgorithmType type, JSTaggedValue thisValue = JSTaggedValue::Undefined())
58 {
59     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), maxArgLen);
60     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
61     ecmaRuntimeCallInfo->SetThis(thisValue);
62     for (size_t i = 0; i < args.size(); i++) {
63         ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
64     }
65     auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
66     JSTaggedValue result;
67     switch (type) {
68         case AlgorithmType::COLLATOR_SUPPORTED_LOCALES_OF:
69             result = BuiltinsCollator::SupportedLocalesOf(ecmaRuntimeCallInfo);
70             break;
71         case AlgorithmType::COLLATOR_RESOLVED_OPTIONS:
72             result = BuiltinsCollator::ResolvedOptions(ecmaRuntimeCallInfo);
73             break;
74         case AlgorithmType::COLLATOR_COMPARE:
75             result = BuiltinsCollator::Compare(ecmaRuntimeCallInfo);
76             break;
77         case AlgorithmType::ARRAY_SORT:
78             result = BuiltinsArray::Sort(ecmaRuntimeCallInfo);
79             break;
80         case AlgorithmType::ARRAY_JOIN:
81             result = BuiltinsArray::Join(ecmaRuntimeCallInfo);
82             break;
83         case AlgorithmType::ARRAY_TOSTR:
84             result = BuiltinsArray::ToString(ecmaRuntimeCallInfo);
85             break;
86         default:
87             break;
88     }
89     TestHelper::TearDownFrame(thread, prev);
90     return result;
91 }
92 
HWTEST_F_L0(BuiltinsCollatorTest, CollatorConstructor)93 HWTEST_F_L0(BuiltinsCollatorTest, CollatorConstructor)
94 {
95     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
96     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
97     JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
98     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
99 
100     JSHandle<JSTaggedValue> usageKey = thread->GlobalConstants()->GetHandledUsageString();
101     JSHandle<JSTaggedValue> localeMatcherKey = thread->GlobalConstants()->GetHandledLocaleMatcherString();
102     JSHandle<JSTaggedValue> numericKey = thread->GlobalConstants()->GetHandledNumericString();
103     JSHandle<JSTaggedValue> caseFirstKey = thread->GlobalConstants()->GetHandledCaseFirstString();
104     JSHandle<JSTaggedValue> sensitivityKey = thread->GlobalConstants()->GetHandledSensitivityString();
105     JSHandle<JSTaggedValue> ignorePunctuationKey = thread->GlobalConstants()->GetHandledIgnorePunctuationString();
106 
107     JSHandle<JSTaggedValue> usageValue(factory->NewFromASCII("search"));
108     JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII("lookup"));
109     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("true"));
110     JSHandle<JSTaggedValue> caseFirstValue(factory->NewFromASCII("lower"));
111     JSHandle<JSTaggedValue> sensitivityValue(factory->NewFromASCII("variant"));
112     JSHandle<JSTaggedValue> ignorePunctuationValue(factory->NewFromASCII("true"));
113     JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-Latn-US"));
114 
115     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
116     JSObject::SetProperty(thread, optionsObj, usageKey, usageValue);
117     JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
118     JSObject::SetProperty(thread, optionsObj, caseFirstKey, caseFirstValue);
119     JSObject::SetProperty(thread, optionsObj, sensitivityKey, sensitivityValue);
120     JSObject::SetProperty(thread, optionsObj, ignorePunctuationKey, ignorePunctuationValue);
121     JSObject::SetProperty(thread, optionsObj, numericKey, numericValue);
122 
123     std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), optionsObj.GetTaggedValue()};
124     auto result = CollatorConstructor(thread, vals, newTarget);
125 
126     EXPECT_TRUE(result.IsJSCollator());
127 }
128 
JSCollatorCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)129 static JSTaggedValue JSCollatorCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)
130 {
131     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
132     JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
133 
134     JSHandle<JSTaggedValue> localesString = locale;
135     std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), JSTaggedValue::Undefined()};
136     auto result = CollatorConstructor(thread, vals, newTarget);
137 
138     EXPECT_TRUE(result.IsJSCollator());
139     return result;
140 }
141 
JSCollatorCreateWithLocaleAndOptionsTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)142 static JSTaggedValue JSCollatorCreateWithLocaleAndOptionsTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)
143 {
144     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
145     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
146     JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
147     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
148 
149     JSHandle<JSTaggedValue> localesString = locale;
150     JSHandle<JSTaggedValue> usageKey = thread->GlobalConstants()->GetHandledUsageString();
151     JSHandle<JSTaggedValue> sensitivityKey = thread->GlobalConstants()->GetHandledSensitivityString();
152     JSHandle<JSTaggedValue> usageValue(factory->NewFromASCII("search"));
153     JSHandle<JSTaggedValue> sensitivityValue(factory->NewFromASCII("base"));
154 
155     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
156     JSObject::SetProperty(thread, optionsObj, usageKey, usageValue);
157     JSObject::SetProperty(thread, optionsObj, sensitivityKey, sensitivityValue);
158 
159     std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), optionsObj.GetTaggedValue()};
160     auto result = CollatorConstructor(thread, vals, newTarget);
161 
162     EXPECT_TRUE(result.IsJSCollator());
163     return result;
164 }
165 
166 // compare with sort(de)
HWTEST_F_L0(BuiltinsCollatorTest, Compare_001)167 HWTEST_F_L0(BuiltinsCollatorTest, Compare_001)
168 {
169     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
170     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de"));
171     JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
172 
173     std::vector<JSTaggedValue> vals{};
174     auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
175     JSHandle<JSFunction> jsFunction(thread, result1);
176 
177     JSArray *jsArray =
178         JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
179     JSHandle<JSObject> jsObject(thread, jsArray);
180 
181     JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
182     JSHandle<JSTaggedValue> value0(factory->NewFromASCII("Z"));
183     PropertyDescriptor desc0(thread, value0, true, true, true);
184     JSArray::DefineOwnProperty(thread, jsObject, key0, desc0);
185     JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
186     JSHandle<JSTaggedValue> value1(factory->NewFromASCII("a"));
187     PropertyDescriptor desc1(thread, value1, true, true, true);
188     JSArray::DefineOwnProperty(thread, jsObject, key1, desc1);
189     JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
190     JSHandle<JSTaggedValue> value2(factory->NewFromUtf8("ä"));
191     PropertyDescriptor desc2(thread, value2, true, true, true);
192     JSArray::DefineOwnProperty(thread, jsObject, key2, desc2);
193 
194     std::vector<JSTaggedValue> arrayVals{jsFunction.GetTaggedValue()};
195     auto result2 = CollatorAlgorithm(thread, arrayVals, 6, AlgorithmType::ARRAY_SORT, jsObject.GetTaggedValue());
196 
197     JSHandle<JSTaggedValue> resultArr =
198         JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result2.GetRawData())));
199     EXPECT_EQ(JSTaggedValue::SameValue(JSArray::GetProperty(thread, resultArr, key0).GetValue(), value1), true);
200     EXPECT_EQ(JSTaggedValue::SameValue(JSArray::GetProperty(thread, resultArr, key1).GetValue(), value2), true);
201     EXPECT_EQ(JSTaggedValue::SameValue(JSArray::GetProperty(thread, resultArr, key2).GetValue(), value0), true);
202 }
203 
204 // // compare with sort(sv)
HWTEST_F_L0(BuiltinsCollatorTest, Compare_002)205 HWTEST_F_L0(BuiltinsCollatorTest, Compare_002)
206 {
207     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
208     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("sv"));
209     JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
210 
211     std::vector<JSTaggedValue> vals{};
212     auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
213 
214     JSHandle<JSFunction> jsFunction(thread, result1);
215     JSArray *jsArray =
216         JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
217     JSHandle<JSObject> jsObject(thread, jsArray);
218 
219     JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
220     JSHandle<JSTaggedValue> value0(factory->NewFromASCII("Z"));
221     PropertyDescriptor desc0(thread, value0, true, true, true);
222     JSArray::DefineOwnProperty(thread, jsObject, key0, desc0);
223     JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
224     JSHandle<JSTaggedValue> value1(factory->NewFromASCII("a"));
225     PropertyDescriptor desc1(thread, value1, true, true, true);
226     JSArray::DefineOwnProperty(thread, jsObject, key1, desc1);
227     JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
228     JSHandle<JSTaggedValue> value2(factory->NewFromUtf8("ä"));
229     PropertyDescriptor desc2(thread, value2, true, true, true);
230     JSArray::DefineOwnProperty(thread, jsObject, key2, desc2);
231 
232     std::vector<JSTaggedValue> arrayVals{jsFunction.GetTaggedValue()};
233     auto result2 = CollatorAlgorithm(thread, arrayVals, 6, AlgorithmType::ARRAY_SORT, jsObject.GetTaggedValue());
234     JSHandle<JSObject> resultObj(thread, result2);
235 
236     JSHandle<EcmaString> str = thread->GetEcmaVM()->GetFactory()->NewFromUtf8("a,Z,ä");
237     std::vector<JSTaggedValue> arrayVals2{};
238     auto result = CollatorAlgorithm(thread, arrayVals2, 4, AlgorithmType::ARRAY_JOIN, resultObj.GetTaggedValue());
239 
240     JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
241     EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultHandle, str), 0);
242 }
243 
244 // compare with options("search")
HWTEST_F_L0(BuiltinsCollatorTest, Compare_003)245 HWTEST_F_L0(BuiltinsCollatorTest, Compare_003)
246 {
247     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
248     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("sv"));
249     JSHandle<JSCollator> jsCollator =
250         JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleAndOptionsTest(thread, locale));
251 
252     std::vector<JSTaggedValue> vals{};
253     auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
254 
255     JSHandle<JSFunction> jsFunction(thread, result1);
256     JSArray *jsArray =
257         JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
258     JSHandle<JSObject> jsObject(thread, jsArray);
259 
260     JSHandle<JSTaggedValue> value0(factory->NewFromUtf8("Congrès"));
261     JSHandle<JSTaggedValue> value1(factory->NewFromASCII("congres"));
262     PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(jsFunction), true, true, true);
263     JSHandle<JSTaggedValue> joinKey(factory->NewFromASCII("join"));
264     JSArray::DefineOwnProperty(thread, jsObject, joinKey, desc);
265 
266     std::vector<JSTaggedValue> arrayVals{value0.GetTaggedValue(), value1.GetTaggedValue()};
267     auto result2 = CollatorAlgorithm(thread, arrayVals, 8, AlgorithmType::ARRAY_TOSTR, jsObject.GetTaggedValue());
268 
269     JSHandle<JSTaggedValue> resultHandle(thread, result2);
270     EXPECT_EQ(resultHandle->GetInt(), 0); // Congrès and congres is matching
271 }
272 
HWTEST_F_L0(BuiltinsCollatorTest, ResolvedOptions)273 HWTEST_F_L0(BuiltinsCollatorTest, ResolvedOptions)
274 {
275     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
276     auto globalConst = thread->GlobalConstants();
277     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de"));
278     JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
279 
280     std::vector<JSTaggedValue> vals{};
281     auto result =
282         CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_RESOLVED_OPTIONS, jsCollator.GetTaggedValue());
283 
284     JSHandle<JSTaggedValue> resultObj =
285         JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
286     // judge whether the properties of the object are the same as those of jscollator tag
287     JSHandle<JSTaggedValue> localeKey = globalConst->GetHandledLocaleString();
288     EXPECT_EQ(JSTaggedValue::SameValue(JSObject::GetProperty(thread, resultObj, localeKey).GetValue(), locale), true);
289     JSHandle<JSTaggedValue> usageKey = globalConst->GetHandledUsageString();
290     JSHandle<JSTaggedValue> defaultUsageValue(factory->NewFromASCII("sort"));
291     EXPECT_EQ(
292         JSTaggedValue::SameValue(JSObject::GetProperty(thread, resultObj, usageKey).GetValue(), defaultUsageValue),
293         true);
294     JSHandle<JSTaggedValue> handledCaseFirstKey = globalConst->GetHandledCaseFirstString();
295     JSHandle<JSTaggedValue> handledCaseFirstValue(factory->NewFromASCII("upper"));
296     EXPECT_EQ(JSTaggedValue::SameValue(JSObject::GetProperty(thread, resultObj, handledCaseFirstKey).GetValue(),
297                                        handledCaseFirstValue),
298               true);
299 }
300 
HWTEST_F_L0(BuiltinsCollatorTest, SupportedLocalesOf)301 HWTEST_F_L0(BuiltinsCollatorTest, SupportedLocalesOf)
302 {
303     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
304     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
305     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
306 
307     JSHandle<JSTaggedValue> localeMatcherKey = thread->GlobalConstants()->GetHandledLocaleMatcherString();
308     JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII("lookup"));
309     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
310     JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
311     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("id-u-co-pinyin-de-ID"));
312 
313     std::vector<JSTaggedValue> vals{locale.GetTaggedValue(), optionsObj.GetTaggedValue()};
314     auto resultArr = CollatorAlgorithm(thread, vals, 8, AlgorithmType::COLLATOR_SUPPORTED_LOCALES_OF);
315 
316     JSHandle<JSArray> resultHandle(thread, resultArr);
317     JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
318     EXPECT_EQ(elements->GetLength(), 1U);
319     JSHandle<EcmaString> handleEcmaStr(thread, elements->Get(0));
320     EXPECT_STREQ("id-u-co-pinyin-de-id", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
321 }
322 }  // namespace panda::test
323