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 23using namespace panda::ecmascript; 24using namespace panda::ecmascript::builtins; 25 26namespace panda::test { 27using BuiltinsArray = ecmascript::builtins::BuiltinsArray; 28class BuiltinsCollatorTest : public BaseTestWithScope<true> { 29}; 30 31static 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 47enum 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 56static 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 93HWTEST_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 129static 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 142static 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) 167HWTEST_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) 205HWTEST_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") 245HWTEST_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 273HWTEST_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 301HWTEST_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