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