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