• 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/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 testing::Test {
28 public:
SetUpTestCase()29     static void SetUpTestCase()
30     {
31         GTEST_LOG_(INFO) << "SetUpTestCase";
32     }
33 
TearDownTestCase()34     static void TearDownTestCase()
35     {
36         GTEST_LOG_(INFO) << "TearDownCase";
37     }
38 
SetUp()39     void SetUp() override
40     {
41         JSRuntimeOptions options;
42 #if PANDA_TARGET_LINUX
43         // for consistency requirement, use ohos_icu4j/data as icu-data-path
44         options.SetIcuDataPath(ICU_PATH);
45 #endif
46         options.SetEnableForceGC(true);
47         instance = JSNApi::CreateEcmaVM(options);
48         instance->SetEnableForceGC(true);
49         ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
50         thread = instance->GetJSThread();
51         scope = new EcmaHandleScope(thread);
52     }
53 
TearDown()54     void TearDown() override
55     {
56         TestHelper::DestroyEcmaVMWithScope(instance, scope);
57     }
58 
59     EcmaVM *instance {nullptr};
60     EcmaHandleScope *scope {nullptr};
61     JSThread *thread {nullptr};
62 };
63 
64 // new DateTimeFormat(newTarget is undefined)
HWTEST_F_L0(BuiltinsNumberFormatTest,NumberFormatConstructor)65 HWTEST_F_L0(BuiltinsNumberFormatTest, NumberFormatConstructor)
66 {
67     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
68     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
69     JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
70 
71     JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-US"));
72     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
73     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
74     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
75     ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
76     // option tag is default value
77     ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined());
78 
79     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
80     JSTaggedValue result = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo);
81     TestHelper::TearDownFrame(thread, prev);
82 
83     EXPECT_TRUE(result.IsJSNumberFormat());
84 }
85 
BuiltinsFormatTest(JSThread * thread,JSHandle<JSObject> & options,JSHandle<JSTaggedValue> & number,JSHandle<JSTaggedValue> & locale)86 static JSTaggedValue BuiltinsFormatTest(JSThread *thread, JSHandle<JSObject> &options,
87                                         JSHandle<JSTaggedValue> &number, JSHandle<JSTaggedValue> &locale)
88 {
89     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
90     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
91     JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
92 
93     auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
94     ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue());
95     ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
96     ecmaRuntimeCallInfo1->SetCallArg(0, locale.GetTaggedValue());
97     ecmaRuntimeCallInfo1->SetCallArg(1, options.GetTaggedValue());
98     // construct numberformat
99     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
100     JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo1);
101     JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
102     TestHelper::TearDownFrame(thread, prev);
103     // get function by calling Format function
104     auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4);
105     ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
106     ecmaRuntimeCallInfo2->SetThis(numberFormatVal.GetTaggedValue());
107 
108     prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
109     JSTaggedValue resultFunc = BuiltinsNumberFormat::Format(ecmaRuntimeCallInfo2);
110     JSHandle<JSFunction> jsFunction(thread, resultFunc);
111     TestHelper::TearDownFrame(thread, prev);
112     JSArray *jsArray =
113         JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
114     JSHandle<JSObject> jsObject(thread, jsArray);
115     PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(jsFunction), true, true, true);
116     JSHandle<JSTaggedValue> joinKey(factory->NewFromASCII("join"));
117     JSArray::DefineOwnProperty(thread, jsObject, joinKey, desc);
118 
119     auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
120     ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined());
121     ecmaRuntimeCallInfo3->SetThis(jsObject.GetTaggedValue());
122     ecmaRuntimeCallInfo3->SetCallArg(0, number.GetTaggedValue());
123 
124     prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3);
125     JSTaggedValue result = BuiltinsArray::ToString(ecmaRuntimeCallInfo3);
126     TestHelper::TearDownFrame(thread, prev);
127     return result;
128 }
129 
130 // format decimal
HWTEST_F_L0(BuiltinsNumberFormatTest,Format_001)131 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_001)
132 {
133     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
134     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
135     auto globalConst = thread->GlobalConstants();
136     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
137 
138     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
139     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("decimal"));
140     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
141     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
142 
143     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
144     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
145     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
146 
147     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
148     EXPECT_STREQ("3,500", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
149 }
150 
151 // format currency
HWTEST_F_L0(BuiltinsNumberFormatTest,Format_002)152 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_002)
153 {
154     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
155     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
156     auto globalConst = thread->GlobalConstants();
157     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
158 
159     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
160     JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
161     JSHandle<JSTaggedValue> currencyDisplayKey = globalConst->GetHandledCurrencyDisplayString();
162     JSHandle<JSTaggedValue> currencySignDisplayKey = globalConst->GetHandledCurrencySignString();
163 
164     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
165     JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("USD"));
166     JSHandle<JSTaggedValue> currencyDisplayValue(factory->NewFromASCII("name"));
167     JSHandle<JSTaggedValue> currencySignDisplayValue(factory->NewFromASCII("accounting"));
168     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
169     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(static_cast<int32_t>(-3500)));
170 
171     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
172     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
173     JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
174     JSObject::SetProperty(thread, optionsObj, currencyDisplayKey, currencyDisplayValue);
175     JSObject::SetProperty(thread, optionsObj, currencySignDisplayKey, currencySignDisplayValue);
176     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
177 
178     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
179     EXPECT_STREQ("($3,500.00)", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
180 }
181 
182 // format percent
HWTEST_F_L0(BuiltinsNumberFormatTest,Format_003)183 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_003)
184 {
185     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
186     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
187     auto globalConst = thread->GlobalConstants();
188     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
189 
190     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
191     JSHandle<JSTaggedValue> signDisplayKey = globalConst->GetHandledSignDisplayString();
192 
193     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("percent"));
194     JSHandle<JSTaggedValue> signDisplayValue(factory->NewFromASCII("exceptZero"));
195     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
196     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(static_cast<double>(0.55)));
197 
198     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
199     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
200     JSObject::SetProperty(thread, optionsObj, signDisplayKey, signDisplayValue);
201     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
202 
203     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
204     EXPECT_STREQ("+55%", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
205 }
206 
207 // format unit
HWTEST_F_L0(BuiltinsNumberFormatTest,Format_004)208 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_004)
209 {
210     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
211     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
212     auto globalConst = thread->GlobalConstants();
213     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
214 
215     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
216     JSHandle<JSTaggedValue> unitKey = globalConst->GetHandledUnitString();
217 
218     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("unit"));
219     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("liter"));
220     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
221     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
222 
223     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
224     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
225     JSObject::SetProperty(thread, optionsObj, unitKey, unitValue);
226     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
227 
228     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
229     EXPECT_STREQ("3,500 L", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
230 }
231 
232 // format notation
HWTEST_F_L0(BuiltinsNumberFormatTest,Format_005)233 HWTEST_F_L0(BuiltinsNumberFormatTest, Format_005)
234 {
235     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
236     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
237     auto globalConst = thread->GlobalConstants();
238     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
239 
240     JSHandle<JSTaggedValue> notationKey = globalConst->GetHandledNotationString();
241     JSHandle<JSTaggedValue> notationValue(factory->NewFromASCII("compact"));
242     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("zh-CN"));
243     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(987654321));
244 
245     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
246     JSObject::SetProperty(thread, optionsObj, notationKey, notationValue);
247     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
248 
249     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
250     EXPECT_STREQ("9.9亿", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
251 }
252 
NumberFormatCreateTest(JSThread * thread,JSHandle<JSObject> & options,JSHandle<JSTaggedValue> & locale)253 static JSTaggedValue NumberFormatCreateTest(JSThread *thread, JSHandle<JSObject> &options,
254                                             JSHandle<JSTaggedValue> &locale)
255 {
256     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
257     JSHandle<JSFunction> newTarget(env->GetNumberFormatFunction());
258 
259     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
260     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
261     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
262     ecmaRuntimeCallInfo->SetCallArg(0, locale.GetTaggedValue());
263     ecmaRuntimeCallInfo->SetCallArg(1, options.GetTaggedValue());
264     // construct numberformat
265     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
266     JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo);
267     TestHelper::TearDownFrame(thread, prev);
268     return numberFormat;
269 }
270 
HWTEST_F_L0(BuiltinsNumberFormatTest,FormatToParts)271 HWTEST_F_L0(BuiltinsNumberFormatTest, FormatToParts)
272 {
273     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
274     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
275     auto globalConst = thread->GlobalConstants();
276     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
277 
278     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
279     JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
280 
281     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
282     JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("EUR"));
283     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("de-DE"));
284     JSHandle<JSTaggedValue> numberVal(thread, JSTaggedValue(3500));
285 
286     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
287     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
288     JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
289     JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString);
290     JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
291     // format currency
292     JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString);
293     JSHandle<EcmaString> resultEcmaStr(thread, formatResult);
294     EXPECT_STREQ("3.500,00 €", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
295 
296     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
297     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
298     ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue());
299     ecmaRuntimeCallInfo->SetCallArg(0, numberVal.GetTaggedValue());
300     // format currency to part
301     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
302     JSTaggedValue result = BuiltinsNumberFormat::FormatToParts(ecmaRuntimeCallInfo);
303     TestHelper::TearDownFrame(thread, prev);
304 
305     JSHandle<JSArray> resultHandle(thread, result);
306     JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
307     EXPECT_EQ(elements->GetLength(), 10U); // "3","." ,"5" ,"0" ,"0" ,"," ,"0" ,0" ," " ,"€"
308 }
309 
HWTEST_F_L0(BuiltinsNumberFormatTest,ResolvedOptions)310 HWTEST_F_L0(BuiltinsNumberFormatTest, ResolvedOptions)
311 {
312     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
313     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
314     auto globalConst = thread->GlobalConstants();
315     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
316 
317     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
318     JSHandle<JSTaggedValue> currencyKey = globalConst->GetHandledCurrencyString();
319 
320     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("currency"));
321     JSHandle<JSTaggedValue> currencyValue(factory->NewFromASCII("USD"));
322     JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
323 
324     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
325     JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
326     JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue);
327     JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString);
328     JSHandle<JSTaggedValue> numberFormatVal(thread, numberFormat);
329 
330     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
331     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
332     ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue());
333 
334     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
335     JSTaggedValue result = BuiltinsNumberFormat::ResolvedOptions(ecmaRuntimeCallInfo);
336     TestHelper::TearDownFrame(thread, prev);
337 
338     JSHandle<JSTaggedValue> resultObj =
339         JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
340     // judge whether the properties of the object are the same as those of jsnumberformat tag
341     EXPECT_EQ(JSTaggedValue::SameValue(
342         JSObject::GetProperty(thread, resultObj, styleKey).GetValue(), styleValue), true);
343     EXPECT_EQ(JSTaggedValue::SameValue(
344         JSObject::GetProperty(thread, resultObj, currencyKey).GetValue(), currencyValue), true);
345 }
346 }
347