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