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_relative_time_format.h"
17 
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/js_relative_time_format.h"
20 #include "ecmascript/tests/test_helper.h"
21 
22 using namespace panda::ecmascript;
23 using namespace panda::ecmascript::builtins;
24 
25 namespace panda::test {
26 class BuiltinsRelativeTimeFormatTest : public BaseTestWithScope<true> {
27 };
28 
29 // new RelativeTimeFormat(newTarget is undefined)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, RelativeTimeFormatConstructor)30 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, RelativeTimeFormatConstructor)
31 {
32     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
34     JSHandle<JSFunction> newTarget(env->GetRelativeTimeFormatFunction());
35 
36     JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en"));
37     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
38     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
39     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
40     ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
41     // option tag is default value
42     ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined());
43 
44     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
45     JSTaggedValue result = BuiltinsRelativeTimeFormat::RelativeTimeFormatConstructor(ecmaRuntimeCallInfo);
46     TestHelper::TearDownFrame(thread, prev);
47 
48     EXPECT_TRUE(result.IsJSRelativeTimeFormat());
49 }
50 
JSRelativeTimeFormatCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale, JSHandle<JSTaggedValue> &numericValue)51 static JSTaggedValue JSRelativeTimeFormatCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale,
52                                                               JSHandle<JSTaggedValue> &numericValue)
53 {
54     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
55     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
56     JSHandle<JSFunction> newTarget(env->GetRelativeTimeFormatFunction());
57     JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
58     JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
59 
60     JSHandle<JSTaggedValue> typeKey = thread->GlobalConstants()->GetHandledNumericString();
61     JSObject::SetProperty(thread, optionsObj, typeKey, numericValue);
62 
63     JSHandle<JSTaggedValue> localesString = locale;
64     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
65     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
66     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
67     ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
68     ecmaRuntimeCallInfo->SetCallArg(1, optionsObj.GetTaggedValue());
69 
70     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
71     JSTaggedValue result = BuiltinsRelativeTimeFormat::RelativeTimeFormatConstructor(ecmaRuntimeCallInfo);
72     EXPECT_TRUE(result.IsJSRelativeTimeFormat());
73     TestHelper::TearDownFrame(thread, prev);
74     return result;
75 }
76 
FormatCommon(JSThread *thread, JSHandle<JSRelativeTimeFormat> &jsPluralRules, JSHandle<JSTaggedValue> &numberValue, JSHandle<JSTaggedValue> &unitValue)77 JSTaggedValue FormatCommon(JSThread *thread, JSHandle<JSRelativeTimeFormat> &jsPluralRules,
78                            JSHandle<JSTaggedValue> &numberValue, JSHandle<JSTaggedValue> &unitValue)
79 {
80     auto ecmaRuntimeCallInfo =
81         TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);  // 8: arg max len
82     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
83     ecmaRuntimeCallInfo->SetThis(jsPluralRules.GetTaggedValue());
84     ecmaRuntimeCallInfo->SetCallArg(0, numberValue.GetTaggedValue());
85     ecmaRuntimeCallInfo->SetCallArg(1, unitValue.GetTaggedValue());
86 
87     auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
88     JSTaggedValue result = BuiltinsRelativeTimeFormat::Format(ecmaRuntimeCallInfo);
89     TestHelper::TearDownFrame(thread, prev);
90     return result;
91 }
92 
93 // format(1, auto)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_001)94 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_001)
95 {
96     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
97     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
98     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("auto")); // the default value
99     JSHandle<JSRelativeTimeFormat> jsPluralRules =
100         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
101                                         thread, locale, numericValue));
102     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("day"));
103     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(1));
104     auto result = FormatCommon(thread, jsPluralRules, numberValue, unitValue);
105 
106     JSHandle<EcmaString> handleEcmaStr(thread, result);
107     EXPECT_STREQ("tomorrow", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
108 }
109 
110 // format(0, auto)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_002)111 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_002)
112 {
113     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
114     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
115     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("auto"));
116     JSHandle<JSRelativeTimeFormat> jsPluralRules =
117         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
118                                         thread, locale, numericValue));
119     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("day"));
120     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(0));
121     auto result = FormatCommon(thread, jsPluralRules, numberValue, unitValue);
122 
123     JSHandle<EcmaString> handleEcmaStr(thread, result);
124     EXPECT_STREQ("today", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
125 }
126 
127 // format(-1, auto)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_003)128 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_003)
129 {
130     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
131     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
132     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("auto")); // the default value
133     JSHandle<JSRelativeTimeFormat> jsPluralRules =
134         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
135                                         thread, locale, numericValue));
136     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("day"));
137     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(-1));
138 
139     auto result = FormatCommon(thread, jsPluralRules, numberValue, unitValue);
140 
141     JSHandle<EcmaString> handleEcmaStr(thread, result);
142     EXPECT_STREQ("yesterday", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
143 }
144 
145 // format(-1, always)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_004)146 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_004)
147 {
148     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
149     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
150     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("always")); // the default value
151     JSHandle<JSRelativeTimeFormat> jsPluralRules =
152         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
153                                         thread, locale, numericValue));
154     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("day"));
155     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(-1));
156 
157     auto result = FormatCommon(thread, jsPluralRules, numberValue, unitValue);
158 
159     JSHandle<EcmaString> handleEcmaStr(thread, result);
160     EXPECT_STREQ("1 day ago", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
161 }
162 
163 // format(1, always)
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_005)164 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, Format_005)
165 {
166     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
167     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
168     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("always")); // the default value
169     JSHandle<JSRelativeTimeFormat> jsPluralRules =
170         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
171                                         thread, locale, numericValue));
172     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("day"));
173     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(1));
174     auto result = FormatCommon(thread, jsPluralRules, numberValue, unitValue);
175 
176     JSHandle<EcmaString> handleEcmaStr(thread, result);
177     EXPECT_STREQ("in 1 day", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
178 }
179 
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, FormatToParts)180 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, FormatToParts)
181 {
182     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
183     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
184     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en"));
185     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("always"));
186     JSHandle<JSRelativeTimeFormat> jsPluralRules =
187         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
188                                         thread, locale, numericValue));
189     JSHandle<JSTaggedValue> unitValue(factory->NewFromASCII("seconds"));
190     JSHandle<JSTaggedValue> numberValue(thread, JSTaggedValue(10));
191 
192     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
193     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
194     ecmaRuntimeCallInfo->SetThis(jsPluralRules.GetTaggedValue());
195     ecmaRuntimeCallInfo->SetCallArg(0, numberValue.GetTaggedValue());
196     ecmaRuntimeCallInfo->SetCallArg(1, unitValue.GetTaggedValue());
197 
198     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
199     JSTaggedValue result = BuiltinsRelativeTimeFormat::FormatToParts(ecmaRuntimeCallInfo);
200     TestHelper::TearDownFrame(thread, prev);
201 
202     JSHandle<JSArray> resultArr(thread, result);
203     EXPECT_EQ(JSObject::GetProperty(thread, JSHandle<JSObject>(resultArr), lengthKey).GetValue()->GetInt(), 3);
204 }
205 
HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, ResolvedOptions)206 HWTEST_F_L0(BuiltinsRelativeTimeFormatTest, ResolvedOptions)
207 {
208     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
209     auto globalConst = thread->GlobalConstants();
210     JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de-DE"));
211     JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("auto"));
212     JSHandle<JSRelativeTimeFormat> jsPluralRules =
213         JSHandle<JSRelativeTimeFormat>(thread, JSRelativeTimeFormatCreateWithLocaleTest(
214                                         thread, locale, numericValue));
215 
216     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
217     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
218     ecmaRuntimeCallInfo->SetThis(jsPluralRules.GetTaggedValue());
219 
220     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
221     JSTaggedValue result = BuiltinsRelativeTimeFormat::ResolvedOptions(ecmaRuntimeCallInfo);
222     TestHelper::TearDownFrame(thread, prev);
223 
224     JSHandle<JSTaggedValue> resultObj =
225         JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
226     // judge whether the properties of the object are the same as those of jsrelativetimeformat tag
227     JSHandle<JSTaggedValue> localeKey = globalConst->GetHandledLocaleString();
228     EXPECT_EQ(JSTaggedValue::SameValue(
229         JSObject::GetProperty(thread, resultObj, localeKey).GetValue(), locale), true);
230     JSHandle<JSTaggedValue> styleKey = globalConst->GetHandledStyleString();
231     JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("long"));
232     EXPECT_EQ(JSTaggedValue::SameValue(
233         JSObject::GetProperty(thread, resultObj, styleKey).GetValue(), styleValue), true);
234     JSHandle<JSTaggedValue> numberingSystemKey = globalConst->GetHandledNumberingSystemString();
235     JSHandle<JSTaggedValue> numberingSystemValue(factory->NewFromASCII("latn"));
236     EXPECT_EQ(JSTaggedValue::SameValue(
237         JSObject::GetProperty(thread, resultObj, numberingSystemKey).GetValue(), numberingSystemValue), true);
238 }
239 } // namespace panda::test
240