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_date_time_format.h"
17
18 #include <ctime>
19 #include <algorithm>
20 #include "ecmascript/builtins/builtins_array.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/js_date.h"
23 #include "ecmascript/js_date_time_format.h"
24 #include "ecmascript/tests/test_helper.h"
25
26 using namespace panda::ecmascript;
27 using namespace panda::ecmascript::builtins;
28
29 namespace panda::test {
30 using BuiltinsArray = ecmascript::builtins::BuiltinsArray;
31 class BuiltinsDateTimeFormatTest : public testing::Test {
32 public:
SetUpTestCase()33 static void SetUpTestCase()
34 {
35 GTEST_LOG_(INFO) << "SetUpTestCase";
36 }
37
TearDownTestCase()38 static void TearDownTestCase()
39 {
40 GTEST_LOG_(INFO) << "TearDownCase";
41 }
42
SetUp()43 void SetUp() override
44 {
45 JSRuntimeOptions options;
46 #if PANDA_TARGET_LINUX
47 // for consistency requirement, use ohos_icu4j/data as icu-data-path
48 options.SetIcuDataPath(ICU_PATH);
49 #endif
50 options.SetEnableForceGC(true);
51 instance = JSNApi::CreateEcmaVM(options);
52 instance->SetEnableForceGC(true);
53 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
54 thread = instance->GetJSThread();
55 scope = new EcmaHandleScope(thread);
56 }
57
TearDown()58 void TearDown() override
59 {
60 TestHelper::DestroyEcmaVMWithScope(instance, scope);
61 }
62
63 EcmaVM *instance {nullptr};
64 EcmaHandleScope *scope {nullptr};
65 JSThread *thread {nullptr};
66 };
67
68 // new DateTimeFormat(locale)
HWTEST_F_L0(BuiltinsDateTimeFormatTest,DateTimeFormatConstructor)69 HWTEST_F_L0(BuiltinsDateTimeFormatTest, DateTimeFormatConstructor)
70 {
71 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
72 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
73 JSHandle<JSFunction> newTarget(env->GetDateTimeFormatFunction());
74
75 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-US"));
76 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
77 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
78 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
79 ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
80 // option tag is default value
81 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined());
82
83 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
84 JSTaggedValue result = BuiltinsDateTimeFormat::DateTimeFormatConstructor(ecmaRuntimeCallInfo);
85 TestHelper::TearDownFrame(thread, prev);
86 EXPECT_TRUE(result.IsJSDateTimeFormat());
87 }
88
BuiltinsDateTimeOptionsSet(JSThread * thread)89 static JSTaggedValue BuiltinsDateTimeOptionsSet(JSThread *thread)
90 {
91 auto globalConst = thread->GlobalConstants();
92 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
93 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
94
95 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
96 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
97
98 JSHandle<JSTaggedValue> weekDay = globalConst->GetHandledWeekdayString();
99 JSHandle<JSTaggedValue> dayPeriod = globalConst->GetHandledDayPeriodString();
100 JSHandle<JSTaggedValue> hourCycle = globalConst->GetHandledHourCycleString();
101 JSHandle<JSTaggedValue> timeZone = globalConst->GetHandledTimeZoneString();
102 JSHandle<JSTaggedValue> numicValue(factory->NewFromASCII("numeric")); // test numeric
103 JSHandle<JSTaggedValue> weekDayValue(factory->NewFromASCII("short")); // test short
104 JSHandle<JSTaggedValue> dayPeriodValue(factory->NewFromASCII("long")); // test long
105 JSHandle<JSTaggedValue> hourCycleValue(factory->NewFromASCII("h24")); // test h24
106 JSHandle<JSTaggedValue> timeZoneValue(factory->NewFromASCII("UTC")); // test UTC
107
108 JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(6); // 6 : 6 length
109 keyArray->Set(thread, 0, globalConst->GetHandledYearString()); // 0 : 0 first position
110 keyArray->Set(thread, 1, globalConst->GetHandledMonthString()); // 1 : 1 second position
111 keyArray->Set(thread, 2, globalConst->GetHandledDayString()); // 2 : 2 third position
112 keyArray->Set(thread, 3, globalConst->GetHandledHourString()); // 3 : 3 fourth position
113 keyArray->Set(thread, 4, globalConst->GetHandledMinuteString()); // 4 : 4 fifth position
114 keyArray->Set(thread, 5, globalConst->GetHandledSecondString()); // 5 : 5 sixth position
115
116 uint32_t arrayLen = keyArray->GetLength();
117 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
118 for (uint32_t i = 0; i < arrayLen; i++) {
119 key.Update(keyArray->Get(thread, i));
120 JSObject::SetProperty(thread, optionsObj, key, numicValue);
121 }
122 JSObject::SetProperty(thread, optionsObj, weekDay, weekDayValue);
123 JSObject::SetProperty(thread, optionsObj, dayPeriod, dayPeriodValue);
124 JSObject::SetProperty(thread, optionsObj, hourCycle, hourCycleValue);
125 JSObject::SetProperty(thread, optionsObj, timeZone, timeZoneValue);
126 return optionsObj.GetTaggedValue();
127 }
128
JSDateTimeFormatCreateWithLocaleTest(JSThread * thread,JSHandle<JSTaggedValue> & locale)129 static JSTaggedValue JSDateTimeFormatCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)
130 {
131 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
132 JSHandle<JSFunction> newTarget(env->GetDateTimeFormatFunction());
133 JSHandle<JSObject> optionsObj(thread, BuiltinsDateTimeOptionsSet(thread));
134
135 JSHandle<JSTaggedValue> localesString = locale;
136 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
137 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
138 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
139 ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
140 ecmaRuntimeCallInfo->SetCallArg(1, optionsObj.GetTaggedValue());
141
142 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
143 JSTaggedValue result = BuiltinsDateTimeFormat::DateTimeFormatConstructor(ecmaRuntimeCallInfo);
144 EXPECT_TRUE(result.IsJSDateTimeFormat());
145 TestHelper::TearDownFrame(thread, prev);
146 return result;
147 }
148
BuiltinsDateCreate(const double year,const double month,const double date)149 static double BuiltinsDateCreate(const double year, const double month, const double date)
150 {
151 const double day = JSDate::MakeDay(year, month, date);
152 const double time = JSDate::MakeTime(0, 0, 0, 0); // 24:00:00
153 double days = JSDate::MakeDate(day, time);
154 return days;
155 }
156
157 // Format.Tostring(en-US)
HWTEST_F_L0(BuiltinsDateTimeFormatTest,Format_001)158 HWTEST_F_L0(BuiltinsDateTimeFormatTest, Format_001)
159 {
160 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
161 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en-US"));
162 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
163 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
164
165 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
166 ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
167 ecmaRuntimeCallInfo1->SetThis(jsDateTimeFormat.GetTaggedValue());
168 ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue::Undefined());
169
170 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
171 JSTaggedValue result1 = BuiltinsDateTimeFormat::Format(ecmaRuntimeCallInfo1);
172 TestHelper::TearDownFrame(thread, prev);
173 // jsDate supports zero to eleven, the month should be added with one
174 JSHandle<JSFunction> jsFunction(thread, result1);
175 JSArray *jsArray =
176 JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
177 JSHandle<JSObject> jsObject(thread, jsArray);
178
179 double days = BuiltinsDateCreate(2020, 10, 1);
180 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(static_cast<double>(days)));
181 PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(jsFunction), true, true, true);
182 JSHandle<JSTaggedValue> joinKey(factory->NewFromASCII("join"));
183 JSArray::DefineOwnProperty(thread, jsObject, joinKey, desc);
184
185 auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
186 ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
187 ecmaRuntimeCallInfo2->SetThis(jsObject.GetTaggedValue());
188 ecmaRuntimeCallInfo2->SetCallArg(0, value.GetTaggedValue());
189
190 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
191 JSTaggedValue result2 = BuiltinsArray::ToString(ecmaRuntimeCallInfo2);
192 TestHelper::TearDownFrame(thread, prev);
193 JSHandle<EcmaString> resultStr(thread, result2);
194 EXPECT_STREQ("Sun, 11/1/2020, 24:00:00", EcmaStringAccessor(resultStr).ToCString().c_str());
195 }
196
197 // Format.Tostring(pt-BR)
HWTEST_F_L0(BuiltinsDateTimeFormatTest,Format_002)198 HWTEST_F_L0(BuiltinsDateTimeFormatTest, Format_002)
199 {
200 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
201 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("pt-BR"));
202 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
203 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
204
205 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
206 ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
207 ecmaRuntimeCallInfo1->SetThis(jsDateTimeFormat.GetTaggedValue());
208 ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue::Undefined());
209
210 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
211 JSTaggedValue result1 = BuiltinsDateTimeFormat::Format(ecmaRuntimeCallInfo1);
212 TestHelper::TearDownFrame(thread, prev);
213
214 JSHandle<JSFunction> jsFunction(thread, result1);
215 JSArray *jsArray =
216 JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
217 JSHandle<JSObject> jsObject(thread, jsArray);
218
219 double days = BuiltinsDateCreate(2020, 5, 11);
220 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(static_cast<double>(days)));
221 PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(jsFunction), true, true, true);
222 JSHandle<JSTaggedValue> joinKey(factory->NewFromASCII("join"));
223 JSArray::DefineOwnProperty(thread, jsObject, joinKey, desc);
224
225 auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
226 ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
227 ecmaRuntimeCallInfo2->SetThis(jsObject.GetTaggedValue());
228 ecmaRuntimeCallInfo2->SetCallArg(0, value.GetTaggedValue());
229
230 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
231 JSTaggedValue result2 = BuiltinsArray::ToString(ecmaRuntimeCallInfo2);
232 TestHelper::TearDownFrame(thread, prev);
233 JSHandle<EcmaString> resultStr(thread, result2);
234 CString resStr = EcmaStringAccessor(resultStr).ToCString();
235 // the index of string "qui" is zero.
236 EXPECT_TRUE(resStr.find("qui") == 0);
237 // the index of string "11/06/2020 24:00:00" is not zero.
238 EXPECT_TRUE(resStr.find("11/06/2020 24:00:00") != 0);
239 }
240
HWTEST_F_L0(BuiltinsDateTimeFormatTest,FormatToParts)241 HWTEST_F_L0(BuiltinsDateTimeFormatTest, FormatToParts)
242 {
243 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
244 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en-US"));
245 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
246 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
247
248 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
249 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
250 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
251 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());
252
253 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
254 JSTaggedValue result = BuiltinsDateTimeFormat::FormatToParts(ecmaRuntimeCallInfo);
255 TestHelper::TearDownFrame(thread, prev);
256
257 JSHandle<JSArray> resultHandle(thread, result);
258 JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
259 EXPECT_EQ(elements->GetLength(), 16U); // sixteen formatters
260 }
261
262 // FormatRange(zh)
HWTEST_F_L0(BuiltinsDateTimeFormatTest,FormatRange_001)263 HWTEST_F_L0(BuiltinsDateTimeFormatTest, FormatRange_001)
264 {
265 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
266 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("zh"));
267 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
268 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
269
270 double days1 = BuiltinsDateCreate(2020, 10, 1);
271 double days2 = BuiltinsDateCreate(2021, 6, 1);
272 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
273 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
274 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
275 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<double>(days1)));
276 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<double>(days2)));
277
278 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
279 JSTaggedValue result = BuiltinsDateTimeFormat::FormatRange(ecmaRuntimeCallInfo);
280 TestHelper::TearDownFrame(thread, prev);
281
282 JSHandle<EcmaString> handleStr(thread, result);
283 JSHandle<EcmaString> resultStr = factory->NewFromUtf8("2020/11/1周日 24:00:00 – 2021/7/1周四 24:00:00");
284 EXPECT_EQ(EcmaStringAccessor::Compare(instance, handleStr, resultStr), 0);
285 }
286
287 // FormatRange(en)
HWTEST_F_L0(BuiltinsDateTimeFormatTest,FormatRange_002)288 HWTEST_F_L0(BuiltinsDateTimeFormatTest, FormatRange_002)
289 {
290 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
291 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en-US"));
292 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
293 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
294
295 double days1 = BuiltinsDateCreate(2020, 12, 1);
296 double days2 = BuiltinsDateCreate(2021, 2, 1);
297 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
298 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
299 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
300 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<double>(days1)));
301 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<double>(days2)));
302
303 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
304 JSTaggedValue result = BuiltinsDateTimeFormat::FormatRange(ecmaRuntimeCallInfo);
305 TestHelper::TearDownFrame(thread, prev);
306
307 JSHandle<EcmaString> handleStr(thread, result);
308 JSHandle<EcmaString> resultStr = factory->NewFromUtf8("Fri, 1/1/2021, 24:00:00 – Mon, 3/1/2021, 24:00:00");
309 EXPECT_EQ(EcmaStringAccessor::Compare(instance, handleStr, resultStr), 0);
310 }
311
HWTEST_F_L0(BuiltinsDateTimeFormatTest,FormatRangeToParts)312 HWTEST_F_L0(BuiltinsDateTimeFormatTest, FormatRangeToParts)
313 {
314 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
315 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("en-US"));
316 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
317 JSHandle<JSDateTimeFormat>(thread, JSDateTimeFormatCreateWithLocaleTest(thread, locale));
318
319 double days1 = BuiltinsDateCreate(2020, 12, 1);
320 double days2 = BuiltinsDateCreate(2021, 2, 1);
321 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
322 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
323 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
324 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<double>(days1)));
325 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<double>(days2)));
326
327 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
328 JSTaggedValue result = BuiltinsDateTimeFormat::FormatRangeToParts(ecmaRuntimeCallInfo);
329 TestHelper::TearDownFrame(thread, prev);
330
331 JSHandle<JSArray> resultHandle(thread, result);
332 JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
333 EXPECT_EQ(elements->GetLength(), 39U); // The number of characters of "Fri1/1/202124:00:00–Mon3/1/202124:00:00"
334 }
335 } // namespace panda::test
336
337