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/js_number_format.h"
17 #include "ecmascript/napi/jsnapi_helper.h"
18 #include "ecmascript/tests/test_helper.h"
19
20 using namespace panda::ecmascript;
21
22 namespace panda::test {
23 class JSNumberFormatTest : public testing::Test {
24 public:
SetUpTestCase()25 static void SetUpTestCase()
26 {
27 GTEST_LOG_(INFO) << "SetUpTestCase";
28 }
29
TearDownTestCase()30 static void TearDownTestCase()
31 {
32 GTEST_LOG_(INFO) << "TearDownCase";
33 }
34
SetUp()35 void SetUp() override
36 {
37 JSRuntimeOptions options;
38 #if PANDA_TARGET_LINUX
39 // for consistency requirement, use ohos_icu4j/data/icudt67l.dat as icu-data-path
40 options.SetIcuDataPath(ICU_PATH);
41 #endif
42 options.SetEnableForceGC(true);
43 instance = JSNApi::CreateEcmaVM(options);
44 instance->SetEnableForceGC(true);
45 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
46 thread = instance->GetJSThread();
47 scope = new EcmaHandleScope(thread);
48 }
49
TearDown()50 void TearDown() override
51 {
52 TestHelper::DestroyEcmaVMWithScope(instance, scope);
53 }
54
55 EcmaVM *instance {nullptr};
56 ecmascript::EcmaHandleScope *scope {nullptr};
57 JSThread *thread {nullptr};
58 };
59
60 /**
61 * @tc.name: GetIcuCallTarget
62 * @tc.desc: Call "NewJSIntlIcuData" function Set IcuCallTarget,check whether the IcuCallTarget through
63 * "GetIcuCallTarget" function is within expectations then call "formatInt" function format
64 * Int type data and check the returned value is within expectations.
65 * @tc.type: FUNC
66 * @tc.require:
67 */
HWTEST_F_L0(JSNumberFormatTest,GetIcuCallTarget)68 HWTEST_F_L0(JSNumberFormatTest, GetIcuCallTarget)
69 {
70 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
71 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
72
73 JSHandle<JSTaggedValue> ctor = env->GetNumberFormatFunction();
74 JSHandle<JSNumberFormat> numberFormat =
75 JSHandle<JSNumberFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
76
77 icu::Locale icuLocale("en", "US");
78 icu::number::LocalizedNumberFormatter icuNumberFormatter =
79 icu::number::NumberFormatter::withLocale(icuLocale).roundingMode(UNUM_ROUND_HALFUP);
80 // Set IcuCallTarget
81 factory->NewJSIntlIcuData(numberFormat, icuNumberFormatter, JSNumberFormat::FreeIcuNumberformat);
82 icu::number::LocalizedNumberFormatter *resultIcuNumberFormatter = numberFormat->GetIcuCallTarget();
83 EXPECT_TRUE(resultIcuNumberFormatter != nullptr);
84 // Use IcuCallTarget format Int
85 int64_t value = -123456;
86 UErrorCode status = U_ZERO_ERROR;
87 icu::number::FormattedNumber formattedNumber = resultIcuNumberFormatter->formatInt(value, status);
88 icu::UnicodeString result = formattedNumber.toString(status);
89 JSHandle<EcmaString> stringValue = JSLocale::IcuToString(thread, result);
90 EXPECT_STREQ("-123,456", EcmaStringAccessor(stringValue).ToCString().c_str());
91 }
92
93 /**
94 * @tc.name: InitializeNumberFormat
95 * @tc.desc: Call "InitializeNumberFormat" function Initialize NumberFormat,and check whether the properties of
96 * the object is within expectations.
97 * @tc.type: FUNC
98 * @tc.require:
99 */
HWTEST_F_L0(JSNumberFormatTest,InitializeNumberFormat)100 HWTEST_F_L0(JSNumberFormatTest, InitializeNumberFormat)
101 {
102 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
103 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
104
105 JSHandle<JSTaggedValue> ctor = env->GetNumberFormatFunction();
106 JSHandle<JSNumberFormat> numberFormat =
107 JSHandle<JSNumberFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
108 EXPECT_TRUE(*numberFormat != nullptr);
109
110 JSHandle<JSTaggedValue> locales(factory->NewFromASCII("zh-Hans-CN"));
111 JSHandle<JSTaggedValue> undefinedOptions(thread, JSTaggedValue::Undefined());
112 JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, undefinedOptions);
113 // Initialize attribute comparison
114 EXPECT_TRUE(numberFormat->GetNumberingSystem().IsUndefined());
115 JSHandle<EcmaString> localeStr(thread, numberFormat->GetLocale().GetTaggedObject());
116 EXPECT_STREQ("zh-Hans-CN", EcmaStringAccessor(localeStr).ToCString().c_str());
117 EXPECT_EQ(NotationOption::STANDARD, numberFormat->GetNotation());
118 EXPECT_EQ(CompactDisplayOption::SHORT, numberFormat->GetCompactDisplay());
119 EXPECT_EQ(SignDisplayOption::AUTO, numberFormat->GetSignDisplay());
120 EXPECT_EQ(CurrencyDisplayOption::SYMBOL, numberFormat->GetCurrencyDisplay());
121 EXPECT_EQ(CurrencySignOption::STANDARD, numberFormat->GetCurrencySign());
122 EXPECT_EQ(UnitDisplayOption::SHORT, numberFormat->GetUnitDisplay());
123 EXPECT_EQ(numberFormat->GetMinimumIntegerDigits().GetInt(), 1); // 1 : 1 default minimum integer
124 EXPECT_EQ(numberFormat->GetMinimumFractionDigits().GetInt(), 0); // 0 : 0 default minimum fraction
125 EXPECT_EQ(numberFormat->GetMaximumFractionDigits().GetInt(), 3); // 1 : 1 default maximum fraction
126 EXPECT_EQ(numberFormat->GetMinimumSignificantDigits().GetInt(), 0); // 0 : 0 default minimum sigfraction
127 EXPECT_EQ(numberFormat->GetMaximumSignificantDigits().GetInt(), 0); // 0 : 0 default maximum sigfraction
128 EXPECT_TRUE(numberFormat->GetUseGrouping().IsTrue());
129 EXPECT_TRUE(numberFormat->GetBoundFormat().IsUndefined());
130 EXPECT_TRUE(numberFormat->GetUnit().IsUndefined());
131 EXPECT_TRUE(numberFormat->GetCurrency().IsUndefined());
132 }
133
134 /**
135 * @tc.name: CurrencyDigits
136 * @tc.desc: If the ISO 4217 currency contains currency as an alphabetic code, return the minor unit value
137 * corresponding to the currency from the list
138 * @tc.type: FUNC
139 * @tc.require:
140 */
HWTEST_F_L0(JSNumberFormatTest,CurrencyDigits)141 HWTEST_F_L0(JSNumberFormatTest, CurrencyDigits)
142 {
143 // Alphabetic code:USD
144 icu::UnicodeString usdCurrency("USD");
145 // 2 : 2 fraction digits
146 EXPECT_EQ(JSNumberFormat::CurrencyDigits(usdCurrency), 2);
147 // Alphabetic code:EUR
148 icu::UnicodeString eurCurrency("EUR");
149 // 2 : 2 fraction digits
150 EXPECT_EQ(JSNumberFormat::CurrencyDigits(eurCurrency), 2);
151 // Alphabetic code:CHF
152 icu::UnicodeString numberCurrency("CHF");
153 // 2 : 2 fraction digits
154 EXPECT_EQ(JSNumberFormat::CurrencyDigits(numberCurrency), 2);
155 }
156
157 /**
158 * @tc.name: FormatNumeric
159 * @tc.desc: Call "InitializeNumberFormat" function Initialize NumberFormat,Set the sytle attribute of the object to
160 * decimal,construct a bigint type data,and the object calls the FormatNumeric method to interpret the bigint
161 * type data into the corresponding decimal, and check whether the decimal meets the expectation.
162 * @tc.type: FUNC
163 * @tc.require:
164 */
HWTEST_F_L0(JSNumberFormatTest,FormatNumeric)165 HWTEST_F_L0(JSNumberFormatTest, FormatNumeric)
166 {
167 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
168 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
169 auto globalConst = thread->GlobalConstants();
170
171 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
172 JSHandle<JSTaggedValue> ctor = env->GetNumberFormatFunction();
173 JSHandle<JSNumberFormat> numberFormat =
174 JSHandle<JSNumberFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
175
176 JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
177 JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("decimal"));
178 JSHandle<JSTaggedValue> localeString(factory->NewFromASCII("en-US"));
179 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
180 JSObject::SetProperty(thread, optionsObj, styleKey, styleValue);
181 JSNumberFormat::InitializeNumberFormat(thread, numberFormat, localeString, JSHandle<JSTaggedValue>(optionsObj));
182 // format BigInt
183 JSHandle<JSTaggedValue> number(thread, JSTaggedValue(123456789));
184 JSHandle<BigInt> jsBigInt(thread, BigInt::NumberToBigInt(thread, number));
185 JSHandle<JSTaggedValue> formatResult =
186 JSNumberFormat::FormatNumeric(thread, numberFormat, jsBigInt.GetTaggedValue());
187
188 JSHandle<EcmaString> resultEcmaStr(thread, formatResult.GetTaggedValue());
189 EXPECT_STREQ("123,456,789", EcmaStringAccessor(resultEcmaStr).ToCString().c_str());
190 }
191
192 /**
193 * @tc.name: UnwrapNumberFormat
194 * @tc.desc: Construct an object,If it is a numberformat object,it will be returned.If it is an object of other types
195 * and inherits the numberformat object, it will get value from the fallbacksymbol key and return.
196 * @tc.type: FUNC
197 * @tc.require:
198 */
HWTEST_F_L0(JSNumberFormatTest,UnwrapNumberFormat)199 HWTEST_F_L0(JSNumberFormatTest, UnwrapNumberFormat)
200 {
201 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
202 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
203 EcmaVM *vm = thread->GetEcmaVM();
204
205 JSHandle<JSTaggedValue> numberFormatFunc = env->GetNumberFormatFunction();
206 JSHandle<JSTaggedValue> numberFormat(
207 factory->NewJSObjectByConstructor(JSHandle<JSFunction>(numberFormatFunc), numberFormatFunc));
208
209 Local<FunctionRef> numberFormatLocal = JSNApiHelper::ToLocal<FunctionRef>(numberFormatFunc);
210 JSHandle<JSTaggedValue> disPlayNamesFunc = env->GetDisplayNamesFunction();
211 Local<FunctionRef> disPlayNamesLocal = JSNApiHelper::ToLocal<FunctionRef>(disPlayNamesFunc);
212 // displaynames Inherit numberformat
213 disPlayNamesLocal->Inherit(vm, numberFormatLocal);
214 JSHandle<JSTaggedValue> disPlayNamesHandle = JSNApiHelper::ToJSHandle(disPlayNamesLocal);
215 JSHandle<JSTaggedValue> disPlayNamesObj(
216 factory->NewJSObjectByConstructor(JSHandle<JSFunction>::Cast(disPlayNamesHandle), disPlayNamesHandle));
217 // object has no Instance
218 JSHandle<JSTaggedValue> unwrapNumberFormat1 = JSNumberFormat::UnwrapNumberFormat(thread, numberFormat);
219 EXPECT_TRUE(JSTaggedValue::SameValue(numberFormat, unwrapNumberFormat1));
220 // object has Instance
221 JSHandle<JSTaggedValue> unwrapNumberFormat2 = JSNumberFormat::UnwrapNumberFormat(thread, disPlayNamesObj);
222 EXPECT_TRUE(unwrapNumberFormat2->IsUndefined());
223 }
224 } // namespace panda::test