/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/builtins/builtins_number_format.h" #include "ecmascript/builtins/builtins_array.h" #include "ecmascript/global_env.h" #include "ecmascript/js_number_format.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; using namespace panda::ecmascript::builtins; namespace panda::test { class BuiltinsNumberFormatTest : public BaseTestWithScope { }; // new DateTimeFormat(newTarget is undefined) HWTEST_F_L0(BuiltinsNumberFormatTest, NumberFormatConstructor) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle newTarget(env->GetNumberFormatFunction()); JSHandle localesString(factory->NewFromASCII("en-US")); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8); ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue()); // option tag is default value ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_TRUE(result.IsJSNumberFormat()); } static JSTaggedValue BuiltinsFormatTest(JSThread *thread, JSHandle &options, JSHandle &number, JSHandle &locale) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle newTarget(env->GetNumberFormatFunction()); auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8); ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo1->SetCallArg(0, locale.GetTaggedValue()); ecmaRuntimeCallInfo1->SetCallArg(1, options.GetTaggedValue()); // construct numberformat [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo1); JSHandle numberFormatVal(thread, numberFormat); TestHelper::TearDownFrame(thread, prev); // get function by calling Format function auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo2->SetThis(numberFormatVal.GetTaggedValue()); prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); JSTaggedValue resultFunc = BuiltinsNumberFormat::Format(ecmaRuntimeCallInfo2); JSHandle jsFunction(thread, resultFunc); TestHelper::TearDownFrame(thread, prev); auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo3->SetFunction(jsFunction.GetTaggedValue()); ecmaRuntimeCallInfo3->SetThis(jsFunction.GetTaggedValue()); ecmaRuntimeCallInfo3->SetCallArg(0, number.GetTaggedValue()); prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); JSTaggedValue result = JSFunction::Call(ecmaRuntimeCallInfo3); TestHelper::TearDownFrame(thread, prev); return result; } // format decimal HWTEST_F_L0(BuiltinsNumberFormatTest, Format_001) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle styleValue(factory->NewFromASCII("decimal")); JSHandle localeString(factory->NewFromASCII("en-US")); JSHandle numberVal(thread, JSTaggedValue(3500)); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("3,500", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); } // format currency HWTEST_F_L0(BuiltinsNumberFormatTest, Format_002) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle currencyKey = globalConst->GetHandledCurrencyString(); JSHandle currencyDisplayKey = globalConst->GetHandledCurrencyDisplayString(); JSHandle currencySignDisplayKey = globalConst->GetHandledCurrencySignString(); JSHandle styleValue(factory->NewFromASCII("currency")); JSHandle currencyValue(factory->NewFromASCII("USD")); JSHandle currencyDisplayValue(factory->NewFromASCII("name")); JSHandle currencySignDisplayValue(factory->NewFromASCII("accounting")); JSHandle localeString(factory->NewFromASCII("en-US")); JSHandle numberVal(thread, JSTaggedValue(static_cast(-3500))); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue); JSObject::SetProperty(thread, optionsObj, currencyDisplayKey, currencyDisplayValue); JSObject::SetProperty(thread, optionsObj, currencySignDisplayKey, currencySignDisplayValue); JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("($3,500.00)", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); } // format percent HWTEST_F_L0(BuiltinsNumberFormatTest, Format_003) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle signDisplayKey = globalConst->GetHandledSignDisplayString(); JSHandle styleValue(factory->NewFromASCII("percent")); JSHandle signDisplayValue(factory->NewFromASCII("exceptZero")); JSHandle localeString(factory->NewFromASCII("en-US")); JSHandle numberVal(thread, JSTaggedValue(static_cast(0.55))); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSObject::SetProperty(thread, optionsObj, signDisplayKey, signDisplayValue); JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("+55%", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); } // format unit HWTEST_F_L0(BuiltinsNumberFormatTest, Format_004) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle unitKey = globalConst->GetHandledUnitString(); JSHandle styleValue(factory->NewFromASCII("unit")); JSHandle unitValue(factory->NewFromASCII("liter")); JSHandle localeString(factory->NewFromASCII("en-US")); JSHandle numberVal(thread, JSTaggedValue(3500)); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSObject::SetProperty(thread, optionsObj, unitKey, unitValue); JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("3,500 L", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); } // format notation HWTEST_F_L0(BuiltinsNumberFormatTest, Format_005) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle notationKey = globalConst->GetHandledNotationString(); JSHandle notationValue(factory->NewFromASCII("compact")); JSHandle localeString(factory->NewFromASCII("zh-CN")); JSHandle numberVal(thread, JSTaggedValue(987654321)); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, notationKey, notationValue); JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("9.9亿", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); } static JSTaggedValue NumberFormatCreateTest(JSThread *thread, JSHandle &options, JSHandle &locale) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle newTarget(env->GetNumberFormatFunction()); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8); ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetCallArg(0, locale.GetTaggedValue()); ecmaRuntimeCallInfo->SetCallArg(1, options.GetTaggedValue()); // construct numberformat [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue numberFormat = BuiltinsNumberFormat::NumberFormatConstructor(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); return numberFormat; } HWTEST_F_L0(BuiltinsNumberFormatTest, FormatToParts) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle currencyKey = globalConst->GetHandledCurrencyString(); JSHandle styleValue(factory->NewFromASCII("currency")); JSHandle currencyValue(factory->NewFromASCII("EUR")); JSHandle localeString(factory->NewFromASCII("de-DE")); JSHandle numberVal(thread, JSTaggedValue(3500)); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue); JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString); JSHandle numberFormatVal(thread, numberFormat); // format currency JSTaggedValue formatResult = BuiltinsFormatTest(thread, optionsObj, numberVal, localeString); JSHandle resultEcmaStr(thread, formatResult); EXPECT_STREQ("3.500,00 €", EcmaStringAccessor(resultEcmaStr).ToCString().c_str()); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue()); ecmaRuntimeCallInfo->SetCallArg(0, numberVal.GetTaggedValue()); // format currency to part [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsNumberFormat::FormatToParts(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); JSHandle resultHandle(thread, result); JSHandle elements(thread, resultHandle->GetElements()); EXPECT_EQ(elements->GetLength(), 10U); // "3","." ,"5" ,"0" ,"0" ,"," ,"0" ,0" ," " ,"€" } HWTEST_F_L0(BuiltinsNumberFormatTest, ResolvedOptions) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = thread->GlobalConstants(); JSHandle objFun = env->GetObjectFunction(); JSHandle styleKey = globalConst->GetHandledStyleString(); JSHandle currencyKey = globalConst->GetHandledCurrencyString(); JSHandle styleValue(factory->NewFromASCII("currency")); JSHandle currencyValue(factory->NewFromASCII("USD")); JSHandle localeString(factory->NewFromASCII("en-US")); JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); JSObject::SetProperty(thread, optionsObj, styleKey, styleValue); JSObject::SetProperty(thread, optionsObj, currencyKey, currencyValue); JSTaggedValue numberFormat = NumberFormatCreateTest(thread, optionsObj, localeString); JSHandle numberFormatVal(thread, numberFormat); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); ecmaRuntimeCallInfo->SetThis(numberFormatVal.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); JSTaggedValue result = BuiltinsNumberFormat::ResolvedOptions(ecmaRuntimeCallInfo); TestHelper::TearDownFrame(thread, prev); JSHandle resultObj = JSHandle(thread, JSTaggedValue(static_cast(result.GetRawData()))); // judge whether the properties of the object are the same as those of jsnumberformat tag EXPECT_EQ(JSTaggedValue::SameValue( JSObject::GetProperty(thread, resultObj, styleKey).GetValue(), styleValue), true); EXPECT_EQ(JSTaggedValue::SameValue( JSObject::GetProperty(thread, resultObj, currencyKey).GetValue(), currencyValue), true); } }