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(thread);
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(thread);
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(thread);
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(thread).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(thread).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(thread).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(thread, 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(thread).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(thread).c_str(), "one");
288 EXPECT_STREQ(EcmaStringAccessor(resValue1.GetTaggedValue()).ToCString(thread).c_str(), "two");
289 EXPECT_STREQ(EcmaStringAccessor(resValue2.GetTaggedValue()).ToCString(thread).c_str(), "three");
290 }
291 } // namespace panda::test
292