• 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 BaseTestWithScope<true> {
28 };
29 
HWTEST_F_L0(JSListFormatTest,Set_Get_IcuListFormatter_001)30 HWTEST_F_L0(JSListFormatTest, Set_Get_IcuListFormatter_001)
31 {
32     auto vm = thread->GetEcmaVM();
33     auto factory = vm->GetFactory();
34     auto env = vm->GetGlobalEnv();
35     JSHandle<JSTaggedValue> ctor = env->GetListFormatFunction();
36     JSHandle<JSListFormat> jsFormatter =
37         JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
38     UErrorCode status = UErrorCode::U_ZERO_ERROR;
39     icu::Locale icuLocale("en", "Latn", "US");
40     icu::ListFormatter* icuFormatter = icu::ListFormatter::createInstance(icuLocale, status);
41     JSListFormat::SetIcuListFormatter(thread, jsFormatter, icuFormatter, JSListFormat::FreeIcuListFormatter);
42     icu::ListFormatter *resFormatter = jsFormatter->GetIcuListFormatter();
43     EXPECT_TRUE(resFormatter != nullptr);
44 
45     const int32_t itemNum = 3;
46     const icu::UnicodeString items[itemNum] = { "One", "Two", "Three" };
47     icu::UnicodeString resStr = "";
48     resStr = resFormatter->format(items, itemNum, resStr, status);
49     const icu::UnicodeString expectResStr("One, Two, and Three");
50     EXPECT_TRUE(resStr.compare(expectResStr) == 0);
51 }
52 
HWTEST_F_L0(JSListFormatTest,Set_Get_IcuListFormatter_002)53 HWTEST_F_L0(JSListFormatTest, Set_Get_IcuListFormatter_002)
54 {
55     auto vm = thread->GetEcmaVM();
56     auto factory = vm->GetFactory();
57     auto env = vm->GetGlobalEnv();
58     JSHandle<JSTaggedValue> ctor = env->GetListFormatFunction();
59     JSHandle<JSListFormat> jsFormatter =
60         JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
61     UErrorCode status = UErrorCode::U_ZERO_ERROR;
62     icu::Locale icuLocale("zh", "Hans", "Cn");
63     icu::ListFormatter* icuFormatter = icu::ListFormatter::createInstance(icuLocale, status);
64     JSListFormat::SetIcuListFormatter(thread, jsFormatter, icuFormatter, JSListFormat::FreeIcuListFormatter);
65     icu::ListFormatter *resFormatter = jsFormatter->GetIcuListFormatter();
66     EXPECT_TRUE(resFormatter != nullptr);
67 
68     const int32_t itemNum = 3;
69     const icu::UnicodeString items[itemNum] = { "一", "二", "三" };
70     icu::UnicodeString resStr = "";
71     resStr = resFormatter->format(items, itemNum, resStr, status);
72     const icu::UnicodeString expectResStr("一、二和三");
73     EXPECT_TRUE(resStr.compare(expectResStr) == 0);
74 }
75 
CreateJSListFormatterTest(JSThread * thread,icu::Locale icuLocale,JSHandle<JSObject> options)76 JSHandle<JSListFormat> CreateJSListFormatterTest(JSThread *thread, icu::Locale icuLocale, JSHandle<JSObject> options)
77 {
78     auto vm = thread->GetEcmaVM();
79     auto factory = vm->GetFactory();
80     auto env = vm->GetGlobalEnv();
81 
82     JSHandle<JSTaggedValue> localeCtor = env->GetLocaleFunction();
83     JSHandle<JSTaggedValue> listCtor = env->GetListFormatFunction();
84     JSHandle<JSLocale> locales =
85         JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(localeCtor), localeCtor));
86     JSHandle<JSListFormat> listFormatter =
87         JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(listCtor), listCtor));
88 
89     JSHandle<JSTaggedValue> optionsVal = JSHandle<JSTaggedValue>::Cast(options);
90     factory->NewJSIntlIcuData(locales, icuLocale, JSLocale::FreeIcuLocale);
91     listFormatter = JSListFormat::InitializeListFormat(thread, listFormatter,
92         JSHandle<JSTaggedValue>::Cast(locales), optionsVal);
93     return listFormatter;
94 }
95 
SetFormatterOptionsTest(JSThread * thread,JSHandle<JSObject> & optionsObj,std::map<std::string,std::string> & options)96 void SetFormatterOptionsTest(JSThread *thread, JSHandle<JSObject> &optionsObj,
97     std::map<std::string, std::string> &options)
98 {
99     auto vm = thread->GetEcmaVM();
100     auto factory = vm->GetFactory();
101     auto globalConst = thread->GlobalConstants();
102     JSHandle<JSTaggedValue> localeMatcherKey = globalConst->GetHandledLocaleMatcherString();
103     JSHandle<JSTaggedValue> typeKey = globalConst->GetHandledTypeString();
104     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
105     JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII(options["localeMatcher"].c_str()));
106     JSHandle<JSTaggedValue> typeValue(factory->NewFromASCII(options["type"].c_str()));
107     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII(options["style"].c_str()));
108     JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
109     JSObject::SetProperty(thread, optionsObj, typeKey, typeValue);
110     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
111 }
112 
HWTEST_F_L0(JSListFormatTest,InitializeListFormat)113 HWTEST_F_L0(JSListFormatTest, InitializeListFormat)
114 {
115     auto vm = thread->GetEcmaVM();
116     auto factory = vm->GetFactory();
117     auto env = vm->GetGlobalEnv();
118     icu::Locale icuLocale("en", "Latn", "US");
119     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
120     JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
121     std::map<std::string, std::string> options {
122         { "localeMatcher", "best fit" },
123         { "type", "conjunction" },
124         { "style", "long" }
125     };
126     SetFormatterOptionsTest(thread, object, options);
127     JSHandle<JSTaggedValue> localeCtor = env->GetLocaleFunction();
128     JSHandle<JSTaggedValue> listCtor = env->GetListFormatFunction();
129     JSHandle<JSLocale> locales =
130         JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(localeCtor), localeCtor));
131     JSHandle<JSListFormat> listFormatter =
132         JSHandle<JSListFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(listCtor), listCtor));
133 
134     JSHandle<JSTaggedValue> optionsVal = JSHandle<JSTaggedValue>::Cast(object);
135     factory->NewJSIntlIcuData(locales, icuLocale, JSLocale::FreeIcuLocale);
136     listFormatter = JSListFormat::InitializeListFormat(thread, listFormatter,
137         JSHandle<JSTaggedValue>::Cast(locales), optionsVal);
138     icu::ListFormatter *resFormatter = listFormatter->GetIcuListFormatter();
139     EXPECT_TRUE(resFormatter != nullptr);
140 
141     const int32_t itemNum = 3;
142     UErrorCode status = UErrorCode::U_ZERO_ERROR;
143     const icu::UnicodeString items[itemNum] = { "Monday", "Tuesday", "Wednesday" };
144     icu::UnicodeString resStr = "";
145     resStr = resFormatter->format(items, itemNum, resStr, status);
146     const icu::UnicodeString expectResStr("Monday, Tuesday, and Wednesday");
147     EXPECT_TRUE(resStr.compare(expectResStr) == 0);
148 }
149 
FormatCommon(JSThread * thread,std::vector<std::string> & strs)150 JSHandle<JSArray> FormatCommon(JSThread *thread, std::vector<std::string>& strs)
151 {
152     auto factory = thread->GetEcmaVM()->GetFactory();
153     JSHandle<JSObject> valueObj = JSHandle<JSObject>::Cast(factory->NewJSArray());
154     for (size_t i = 0; i < strs.size(); i++) {
155         JSHandle<JSTaggedValue> key(thread, JSTaggedValue(static_cast<int32_t>(i)));
156         JSHandle<JSTaggedValue> value(factory->NewFromStdString(strs[i]));
157         JSObject::SetProperty(thread, valueObj, key, value);
158     }
159     JSHandle<JSArray> valueArr = JSHandle<JSArray>::Cast(valueObj);
160     return valueArr;
161 }
162 
GetFormatter(JSThread * thread,std::map<std::string,std::string> & options,icu::Locale & icuLocale)163 JSHandle<JSListFormat> GetFormatter(JSThread *thread, std::map<std::string, std::string>& options,
164     icu::Locale& icuLocale)
165 {
166     auto vm = thread->GetEcmaVM();
167     auto factory = vm->GetFactory();
168     auto env = vm->GetGlobalEnv();
169     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
170     JSHandle<JSObject> object = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
171     SetFormatterOptionsTest(thread, object, options);
172     JSHandle<JSListFormat> jsFormatter = CreateJSListFormatterTest(thread, icuLocale, object);
173     return jsFormatter;
174 }
175 
HWTEST_F_L0(JSListFormatTest,FormatList_001)176 HWTEST_F_L0(JSListFormatTest, FormatList_001)
177 {
178     icu::Locale icuLocale("en", "Latn", "US");
179     std::map<std::string, std::string> options {
180         { "localeMatcher", "best fit" },
181         { "type", "conjunction" },
182         { "style", "long" }
183     };
184     JSHandle<JSListFormat> jsFormatter = GetFormatter(thread, options, icuLocale);
185     std::vector<std::string> strs{"Zero", "One", "Two"};
186     JSHandle<JSArray> valueArr = FormatCommon(thread, strs);
187     JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
188     EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "Zero, One, and Two");
189 }
190 
HWTEST_F_L0(JSListFormatTest,FormatList_002)191 HWTEST_F_L0(JSListFormatTest, FormatList_002)
192 {
193     icu::Locale icuLocale("en", "Latn", "US");
194     // when style is narrow, type can only be unit
195     std::map<std::string, std::string> options {
196         { "localeMatcher", "best fit" },
197         { "type", "unit" },
198         { "style", "narrow" }
199     };
200     JSHandle<JSListFormat> jsFormatter = GetFormatter(thread, options, icuLocale);
201     std::vector<std::string> strs{"Zero", "One", "Two"};
202     JSHandle<JSArray> valueArr = FormatCommon(thread, strs);
203     JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
204     EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "Zero One Two");
205 }
206 
HWTEST_F_L0(JSListFormatTest,FormatList_003)207 HWTEST_F_L0(JSListFormatTest, FormatList_003)
208 {
209     icu::Locale icuLocale("zh", "Hans", "Cn");
210     std::map<std::string, std::string> options {
211         { "localeMatcher", "best fit" },
212         { "type", "disjunction" },
213         { "style", "long" }
214     };
215     JSHandle<JSListFormat> jsFormatter = GetFormatter(thread, options, icuLocale);
216     std::vector<std::string> strs{"苹果", "梨子", "桃"};
217     JSHandle<JSArray> valueArr = FormatCommon(thread, strs);
218     JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
219     EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "苹果、梨子或桃");
220 }
221 
GetListPartStringTest(JSThread * thread,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> part)222 std::string GetListPartStringTest(JSThread *thread, JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> part)
223 {
224     JSHandle<JSObject> partObj = JSHandle<JSObject>::Cast(part);
225     JSHandle<JSTaggedValue> partValue = JSObject::GetProperty(thread, partObj, key).GetValue();
226     JSHandle<EcmaString> partEcmaStr = JSHandle<EcmaString>::Cast(partValue);
227     std::string partStr = intl::LocaleHelper::ConvertToStdString(partEcmaStr);
228     return partStr;
229 }
230 
HWTEST_F_L0(JSListFormatTest,FormatListToParts)231 HWTEST_F_L0(JSListFormatTest, FormatListToParts)
232 {
233     icu::Locale icuLocale("zh", "Hans", "Cn");
234     std::map<std::string, std::string> options {
235         { "localeMatcher", "best fit" },
236         { "type", "conjunction" },
237         { "style", "long" }
238     };
239     JSHandle<JSListFormat> jsFormatter = GetFormatter(thread, options, icuLocale);
240     std::vector<std::string> strs{"苹果", "梨子", "桃"};
241     JSHandle<JSArray> valueArr = FormatCommon(thread, strs);
242     JSHandle<EcmaString> valueStr = JSListFormat::FormatList(thread, jsFormatter, valueArr);
243     EXPECT_STREQ(EcmaStringAccessor(valueStr).ToCString().c_str(), "苹果、梨子和桃");
244 
245     auto globalConst = thread->GlobalConstants();
246     JSHandle<JSTaggedValue> typeKey = globalConst->GetHandledTypeString();
247     JSHandle<JSTaggedValue> valueKey = globalConst->GetHandledValueString();
248     JSHandle<JSArray> parts = JSListFormat::FormatListToParts(thread, jsFormatter, valueArr);
249     auto element1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 0).GetValue();
250     auto literal1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 1).GetValue();
251     auto element2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 2).GetValue();
252     auto literal2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 3).GetValue();
253     auto element3 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(parts), 4).GetValue();
254     EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element1).c_str(), "element");
255     EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element1).c_str(), "苹果");
256     EXPECT_STREQ(GetListPartStringTest(thread, typeKey, literal1).c_str(), "literal");
257     EXPECT_STREQ(GetListPartStringTest(thread, valueKey, literal1).c_str(), "、");
258     EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element2).c_str(), "element");
259     EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element2).c_str(), "梨子");
260     EXPECT_STREQ(GetListPartStringTest(thread, typeKey, literal2).c_str(), "literal");
261     EXPECT_STREQ(GetListPartStringTest(thread, valueKey, literal2).c_str(), "和");
262     EXPECT_STREQ(GetListPartStringTest(thread, typeKey, element3).c_str(), "element");
263     EXPECT_STREQ(GetListPartStringTest(thread, valueKey, element3).c_str(), "桃");
264 }
265 
HWTEST_F_L0(JSListFormatTest,StringListFromIterable)266 HWTEST_F_L0(JSListFormatTest, StringListFromIterable)
267 {
268     auto vm = thread->GetEcmaVM();
269     auto factory = vm->GetFactory();
270     JSHandle<TaggedArray> data = factory->NewTaggedArray(3);
271     JSHandle<JSTaggedValue> value0(factory->NewFromStdString("one"));
272     JSHandle<JSTaggedValue> value1(factory->NewFromStdString("two"));
273     JSHandle<JSTaggedValue> value2(factory->NewFromStdString("three"));
274     data->Set(thread, 0, value0.GetTaggedValue());
275     data->Set(thread, 1, value1.GetTaggedValue());
276     data->Set(thread, 2, value2.GetTaggedValue());
277     JSHandle<JSTaggedValue> array(JSArray::CreateArrayFromList(thread, data));
278     JSHandle<JSArrayIterator> iter(JSIterator::GetIterator(thread, array));
279     JSHandle<JSTaggedValue> arrayString =
280         JSListFormat::StringListFromIterable(thread, JSHandle<JSTaggedValue>::Cast(iter));
281     JSHandle<JSArray> strValue = JSHandle<JSArray>::Cast(arrayString);
282     EXPECT_EQ(strValue->GetArrayLength(), 3U);
283 
284     auto resValue0 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 0).GetValue();
285     auto resValue1 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 1).GetValue();
286     auto resValue2 = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(strValue), 2).GetValue();
287     EXPECT_STREQ(EcmaStringAccessor(resValue0.GetTaggedValue()).ToCString().c_str(), "one");
288     EXPECT_STREQ(EcmaStringAccessor(resValue1.GetTaggedValue()).ToCString().c_str(), "two");
289     EXPECT_STREQ(EcmaStringAccessor(resValue2.GetTaggedValue()).ToCString().c_str(), "three");
290 }
291 }  // namespace panda::test
292