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/builtins/builtins_number_format.h"
17
18 #include "ecmascript/builtins/builtins_array.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/js_number_format.h"
21 #include "ecmascript/tests/test_helper.h"
22
23 using namespace panda::ecmascript;
24 using namespace panda::ecmascript::builtins;
25
26 namespace panda::test {
27 class BuiltinsNumberFormatTest : public BaseTestWithScope<true> {
28 };
29
30 // new DateTimeFormat(newTarget is undefined)
HWTEST_F_L0(BuiltinsNumberFormatTest, NumberFormatConstructor)31 HWTEST_F_L0(BuiltinsNumberFormatTest, NumberFormatConstructor)
32 {
33 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
34 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
35 JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
36
37 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-US"));
38 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
39 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
40 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
41 ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
42 // option tag is default value
43 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined());
44
45 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
46 JSTaggedValue result = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo);
47 TestHelper::TearDownFrame(thread, prev);
48
49 EXPECT_TRUE(result.IsJSNumberFormat());
50 }
51
BuiltinsFormatTest(JSThread *thread, JSHandle<JSObject> &options, JSHandle<JSTaggedValue> &number, JSHandle<JSTaggedValue> &locale)52 static JSTaggedValue BuiltinsFormatTest(JSThread *thread, JSHandle<JSObject> &options,
53 JSHandle<JSTaggedValue> &number, JSHandle<JSTaggedValue> &locale)
54 {
55 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
56 JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
57
58 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
59 ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue());
60 ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
61 ecmaRuntimeCallInfo1->SetCallArg(0, locale.GetTaggedValue());
62 ecmaRuntimeCallInfo1->SetCallArg(1, options.GetTaggedValue());
63 // construct numberformat
64 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
65 JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo1);
66 JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
67 TestHelper::TearDownFrame(thread, prev);
68 // get function by calling Format function
69 auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4);
70 ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
71 ecmaRuntimeCallInfo2->SetThis(numberFormatVal.GetTaggedValue());
72
73 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
74 JSTaggedValue resultFunc = BuiltinsNumberFormat::Format(ecmaRuntimeCallInfo2);
75 JSHandle<JSFunction> jsFunction(thread, resultFunc);
76 TestHelper::TearDownFrame(thread, prev);
77 auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
78 ecmaRuntimeCallInfo3->SetFunction(jsFunction.GetTaggedValue());
79 ecmaRuntimeCallInfo3->SetThis(jsFunction.GetTaggedValue());
80 ecmaRuntimeCallInfo3->SetCallArg(0, number.GetTaggedValue());
81
82 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3);
83 JSTaggedValue result = JSFunction::Call(ecmaRuntimeCallInfo3);
84 TestHelper::TearDownFrame(thread, prev);
85 return result;
86 }
87
88 // format decimal
HWTEST_F_L0(BuiltinsNumberFormatTest, Format_001)89 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_001)
90 {
91 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
92 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
93 auto globalConst = thread->GlobalConstants();
94 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
95
96 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
97 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("decimal"));
98 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
99 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
100
101 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
102 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
103 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
104
105 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
106 EXPECT_STREQ("3,500", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
107 }
108
109 // format currency
HWTEST_F_L0(BuiltinsNumberFormatTest, Format_002)110 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_002)
111 {
112 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
113 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
114 auto globalConst = thread->GlobalConstants();
115 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
116
117 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
118 JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
119 JSHandle<JSTaggedValue> currencyDisplayKey = globalConst->GetHandledCurrencyDisplayString();
120 JSHandle<JSTaggedValue> currencySignDisplayKey = globalConst->GetHandledCurrencySignString();
121
122 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
123 JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("USD"));
124 JSHandle<JSTaggedValue> currencyDisplayValue(factory->NewFromASCII("name"));
125 JSHandle<JSTaggedValue> currencySignDisplayValue(factory->NewFromASCII("accounting"));
126 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
127 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(static_cast<int32_t>(-3500)));
128
129 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
130 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
131 JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
132 JSObject::SetProperty(thread, optionsObj, currencyDisplayKey, currencyDisplayValue);
133 JSObject::SetProperty(thread, optionsObj, currencySignDisplayKey, currencySignDisplayValue);
134 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
135
136 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
137 EXPECT_STREQ("($3,500.00)", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
138 }
139
140 // format percent
HWTEST_F_L0(BuiltinsNumberFormatTest, Format_003)141 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_003)
142 {
143 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
144 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
145 auto globalConst = thread->GlobalConstants();
146 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
147
148 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
149 JSHandle<JSTaggedValue> signDisplayKey = globalConst->GetHandledSignDisplayString();
150
151 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("percent"));
152 JSHandle<JSTaggedValue> signDisplayValue(factory->NewFromASCII("exceptZero"));
153 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
154 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(static_cast<double>(0.55)));
155
156 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
157 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
158 JSObject::SetProperty(thread, optionsObj, signDisplayKey, signDisplayValue);
159 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
160
161 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
162 EXPECT_STREQ("+55%", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
163 }
164
165 // format unit
HWTEST_F_L0(BuiltinsNumberFormatTest, Format_004)166 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_004)
167 {
168 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
169 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
170 auto globalConst = thread->GlobalConstants();
171 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
172
173 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
174 JSHandle<JSTaggedValue> unitKey = globalConst->GetHandledUnitString();
175
176 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("unit"));
177 JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("liter"));
178 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
179 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
180
181 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
182 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
183 JSObject::SetProperty(thread, optionsObj, unitKey, unitValue);
184 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
185
186 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
187 EXPECT_STREQ("3,500 L", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
188 }
189
190 // format notation
HWTEST_F_L0(BuiltinsNumberFormatTest, Format_005)191 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_005)
192 {
193 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
194 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
195 auto globalConst = thread->GlobalConstants();
196 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
197
198 JSHandle<JSTaggedValue> notationKey = globalConst->GetHandledNotationString();
199 JSHandle<JSTaggedValue> notationValue(factory->NewFromASCII("compact"));
200 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("zh-CN"));
201 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(987654321));
202
203 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
204 JSObject::SetProperty(thread, optionsObj, notationKey, notationValue);
205 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
206
207 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
208 EXPECT_STREQ("9.9亿", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
209 }
210
NumberFormatCreateTest(JSThread *thread, JSHandle<JSObject> &options, JSHandle<JSTaggedValue> &locale)211 static JSTaggedValue NumberFormatCreateTest(JSThread *thread, JSHandle<JSObject> &options,
212 JSHandle<JSTaggedValue> &locale)
213 {
214 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
215 JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
216
217 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
218 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
219 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
220 ecmaRuntimeCallInfo->SetCallArg(0, locale.GetTaggedValue());
221 ecmaRuntimeCallInfo->SetCallArg(1, options.GetTaggedValue());
222 // construct numberformat
223 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
224 JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo);
225 TestHelper::TearDownFrame(thread, prev);
226 return numberFormat;
227 }
228
HWTEST_F_L0(BuiltinsNumberFormatTest, FormatToParts)229 HWTEST_F_L0(BuiltinsNumberFormatTest, FormatToParts)
230 {
231 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
232 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
233 auto globalConst = thread->GlobalConstants();
234 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
235
236 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
237 JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
238
239 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
240 JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("EUR"));
241 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("de-DE"));
242 JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
243
244 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
245 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
246 JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
247 JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString);
248 JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
249 // format currency
250 JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
251 JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
252 EXPECT_STREQ("3.500,00 €", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
253
254 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
255 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
256 ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue());
257 ecmaRuntimeCallInfo->SetCallArg(0, numberVal.GetTaggedValue());
258 // format currency to part
259 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
260 JSTaggedValue result = BuiltinsNumberFormat::FormatToParts(ecmaRuntimeCallInfo);
261 TestHelper::TearDownFrame(thread, prev);
262
263 JSHandle<JSArray> resultHandle(thread, result);
264 JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
265 EXPECT_EQ(elements->GetLength(), 10U); // "3","." ,"5" ,"0" ,"0" ,"," ,"0" ,0" ," " ,"€"
266 }
267
HWTEST_F_L0(BuiltinsNumberFormatTest, ResolvedOptions)268 HWTEST_F_L0(BuiltinsNumberFormatTest, ResolvedOptions)
269 {
270 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
271 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
272 auto globalConst = thread->GlobalConstants();
273 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
274
275 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
276 JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
277
278 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
279 JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("USD"));
280 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
281
282 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
283 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
284 JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
285 JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString);
286 JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
287
288 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
289 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
290 ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue());
291
292 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
293 JSTaggedValue result = BuiltinsNumberFormat::ResolvedOptions(ecmaRuntimeCallInfo);
294 TestHelper::TearDownFrame(thread, prev);
295
296 JSHandle<JSTaggedValue> resultObj =
297 JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
298 // judge whether the properties of the object are the same as those of jsnumberformat tag
299 EXPECT_EQ(JSTaggedValue::SameValue(
300 JSObject::GetProperty(thread, resultObj, styleKey).GetValue(), styleValue), true);
301 EXPECT_EQ(JSTaggedValue::SameValue(
302 JSObject::GetProperty(thread, resultObj, currencyKey).GetValue(), currencyValue), true);
303 }
304 }
305