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