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