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