1 /*
2 * Copyright (c) 2022-2024 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/intl/locale_helper.h"
17 #include "ecmascript/ecma_string.h"
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/js_collator.h"
20 #include "ecmascript/js_object-inl.h"
21 #include "ecmascript/tests/test_helper.h"
22
23 using namespace panda::ecmascript;
24 using namespace panda::ecmascript::base;
25 using LocaleHelper = panda::ecmascript::intl::LocaleHelper;
26
27 namespace panda::test {
28 class LocaleHelperTest : public BaseTestWithScope<true> {
29 };
30
31 /**
32 * @tc.name: UStringToString
33 * @tc.desc: Call "UStringToString" function Convert UnicodeString to string(Utf16).
34 * @tc.type: FUNC
35 * @tc.require:
36 */
HWTEST_F_L0(LocaleHelperTest, UStringToString)37 HWTEST_F_L0(LocaleHelperTest, UStringToString)
38 {
39 icu::UnicodeString unicodeString1("GMT-12:00"); // times
40 JSHandle<EcmaString> ecmaString = LocaleHelper::UStringToString(thread, unicodeString1);
41 EXPECT_STREQ("GMT-12:00", EcmaStringAccessor(ecmaString).ToCString().c_str());
42
43 icu::UnicodeString unicodeString2("周日16:00:00–周五23:00:00"); // date
44 ecmaString = LocaleHelper::UStringToString(thread, unicodeString2);
45 EXPECT_STREQ("周日16:00:00–周五23:00:00", EcmaStringAccessor(ecmaString).ToCString().c_str());
46
47 icu::UnicodeString unicodeString3("$654K"); // money
48 ecmaString = LocaleHelper::UStringToString(thread, unicodeString3);
49 EXPECT_STREQ("$654K", EcmaStringAccessor(ecmaString).ToCString().c_str());
50
51 icu::UnicodeString unicodeString4("1 minute ago"); // moment
52 ecmaString = LocaleHelper::UStringToString(thread, unicodeString4, 0, 2);
53 EXPECT_STREQ("1 ", EcmaStringAccessor(ecmaString).ToCString().c_str());
54 }
55
56 /**
57 * @tc.name: CanonicalizeLocaleList
58 * @tc.desc: Create a list of locales and canonicalize the locales in the list through "CanonicalizeUnicodeLocaleId"
59 * function.
60 * @tc.type: FUNC
61 * @tc.require:
62 */
HWTEST_F_L0(LocaleHelperTest, CanonicalizeLocaleList)63 HWTEST_F_L0(LocaleHelperTest, CanonicalizeLocaleList)
64 {
65 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
66 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
67 JSHandle<JSTaggedValue> ctor = env->GetLocaleFunction();
68 JSHandle<JSLocale> jsLocale =
69 JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
70 // Set IcuLocale
71 icu::Locale icuLocale("fr", "Latn", "Fr");
72 factory->NewJSIntlIcuData(jsLocale, icuLocale, JSLocale::FreeIcuLocale);
73 // test locale is jslocale
74 JSHandle<TaggedArray> localeArr = LocaleHelper::CanonicalizeLocaleList(thread, JSHandle<JSTaggedValue>(jsLocale));
75 EXPECT_EQ(localeArr->GetLength(), 1U);
76 JSHandle<EcmaString> handleEcmaStr(thread, localeArr->Get(0));
77 EXPECT_STREQ("fr-Latn-FR", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
78 // test locale is object
79 JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject<JSArray>();
80 JSHandle<JSTaggedValue> localeObj(thread, arr);
81
82 JSHandle<JSTaggedValue> localeStr1(factory->NewFromASCII("EN-us"));
83 PropertyDescriptor desc1(thread, localeStr1, true, true, true);
84 JSHandle<JSTaggedValue> key1(factory->NewFromASCII("1"));
85 JSArray::DefineOwnProperty(thread, JSHandle<JSObject>(localeObj), key1, desc1);
86
87 JSHandle<JSTaggedValue> localeStr2(factory->NewFromASCII("en-GB"));
88 PropertyDescriptor desc2(thread, localeStr2, true, true, true);
89 JSHandle<JSTaggedValue> key2(factory->NewFromASCII("2"));
90 JSArray::DefineOwnProperty(thread, JSHandle<JSObject>(localeObj), key2, desc2);
91 // check canonicalized string
92 localeArr = LocaleHelper::CanonicalizeLocaleList(thread, localeObj);
93 EXPECT_EQ(localeArr->GetLength(), 2U);
94 JSHandle<EcmaString> resultEcmaStr1(thread, localeArr->Get(0));
95 EXPECT_STREQ("en-US", EcmaStringAccessor(resultEcmaStr1).ToCString().c_str());
96 JSHandle<EcmaString> resultEcmaStr2(thread, localeArr->Get(1));
97 EXPECT_STREQ("en-GB", EcmaStringAccessor(resultEcmaStr2).ToCString().c_str());
98 }
99
100 /**
101 * @tc.name: CanonicalizeUnicodeLocaleId
102 * @tc.desc: Call "CanonicalizeUnicodeLocaleId" function canonicalize locale(Language-Tag),The English case of language,
103 * region and script is fixed.the language is lowercase.the beginning of region is uppercase, and the script
104 * is lowercase.if locale string is IsUtf16,return empty string.
105 * @tc.type: FUNC
106 * @tc.require:
107 */
HWTEST_F_L0(LocaleHelperTest, CanonicalizeUnicodeLocaleId)108 HWTEST_F_L0(LocaleHelperTest, CanonicalizeUnicodeLocaleId)
109 {
110 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
111
112 JSHandle<EcmaString> locale = factory-> NewFromStdString("en-Us");
113 JSHandle<EcmaString> canonicalizeLocaleId = LocaleHelper::CanonicalizeUnicodeLocaleId(thread, locale);
114 EXPECT_STREQ("en-US", EcmaStringAccessor(canonicalizeLocaleId).ToCString().c_str());
115
116 locale = factory-> NewFromStdString("kO-kore-kr");
117 canonicalizeLocaleId = LocaleHelper::CanonicalizeUnicodeLocaleId(thread, locale);
118 EXPECT_STREQ("ko-Kore-KR", EcmaStringAccessor(canonicalizeLocaleId).ToCString().c_str());
119
120 locale = factory-> NewFromStdString("id-u-co-pinyin-de-ID");
121 canonicalizeLocaleId = LocaleHelper::CanonicalizeUnicodeLocaleId(thread, locale);
122 EXPECT_STREQ("id-u-co-pinyin-de-id", EcmaStringAccessor(canonicalizeLocaleId).ToCString().c_str());
123 // invalid locale
124 uint16_t localeArr[] = {0x122, 0x104, 0x45, 0x72, 0x97, 0x110, 0x115, 0x45, 0x67, 0x78}; // zh-Hans-CN
125 uint32_t localeArrLength = sizeof(localeArr) / sizeof(localeArr[0]);
126 locale = factory->NewFromUtf16(localeArr, localeArrLength);
127
128 canonicalizeLocaleId = LocaleHelper::CanonicalizeUnicodeLocaleId(thread, locale);
129 JSHandle<EcmaString> emptyString = factory->GetEmptyString();
130 EXPECT_EQ(EcmaStringAccessor::Compare(instance, canonicalizeLocaleId, emptyString), 0);
131 }
132
133 /**
134 * @tc.name: ToLanguageTag
135 * @tc.desc: call "ToLanguageTag" function Convert ICU Locale into language tag.
136 * @tc.type: FUNC
137 * @tc.require:
138 */
HWTEST_F_L0(LocaleHelperTest, ToLanguageTag)139 HWTEST_F_L0(LocaleHelperTest, ToLanguageTag)
140 {
141 icu::Locale icuLocale1("en", "Latn", "US", "collation=phonebk;currency=euro");
142 JSHandle<EcmaString> languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale1);
143 EXPECT_STREQ("en-Latn-US-u-co-phonebk-cu-euro", EcmaStringAccessor(languageTag).ToCString().c_str());
144
145 icu::Locale icuLocale2("zh", "Hans", "CN", "collation=phonebk;kn=true");
146 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale2);
147 EXPECT_STREQ("zh-Hans-CN-u-co-phonebk-kn", EcmaStringAccessor(languageTag).ToCString().c_str());
148
149 icu::Locale icuLocale3("ja", "Jpan", "JP", "collation=phonebk;co=yes");
150 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale3);
151 EXPECT_STREQ("ja-Jpan-JP-u-co", EcmaStringAccessor(languageTag).ToCString().c_str());
152
153 icu::Locale icuLocale4("z", "CN"); // language is fault
154 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale4);
155 EXPECT_STREQ("und-CN", EcmaStringAccessor(languageTag).ToCString().c_str());
156
157 icu::Locale icuLocale5("zh", "c"); // script is fault
158 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale5);
159 EXPECT_STREQ("zh-x-lvariant-c", EcmaStringAccessor(languageTag).ToCString().c_str());
160
161 icu::Locale icuLocale6("en", "Latn", "E"); // region is fault
162 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale6);
163 EXPECT_STREQ("en-Latn-x-lvariant-e", EcmaStringAccessor(languageTag).ToCString().c_str());
164
165 icu::Locale icuLocale7("en", "Latn", "EG", "kf=yes"); // key value is fault
166 languageTag = LocaleHelper::ToLanguageTag(thread, icuLocale7);
167 EXPECT_STREQ("en-Latn-EG-u-kf", EcmaStringAccessor(languageTag).ToCString().c_str());
168 }
169
170 /**
171 * @tc.name: IsStructurallyValidLanguageTag
172 * @tc.desc: Call "IsStructurallyValidLanguageTag" function check Language-Tag is valid structurally.If the tag contains
173 * the correct language, region, script and extension, return true otherwise, return false.
174 * @tc.type: FUNC
175 * @tc.require:
176 */
HWTEST_F_L0(LocaleHelperTest, IsStructurallyValidLanguageTag)177 HWTEST_F_L0(LocaleHelperTest, IsStructurallyValidLanguageTag)
178 {
179 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
180 // number-language
181 JSHandle<EcmaString> handleEcmaStr = factory->NewFromStdString("123-de");
182 EXPECT_FALSE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
183 // only language(zh)
184 handleEcmaStr = factory-> NewFromStdString("zh");
185 EXPECT_TRUE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
186 // only language and script, region
187 handleEcmaStr = factory-> NewFromStdString("zh-Hans-Cn");
188 EXPECT_TRUE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
189
190 handleEcmaStr = factory-> NewFromStdString("ja-JP-u-ca-japanese");
191 EXPECT_TRUE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
192
193 handleEcmaStr = factory-> NewFromStdString("语言脚本地区");
194 EXPECT_FALSE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
195
196 handleEcmaStr = factory-> NewFromStdString("e-US");
197 EXPECT_FALSE(LocaleHelper::IsStructurallyValidLanguageTag(handleEcmaStr));
198 }
199
200 /**
201 * @tc.name: ConvertToStdString
202 * @tc.desc: Convert char* type to std string,check whether the returned string through "ConvertToStdString"
203 * function is within expectations.
204 * @tc.type: FUNC
205 * @tc.require:
206 */
HWTEST_F_L0(LocaleHelperTest, ConvertToStdString)207 HWTEST_F_L0(LocaleHelperTest, ConvertToStdString)
208 {
209 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
210 JSHandle<EcmaString> handleEcmaStr = factory-> NewFromStdString("一二三四");
211 std::string stdString = LocaleHelper::ConvertToStdString(handleEcmaStr);
212 EXPECT_STREQ(stdString.c_str(), "一二三四");
213
214 handleEcmaStr = factory-> NewFromStdString("#%!\0@$");
215 stdString = LocaleHelper::ConvertToStdString(handleEcmaStr);
216 EXPECT_STREQ(stdString.c_str(), "#%!\0@$");
217
218 handleEcmaStr = factory-> NewFromStdString("123456");
219 stdString = LocaleHelper::ConvertToStdString(handleEcmaStr);
220 EXPECT_STREQ(stdString.c_str(), "123456");
221
222 handleEcmaStr = factory-> NewFromStdString("zhde");
223 stdString = LocaleHelper::ConvertToStdString(handleEcmaStr);
224 EXPECT_STREQ(stdString.c_str(), "zhde");
225 }
226
227 /**
228 * @tc.name: HandleLocaleExtension
229 * @tc.desc: Find position of subtag "x" or "u" in Locale through "HandleLocaleExtension" function.
230 * @tc.type: FUNC
231 * @tc.require:
232 */
HWTEST_F_L0(LocaleHelperTest, HandleLocaleExtension)233 HWTEST_F_L0(LocaleHelperTest, HandleLocaleExtension)
234 {
235 std::string result = "en-Latn-US-u-ca-gregory-co-compat";
236 size_t start = 0;
237 size_t extensionEnd = 0;
238 LocaleHelper::HandleLocaleExtension(start, extensionEnd, result, result.size());
239 EXPECT_EQ(extensionEnd, 10U); // the position of "u"
240 // private extension("x")
241 result = "de-zh-x-co-phonebk-nu-kali";
242 start = 0;
243 extensionEnd = 0;
244 LocaleHelper::HandleLocaleExtension(start, extensionEnd, result, result.size());
245 EXPECT_EQ(extensionEnd, 5U); // the position of "x"
246 }
247
248 /**
249 * @tc.name: HandleLocale
250 * @tc.desc: Call "HandleLocale" function handle locale,if Locale has subtag "u" ignore it.If Locale has
251 * both subtag "x" and "u","x" is in front of "u","u" does not ignore,"x" is after "u","u" ignores.
252 * @tc.type: FUNC
253 * @tc.require:
254 */
HWTEST_F_L0(LocaleHelperTest, HandleLocale)255 HWTEST_F_L0(LocaleHelperTest, HandleLocale)
256 {
257 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
258 // no "u" or "x"
259 JSHandle<EcmaString> localeString = factory->NewFromASCII("en-Latn-US");
260 LocaleHelper::ParsedLocale parsedResult = LocaleHelper::HandleLocale(localeString);
261 EXPECT_STREQ(parsedResult.base.c_str(), "en-Latn-US");
262 // only "x"
263 localeString = factory->NewFromASCII("zh-CN-x-ca-pinyin");
264 parsedResult = LocaleHelper::HandleLocale(localeString);
265 EXPECT_STREQ(parsedResult.base.c_str(), "zh-CN-x-ca-pinyin");
266 // only "u"
267 localeString = factory->NewFromASCII("ko-Kore-KR-u-co-phonebk");
268 parsedResult = LocaleHelper::HandleLocale(localeString);
269 EXPECT_STREQ(parsedResult.base.c_str(), "ko-Kore-KR");
270 // both "x" and "u"
271 localeString = factory->NewFromASCII("en-Latn-US-u-x-co-phonebk-kn-true");
272 parsedResult = LocaleHelper::HandleLocale(localeString);
273 EXPECT_STREQ(parsedResult.base.c_str(), "en-Latn-US-x-co-phonebk-kn-true");
274
275 localeString = factory->NewFromASCII("en-Latn-US-x-u-ca-pinyin-co-compat");
276 parsedResult = LocaleHelper::HandleLocale(localeString);
277 EXPECT_STREQ(parsedResult.base.c_str(), "en-Latn-US-x-u-ca-pinyin-co-compat");
278 }
279
280 /**
281 * @tc.name: BestAvailableLocale
282 * @tc.desc: Match the best Locale and return from available locale through "BestAvailableLocale" function.
283 * @tc.type: FUNC
284 * @tc.require:
285 */
HWTEST_F_L0(LocaleHelperTest, BestAvailableLocale)286 HWTEST_F_L0(LocaleHelperTest, BestAvailableLocale)
287 {
288 const char *path = JSCollator::uIcuDataColl.c_str();
289 // available locales in uIcuDataColl
290 std::vector<std::string> icuDataAvailableLocales =
291 LocaleHelper::GetAvailableLocales(thread, nullptr, path);
292 // available locales(calendar)
293 std::vector<std::string> calendarAvailableLocales =
294 LocaleHelper::GetAvailableLocales(thread, "calendar", nullptr);
295 // available locales(NumberElements)
296 std::vector<std::string> numberAvailableLocales =
297 LocaleHelper::GetAvailableLocales(thread, "NumberElements", nullptr);
298 // "ar-001" is found
299 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(icuDataAvailableLocales, "ar-001").c_str(), "ar-001");
300 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(calendarAvailableLocales, "ar-001").c_str(), "ar-001");
301 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(numberAvailableLocales, "ar-001").c_str(), "ar-001");
302 // "agq-CM" is not found in uIcuDataColl
303 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(icuDataAvailableLocales, "agq-CM").c_str(), "");
304 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(calendarAvailableLocales, "agq-CM").c_str(), "agq-CM");
305 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(numberAvailableLocales, "agq-CM").c_str(), "agq-CM");
306 // language(und)-region(CN)
307 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(icuDataAvailableLocales, "und-CN").c_str(), "");
308 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(calendarAvailableLocales, "und-CN").c_str(), "");
309 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(numberAvailableLocales, "und-CN").c_str(), "");
310 // language(en)-region(001)
311 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(icuDataAvailableLocales, "en-001").c_str(), "en-001");
312 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(calendarAvailableLocales, "en-001").c_str(), "en-001");
313 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(numberAvailableLocales, "en-001").c_str(), "en-001");
314 // language(en)-script(Hans)-region(US)
315 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(icuDataAvailableLocales, "en-Hans-US").c_str(), "en");
316 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(calendarAvailableLocales, "en-Hans-US").c_str(), "en");
317 EXPECT_STREQ(LocaleHelper::BestAvailableLocale(numberAvailableLocales, "en-Hans-US").c_str(), "en");
318 }
319 } // namespace panda::test