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/global_env.h"
18 #include "ecmascript/js_array.h"
19 #include "ecmascript/js_list_format.h"
20 #include "ecmascript/js_iterator.h"
21 #include "ecmascript/object_factory-inl.h"
22 #include "ecmascript/tests/test_helper.h"
23
24 using namespace panda::ecmascript;
25
26 namespace panda::test {
27 class JSListFormatTest : 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/icudt67l.dat 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
HWTEST_F_L0(JSListFormatTest,Set_Get_IcuListFormatter_001)64 HWTEST_F_L0(JSListFormatTest, Set_Get_IcuListFormatter_001)
65 {
66 auto vm = thread->GetEcmaVM();
67 auto factory = vm->GetFactory();
68 auto env = vm->GetGlobalEnv();
69 JSHandle<JSTaggedValue> ctor = env->GetListFormatFunction();
70 JSHandle<JSListFormat> jsFormatter =
71 JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
72 UErrorCode status = UErrorCode::U_ZERO_ERROR;
73 icu::Locale icuLocale("en", "Latn", "US");
74 icu::ListFormatter* icuFormatter = icu::ListFormatter::createInstance(icuLocale, status);
75 JSListFormat::SetIcuListFormatter(thread, jsFormatter, icuFormatter, JSListFormat::FreeIcuListFormatter);
76 icu::ListFormatter *resFormatter = jsFormatter->GetIcuListFormatter();
77 EXPECT_TRUE(resFormatter != nullptr);
78
79 const int32_t itemNum = 3;
80 const icu::UnicodeString items[itemNum] = { "One", "Two", "Three" };
81 icu::UnicodeString resStr = "";
82 resStr = resFormatter->format(items, itemNum, resStr, status);
83 const icu::UnicodeString expectResStr("One, Two, and Three");
84 EXPECT_TRUE(resStr.compare(expectResStr) == 0);
85 }
86
HWTEST_F_L0(JSListFormatTest,Set_Get_IcuListFormatter_002)87 HWTEST_F_L0(JSListFormatTest, Set_Get_IcuListFormatter_002)
88 {
89 auto vm = thread->GetEcmaVM();
90 auto factory = vm->GetFactory();
91 auto env = vm->GetGlobalEnv();
92 JSHandle<JSTaggedValue> ctor = env->GetListFormatFunction();
93 JSHandle<JSListFormat> jsFormatter =
94 JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
95 UErrorCode status = UErrorCode::U_ZERO_ERROR;
96 icu::Locale icuLocale("zh", "Hans", "Cn");
97 icu::ListFormatter* icuFormatter = icu::ListFormatter::createInstance(icuLocale, status);
98 JSListFormat::SetIcuListFormatter(thread, jsFormatter, icuFormatter, JSListFormat::FreeIcuListFormatter);
99 icu::ListFormatter *resFormatter = jsFormatter->GetIcuListFormatter();
100 EXPECT_TRUE(resFormatter != nullptr);
101
102 const int32_t itemNum = 3;
103 const icu::UnicodeString items[itemNum] = { "一", "二", "三" };
104 icu::UnicodeString resStr = "";
105 resStr = resFormatter->format(items, itemNum, resStr, status);
106 const icu::UnicodeString expectResStr("一、二和三");
107 EXPECT_TRUE(resStr.compare(expectResStr) == 0);
108 }
109
CreateJSListFormatterTest(JSThread * thread,icu::Locale icuLocale,JSHandle<JSObject> options)110 JSHandle<JSListFormat> CreateJSListFormatterTest(JSThread *thread, icu::Locale icuLocale, JSHandle<JSObject> options)
111 {
112 auto vm = thread->GetEcmaVM();
113 auto factory = vm->GetFactory();
114 auto env = vm->GetGlobalEnv();
115
116 JSHandle<JSTaggedValue> localeCtor = env->GetLocaleFunction();
117 JSHandle<JSTaggedValue> listCtor = env->GetListFormatFunction();
118 JSHandle<JSLocale> locales =
119 JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(localeCtor), localeCtor));
120 JSHandle<JSListFormat> listFormatter =
121 JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(listCtor), listCtor));
122
123 JSHandle<JSTaggedValue> optionsVal = JSHandle<JSTaggedValue>::Cast(options);
124 factory->NewJSIntlIcuData(locales, icuLocale, JSLocale::FreeIcuLocale);
125 listFormatter = JSListFormat::InitializeListFormat(thread, listFormatter,
126 JSHandle<JSTaggedValue>::Cast(locales), optionsVal);
127 return listFormatter;
128 }
129
SetFormatterOptionsTest(JSThread * thread,JSHandle<JSObject> & optionsObj,std::map<std::string,std::string> & options)130 void SetFormatterOptionsTest(JSThread *thread, JSHandle<JSObject> &optionsObj,
131 std::map<std::string, std::string> &options)
132 {
133 auto vm = thread->GetEcmaVM();
134 auto factory = vm->GetFactory();
135 auto globalConst = thread->GlobalConstants();
136 JSHandle<JSTaggedValue> localeMatcherKey = globalConst->GetHandledLocaleMatcherString();
137 JSHandle<JSTaggedValue> typeKey = globalConst->GetHandledTypeString();
138 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
139 JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII(options["localeMatcher"].c_str()));
140 JSHandle<JSTaggedValue> typeValue(factory->NewFromASCII(options["type"].c_str()));
141 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII(options["style"].c_str()));
142 JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
143 JSObject::SetProperty(thread, optionsObj, typeKey, typeValue);
144 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
145 }
146
HWTEST_F_L0(JSListFormatTest,InitializeListFormat)147 HWTEST_F_L0(JSListFormatTest, InitializeListFormat)
148 {
149 auto vm = thread->GetEcmaVM();
150 auto factory = vm->GetFactory();
151 auto env = vm->GetGlobalEnv();
152 icu::Locale icuLocale("en", "Latn", "US");
153 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
154 JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
155 std::map<std::string, std::string> options {
156 { "localeMatcher", "best fit" },
157 { "type", "conjunction" },
158 { "style", "long" }
159 };
160 SetFormatterOptionsTest(thread, object, options);
161 JSHandle<JSTaggedValue> localeCtor = env->GetLocaleFunction();
162 JSHandle<JSTaggedValue> listCtor = env->GetListFormatFunction();
163 JSHandle<JSLocale> locales =
164 JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(localeCtor), localeCtor));
165 JSHandle<JSListFormat> listFormatter =
166 JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(listCtor), listCtor));
167
168 JSHandle<JSTaggedValue> optionsVal = JSHandle<JSTaggedValue>::Cast(object);
169 factory->NewJSIntlIcuData(locales, icuLocale, JSLocale::FreeIcuLocale);
170 listFormatter = JSListFormat::InitializeListFormat(thread, listFormatter,
171 JSHandle<JSTaggedValue>::Cast(locales), optionsVal);
172 icu::ListFormatter *resFormatter = listFormatter->GetIcuListFormatter();
173 EXPECT_TRUE(resFormatter != nullptr);
174
175 const int32_t itemNum = 3;
176 UErrorCode status = UErrorCode::U_ZERO_ERROR;
177 const icu::UnicodeString items[itemNum] = { "Monday", "Tuesday", "Wednesday" };
178 icu::UnicodeString resStr = "";
179 resStr = resFormatter->format(items, itemNum, resStr, status);
180 const icu::UnicodeString expectResStr("Monday, Tuesday, and Wednesday");
181 EXPECT_TRUE(resStr.compare(expectResStr) == 0);
182 }
183
HWTEST_F_L0(JSListFormatTest,FormatList_001)184 HWTEST_F_L0(JSListFormatTest, FormatList_001)
185 {
186 auto vm = thread->GetEcmaVM();
187 auto factory = vm->GetFactory();
188 auto env = vm->GetGlobalEnv();
189 icu::Locale icuLocale("en", "Latn", "US");
190 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
191 JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
192 std::map<std::string, std::string> options {
193 { "localeMatcher", "best fit" },
194 { "type", "conjunction" },
195 { "style", "long" }
196 };
197 SetFormatterOptionsTest(thread, object, options);
198 JSHandle<JSListFormat> jsFormatter = CreateJSListFormatterTest(thread, icuLocale, object);
199 JSHandle<JSObject> valueObj = JSHandle<JSObject>::Cast(factory->NewJSArray());
200 JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
201 JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
202 JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
203 JSHandle<JSTaggedValue> value0(factory->NewFromStdString("Zero"));
204 JSHandle<JSTaggedValue> value1(factory->NewFromStdString("One"));
205 JSHandle<JSTaggedValue> value2(factory->NewFromStdString("Two"));
206 JSObject::SetProperty(thread, valueObj, key0, value0);
207 JSObject::SetProperty(thread, valueObj, key1, value1);
208 JSObject::SetProperty(thread, valueObj, key2, value2);
209 JSHandle<JSArray> valueArr = JSHandle<JSArray>::Cast(valueObj);
210 JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
211 EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "Zero, One, and Two");
212 }
213
HWTEST_F_L0(JSListFormatTest,FormatList_002)214 HWTEST_F_L0(JSListFormatTest, FormatList_002)
215 {
216 auto vm = thread->GetEcmaVM();
217 auto factory = vm->GetFactory();
218 auto env = vm->GetGlobalEnv();
219 icu::Locale icuLocale("en", "Latn", "US");
220 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
221 JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
222 // when style is narrow, type can only be unit
223 std::map<std::string, std::string> options {
224 { "localeMatcher", "best fit" },
225 { "type", "unit" },
226 { "style", "narrow" }
227 };
228 SetFormatterOptionsTest(thread, object, options);
229 JSHandle<JSListFormat> jsFormatter = CreateJSListFormatterTest(thread, icuLocale, object);
230 JSHandle<JSObject> valueObj = JSHandle<JSObject>::Cast(factory->NewJSArray());
231 JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
232 JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
233 JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
234 JSHandle<JSTaggedValue> value0(factory->NewFromStdString("Zero"));
235 JSHandle<JSTaggedValue> value1(factory->NewFromStdString("One"));
236 JSHandle<JSTaggedValue> value2(factory->NewFromStdString("Two"));
237 JSObject::SetProperty(thread, valueObj, key0, value0);
238 JSObject::SetProperty(thread, valueObj, key1, value1);
239 JSObject::SetProperty(thread, valueObj, key2, value2);
240 JSHandle<JSArray> valueArr = JSHandle<JSArray>::Cast(valueObj);
241 JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
242 EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "Zero One Two");
243 }
244
HWTEST_F_L0(JSListFormatTest,FormatList_003)245 HWTEST_F_L0(JSListFormatTest, FormatList_003)
246 {
247 auto vm = thread->GetEcmaVM();
248 auto factory = vm->GetFactory();
249 auto env = vm->GetGlobalEnv();
250 icu::Locale icuLocale("zh", "Hans", "Cn");
251 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
252 JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
253 std::map<std::string, std::string> options {
254 { "localeMatcher", "best fit" },
255 { "type", "disjunction" },
256 { "style", "long" }
257 };
258 SetFormatterOptionsTest(thread, object, options);
259 JSHandle<JSListFormat> jsFormatter = CreateJSListFormatterTest(thread, icuLocale, object);
260 JSHandle<JSObject> valueObj = JSHandle<JSObject>::Cast(factory->NewJSArray());
261 JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
262 JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
263 JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
264 JSHandle<JSTaggedValue> value0(factory->NewFromStdString("苹果"));
265 JSHandle<JSTaggedValue> value1(factory->NewFromStdString("梨子"));
266 JSHandle<JSTaggedValue> value2(factory->NewFromStdString("桃"));
267 JSObject::SetProperty(thread, valueObj, key0, value0);
268 JSObject::SetProperty(thread, valueObj, key1, value1);
269 JSObject::SetProperty(thread, valueObj, key2, value2);
270 JSHandle<JSArray> valueArr = JSHandle<JSArray>::Cast(valueObj);
271 JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
272 EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "苹果、梨子或桃");
273 }
274
GetListPartStringTest(JSThread * thread,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> part)275 std::string GetListPartStringTest(JSThread *thread, JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> part)
276 {
277 JSHandle<JSObject> partObj = JSHandle<JSObject>::Cast(part);
278 JSHandle<JSTaggedValue> partValue = JSObject::GetProperty(thread, partObj, key).GetValue();
279 JSHandle<EcmaString> partEcmaStr = JSHandle<EcmaString>::Cast(partValue);
280 std::string partStr = intl::LocaleHelper::ConvertToStdString(partEcmaStr);
281 return partStr;
282 }
283
HWTEST_F_L0(JSListFormatTest,FormatListToParts)284 HWTEST_F_L0(JSListFormatTest, FormatListToParts)
285 {
286 auto vm = thread->GetEcmaVM();
287 auto factory = vm->GetFactory();
288 auto env = vm->GetGlobalEnv();
289 auto globalConst = thread->GlobalConstants();
290 icu::Locale icuLocale("zh", "Hans", "Cn");
291 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
292 JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
293 std::map<std::string, std::string> options {
294 { "localeMatcher", "best fit" },
295 { "type", "conjunction" },
296 { "style", "long" }
297 };
298 SetFormatterOptionsTest(thread, object, options);
299 JSHandle<JSListFormat> jsFormatter = CreateJSListFormatterTest(thread, icuLocale, object);
300 JSHandle<JSObject> valueObj = JSHandle<JSObject>::Cast(factory->NewJSArray());
301 JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
302 JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
303 JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
304 JSHandle<JSTaggedValue> value0(factory->NewFromStdString("苹果"));
305 JSHandle<JSTaggedValue> value1(factory->NewFromStdString("梨子"));
306 JSHandle<JSTaggedValue> value2(factory->NewFromStdString("桃"));
307 JSObject::SetProperty(thread, valueObj, key0, value0);
308 JSObject::SetProperty(thread, valueObj, key1, value1);
309 JSObject::SetProperty(thread, valueObj, key2, value2);
310 JSHandle<JSArray> valueArr = JSHandle<JSArray>::Cast(valueObj);
311 JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
312 EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "苹果、梨子和桃");
313
314 JSHandle<JSTaggedValue> typeKey = globalConst->GetHandledTypeString();
315 JSHandle<JSTaggedValue> valueKey = globalConst->GetHandledValueString();
316 JSHandle<JSArray> parts = JSListFormat::FormatListToParts(thread, jsFormatter, valueArr);
317 auto element1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 0).GetValue();
318 auto literal1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 1).GetValue();
319 auto element2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 2).GetValue();
320 auto literal2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 3).GetValue();
321 auto element3 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 4).GetValue();
322 EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element1).c_str(), "element");
323 EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element1).c_str(), "苹果");
324 EXPECT_STREQ(GetListPartStringTest(thread, typeKey, literal1).c_str(), "literal");
325 EXPECT_STREQ(GetListPartStringTest(thread, valueKey, literal1).c_str(), "、");
326 EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element2).c_str(), "element");
327 EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element2).c_str(), "梨子");
328 EXPECT_STREQ(GetListPartStringTest(thread, typeKey, literal2).c_str(), "literal");
329 EXPECT_STREQ(GetListPartStringTest(thread, valueKey, literal2).c_str(), "和");
330 EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element3).c_str(), "element");
331 EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element3).c_str(), "桃");
332 }
333
HWTEST_F_L0(JSListFormatTest,StringListFromIterable)334 HWTEST_F_L0(JSListFormatTest, StringListFromIterable)
335 {
336 auto vm = thread->GetEcmaVM();
337 auto factory = vm->GetFactory();
338 JSHandle<TaggedArray> data = factory->NewTaggedArray(3);
339 JSHandle<JSTaggedValue> value0(factory->NewFromStdString("one"));
340 JSHandle<JSTaggedValue> value1(factory->NewFromStdString("two"));
341 JSHandle<JSTaggedValue> value2(factory->NewFromStdString("three"));
342 data->Set(thread, 0, value0.GetTaggedValue());
343 data->Set(thread, 1, value1.GetTaggedValue());
344 data->Set(thread, 2, value2.GetTaggedValue());
345 JSHandle<JSTaggedValue> array(JSArray::CreateArrayFromList(thread, data));
346 JSHandle<JSArrayIterator> iter(JSIterator::GetIterator(thread, array));
347 JSHandle<JSTaggedValue> arrayString =
348 JSListFormat::StringListFromIterable(thread, JSHandle<JSTaggedValue>::Cast(iter));
349 JSHandle<JSArray> strValue = JSHandle<JSArray>::Cast(arrayString);
350 EXPECT_EQ(strValue->GetArrayLength(), 3U);
351
352 auto resValue0 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 0).GetValue();
353 auto resValue1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 1).GetValue();
354 auto resValue2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 2).GetValue();
355 EXPECT_STREQ(EcmaStringAccessor(resValue0.GetTaggedValue()).ToCString().c_str(), "one");
356 EXPECT_STREQ(EcmaStringAccessor(resValue1.GetTaggedValue()).ToCString().c_str(), "two");
357 EXPECT_STREQ(EcmaStringAccessor(resValue2.GetTaggedValue()).ToCString().c_str(), "three");
358 }
359 } // namespace panda::test
360