• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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