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
23using namespace panda::ecmascript;
24using namespace panda::ecmascript::base;
25using LocaleHelper = panda::ecmascript::intl::LocaleHelper;
26
27namespace panda::test {
28class 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 */
37HWTEST_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 */
63HWTEST_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 */
108HWTEST_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 */
139HWTEST_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 */
177HWTEST_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 */
207HWTEST_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 */
233HWTEST_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 */
255HWTEST_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 */
286HWTEST_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