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 "builtin_test_util.h"
17 #include "ecmascript/builtins/builtins_date_time_format.h"
18
19 #include <ctime>
20 #include <algorithm>
21 #include "ecmascript/builtins/builtins_array.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/js_date.h"
24 #include "ecmascript/js_date_time_format.h"
25 #include "ecmascript/tests/test_helper.h"
26
27 using namespace panda::ecmascript;
28 using namespace panda::ecmascript::builtins;
29
30 namespace panda::test {
31 using BuiltinsArray = ecmascript::builtins::BuiltinsArray;
32 class BuiltinsDateTimeFormatTest : public BaseTestWithScope<true> {
33 };
34
HWTEST_F_L0(BuiltinsDateTimeFormatTest,ResolvedOptions)35 HWTEST_F_L0(BuiltinsDateTimeFormatTest, ResolvedOptions)
36 {
37 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
38 auto globalConst = thread->GlobalConstants();
39 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de-ID"));
40 JSHandle<JSDateTimeFormat> jsDateTimeFormat =
41 JSHandle<JSDateTimeFormat>(thread, BuiltTestUtil::JSDateTimeFormatCreateWithLocaleTest(thread, locale));
42
43 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
44 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
45 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
46
47 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
48 JSTaggedValue result = BuiltinsDateTimeFormat::ResolvedOptions(ecmaRuntimeCallInfo);
49 TestHelper::TearDownFrame(thread, prev);
50
51 JSHandle<JSTaggedValue> resultObj =
52 JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
53 // judge whether the properties of the object are the same as those of jsdatetimeformat tag
54 JSHandle<JSTaggedValue> localeKey = globalConst->GetHandledLocaleString();
55 JSHandle<JSTaggedValue> localeValue(factory->NewFromASCII("de"));
56 EXPECT_EQ(JSTaggedValue::SameValue(
57 JSObject::GetProperty(thread, resultObj, localeKey).GetValue(), localeValue), true);
58 JSHandle<JSTaggedValue> timeZone = globalConst->GetHandledTimeZoneString();
59 JSHandle<JSTaggedValue> timeZoneValue(factory->NewFromASCII("UTC"));
60 EXPECT_EQ(JSTaggedValue::SameValue(
61 JSObject::GetProperty(thread, resultObj, timeZone).GetValue(), timeZoneValue), true);
62 }
63
64 // SupportedLocalesOf("best fit")
HWTEST_F_L0(BuiltinsDateTimeFormatTest,SupportedLocalesOf_001)65 HWTEST_F_L0(BuiltinsDateTimeFormatTest, SupportedLocalesOf_001)
66 {
67 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
68 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("id-u-co-pinyin-de-ID"));
69
70 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
71 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
72 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
73 ecmaRuntimeCallInfo->SetCallArg(0, locale.GetTaggedValue());
74 // set the tag is default value
75 ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined());
76
77 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
78 JSTaggedValue resultArr = BuiltinsDateTimeFormat::SupportedLocalesOf(ecmaRuntimeCallInfo);
79 TestHelper::TearDownFrame(thread, prev);
80
81 JSHandle<JSArray> resultHandle(thread, resultArr);
82 JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
83 EXPECT_EQ(elements->GetLength(), 1U);
84
85 JSHandle<EcmaString> resultStr(thread, elements->Get(0));
86 EXPECT_STREQ("id-u-co-pinyin-de-id", EcmaStringAccessor(resultStr).ToCString().c_str());
87 }
88
89 // SupportedLocalesOf("look up")
HWTEST_F_L0(BuiltinsDateTimeFormatTest,SupportedLocalesOf_002)90 HWTEST_F_L0(BuiltinsDateTimeFormatTest, SupportedLocalesOf_002)
91 {
92 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
93 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
94
95 JSHandle<JSTaggedValue> localeMatcherKey = thread->GlobalConstants()->GetHandledLocaleMatcherString();
96 JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII("lookup"));
97 JSHandle<JSTaggedValue> locale(factory->NewFromASCII("id-u-co-pinyin-de-DE"));
98
99 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
100 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
101 JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
102
103 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
104 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
105 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
106 ecmaRuntimeCallInfo->SetCallArg(0, locale.GetTaggedValue());
107 ecmaRuntimeCallInfo->SetCallArg(1, optionsObj.GetTaggedValue());
108
109 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
110 JSTaggedValue resultArr = BuiltinsDateTimeFormat::SupportedLocalesOf(ecmaRuntimeCallInfo);
111 TestHelper::TearDownFrame(thread, prev);
112
113 JSHandle<JSArray> resultHandle(thread, resultArr);
114 JSHandle<TaggedArray> elements(thread, resultHandle->GetElements());
115 EXPECT_EQ(elements->GetLength(), 1U);
116
117 JSHandle<EcmaString> resultStr(thread, elements->Get(0));
118 EXPECT_STREQ("id-u-co-pinyin-de", EcmaStringAccessor(resultStr).ToCString().c_str());
119 }
120
JSDateTime(JSThread * thread,JSTaggedValue & formatResult)121 static JSTaggedValue JSDateTime(JSThread *thread, JSTaggedValue &formatResult)
122 {
123 double days = 1665187200000;
124 JSHandle<JSFunction> jsFunction(thread, formatResult);
125 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(static_cast<double>(days)));
126 auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
127 ecmaRuntimeCallInfo2->SetFunction(jsFunction.GetTaggedValue());
128 ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined());
129 ecmaRuntimeCallInfo2->SetCallArg(0, value.GetTaggedValue());
130
131 [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
132 JSTaggedValue result2 = JSFunction::Call(ecmaRuntimeCallInfo2);
133 TestHelper::TearDownFrame(thread, prev2);
134 return result2;
135 }
136
JSDateTimeFormatConstructor(JSThread * thread,JSHandle<JSObject> & optionsObj,JSHandle<JSTaggedValue> & localesString)137 static JSTaggedValue JSDateTimeFormatConstructor(JSThread *thread, JSHandle<JSObject> &optionsObj,
138 JSHandle<JSTaggedValue> &localesString)
139 {
140 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
141 JSHandle<JSFunction> newTarget(env->GetDateTimeFormatFunction());
142
143 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 8);
144 ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
145 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
146 ecmaRuntimeCallInfo->SetCallArg(0, localesString.GetTaggedValue());
147 ecmaRuntimeCallInfo->SetCallArg(1, optionsObj.GetTaggedValue());
148
149 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
150 JSTaggedValue result = BuiltinsDateTimeFormat::DateTimeFormatConstructor(ecmaRuntimeCallInfo);
151 EXPECT_TRUE(result.IsJSDateTimeFormat());
152 TestHelper::TearDownFrame(thread, prev);
153 return result;
154 }
155
JSDateTimeFormatForObject(JSThread * thread,JSTaggedValue & constructorResult)156 static JSTaggedValue JSDateTimeFormatForObject(JSThread *thread, JSTaggedValue &constructorResult)
157 {
158 JSHandle<JSDateTimeFormat> jsDateTimeFormat = JSHandle<JSDateTimeFormat>(thread, constructorResult);
159 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
160 ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
161 ecmaRuntimeCallInfo1->SetThis(jsDateTimeFormat.GetTaggedValue());
162 ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue::Undefined());
163 [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
164 JSTaggedValue result1 = BuiltinsDateTimeFormat::Format(ecmaRuntimeCallInfo1);
165 TestHelper::TearDownFrame(thread, prev1);
166 return result1;
167 }
168
JSDateTimeFormatForObj_001(JSThread * thread)169 static JSTaggedValue JSDateTimeFormatForObj_001(JSThread *thread)
170 {
171 auto globalConst = thread->GlobalConstants();
172 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
173 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
174 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
175 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
176
177 JSHandle<JSTaggedValue> timeZoneName = globalConst->GetHandledTimeZoneNameString();
178 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-US"));
179 JSHandle<JSTaggedValue> digitValue(factory->NewFromASCII("2-digit"));
180 JSHandle<JSTaggedValue> timeZoneNameValue(factory->NewFromASCII("short"));
181
182 JSHandle<TaggedArray> keyArray = BuiltTestUtil::DateTimeGlobalSet(thread);
183 uint32_t arrayLen = keyArray->GetLength();
184 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
185 for (uint32_t i = 0; i < arrayLen; i++) {
186 key.Update(keyArray->Get(thread, i));
187 JSObject::SetProperty(thread, optionsObj, key, digitValue);
188 }
189 JSObject::SetProperty(thread, optionsObj, timeZoneName, timeZoneNameValue);
190 return optionsObj.GetTaggedValue();
191 }
192
TimeOffset()193 static int TimeOffset()
194 {
195 // Get Sys time
196 time_t rt = time(nullptr);
197 // Convert Sys time to GMT time
198 tm gtm = *gmtime(&rt);
199 // Convert GMT time to Sys time
200 time_t gt = mktime(>m);
201 tm gtm2 = *localtime(>);
202 // Calculate time difference
203 int offset = ((rt - gt) + (gtm2.tm_isdst ? 3600 : 0)) / 60;
204 return offset;
205 }
206
207 // DateTimeFormat_001
HWTEST_F_L0(BuiltinsDateTimeFormatTest,DateTimeFormat_001)208 HWTEST_F_L0(BuiltinsDateTimeFormatTest, DateTimeFormat_001)
209 {
210 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
211 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-US"));
212 auto jsObj = JSHandle<JSObject>(thread, JSDateTimeFormatForObj_001(thread));
213 auto constructorResult = JSDateTimeFormatConstructor(thread, jsObj, localesString);
214 auto formatResult = JSDateTimeFormatForObject(thread, constructorResult);
215 auto dtResult = JSDateTime(thread, formatResult);
216 JSHandle<EcmaString> resultStr(thread, dtResult);
217
218 constexpr int shanghai = 480;
219 constexpr int americaRegina = -360;
220 constexpr int americaNewYork = -300;
221 constexpr int sysDefaultTimezone = -180; // america_argentina_Buenos_Aires
222 constexpr int utc = 0;
223 auto cstr = EcmaStringAccessor(resultStr).ToCString();
224 if (TimeOffset() == utc) {
225 if (cstr.find("GMT") != std::string::npos) {
226 EXPECT_STREQ("10/08/22, 12:00:00 AM GMT", cstr.c_str());
227 }
228 if (cstr.find("UTC") != std::string::npos) {
229 EXPECT_STREQ("10/08/22, 12:00:00 AM UTC", cstr.c_str());
230 }
231 }
232 if (TimeOffset() == shanghai) {
233 if (cstr.find("CST") != std::string::npos) {
234 EXPECT_STREQ("10/08/22, 08:00:00 AM CST", cstr.c_str());
235 }
236 if (cstr.find("GMT+8") != std::string::npos) {
237 EXPECT_STREQ("10/08/22, 08:00:00 AM GMT+8", cstr.c_str());
238 }
239 }
240 if (TimeOffset() == americaRegina) {
241 if (cstr.find("CST") != std::string::npos) {
242 EXPECT_STREQ("10/07/22, 06:00:00 PM CST", cstr.c_str());
243 }
244 }
245 if (TimeOffset() == americaNewYork) {
246 if (cstr.find("EST") != std::string::npos) {
247 EXPECT_STREQ("10/07/22, 06:00:00 PM EST", cstr.c_str());
248 }
249 }
250 if (TimeOffset() == sysDefaultTimezone) {
251 if (cstr.find("GMT-3") != std::string::npos) {
252 EXPECT_STREQ("10/07/22, 09:00:00 PM GMT-3", cstr.c_str());
253 }
254 }
255 }
256
JSDateTimeFormatForObj_002(JSThread * thread)257 static JSTaggedValue JSDateTimeFormatForObj_002(JSThread *thread)
258 {
259 auto globalConst = thread->GlobalConstants();
260 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
261 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
262 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
263 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
264
265 JSHandle<JSTaggedValue> timeZoneName = globalConst->GetHandledTimeZoneNameString();
266 JSHandle<JSTaggedValue> timeZone = globalConst->GetHandledTimeZoneString();
267 JSHandle<JSTaggedValue> numicValue(factory->NewFromASCII("numeric"));
268 JSHandle<JSTaggedValue> digitValue(factory->NewFromASCII("2-digit"));
269 JSHandle<JSTaggedValue> longValue(factory->NewFromASCII("long"));
270 JSHandle<JSTaggedValue> timeZoneNameValue(factory->NewFromASCII("short"));
271 JSHandle<JSTaggedValue> timeZoneValue(factory->NewFromASCII("UTC"));
272
273 JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(6); // 6 : 6 length
274 keyArray->Set(thread, 0, globalConst->GetHandledYearString()); // 0 : 0 first position
275 keyArray->Set(thread, 1, globalConst->GetHandledMonthString()); // 1 : 1 second position
276 keyArray->Set(thread, 2, globalConst->GetHandledDayString()); // 2 : 2 third position
277 keyArray->Set(thread, 3, globalConst->GetHandledHourString()); // 3 : 3 fourth position
278 keyArray->Set(thread, 4, globalConst->GetHandledMinuteString()); // 4 : 4 fifth position
279 keyArray->Set(thread, 5, globalConst->GetHandledSecondString()); // 5 : 5 sixth position
280 uint32_t arrayLen = keyArray->GetLength();
281 uint32_t arrIndex[] = {0, 2};
282 uint32_t arrIndex2 = 1;
283 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
284 for (uint32_t i = 0; i < arrayLen; i++) {
285 key.Update(keyArray->Get(thread, i));
286 bool exists = std::count(std::begin(arrIndex), std::end(arrIndex), i) > 0;
287 if (exists) {
288 JSObject::SetProperty(thread, optionsObj, key, numicValue);
289 } else if (i == arrIndex2) {
290 JSObject::SetProperty(thread, optionsObj, key, longValue);
291 } else {
292 JSObject::SetProperty(thread, optionsObj, key, digitValue);
293 }
294 }
295 JSObject::SetProperty(thread, optionsObj, timeZoneName, timeZoneNameValue);
296 JSObject::SetProperty(thread, optionsObj, timeZone, timeZoneValue);
297 return optionsObj.GetTaggedValue();
298 }
299
300 // DateTimeFormat_002
HWTEST_F_L0(BuiltinsDateTimeFormatTest,DateTimeFormat_002)301 HWTEST_F_L0(BuiltinsDateTimeFormatTest, DateTimeFormat_002)
302 {
303 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
304 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("zh-CN"));
305 auto jsObj = JSHandle<JSObject>(thread, JSDateTimeFormatForObj_002(thread));
306 auto constructorResult = JSDateTimeFormatConstructor(thread, jsObj, localesString);
307 auto formatResult = JSDateTimeFormatForObject(thread, constructorResult);
308 auto dtResult = JSDateTime(thread, formatResult);
309 JSHandle<EcmaString> resultStr(thread, dtResult);
310 EXPECT_STREQ("2022年10月8日 UTC 上午12:00:00", EcmaStringAccessor(resultStr).ToCString().c_str());
311 }
312
313 // DateTimeFormat_003
HWTEST_F_L0(BuiltinsDateTimeFormatTest,DateTimeFormat_003)314 HWTEST_F_L0(BuiltinsDateTimeFormatTest, DateTimeFormat_003)
315 {
316 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
317 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("zh-CN"));
318
319 auto jsObj = JSHandle<JSObject>(thread, JSDateTimeFormatForObj_002(thread));
320 auto constructorResult = JSDateTimeFormatConstructor(thread, jsObj, localesString);
321 JSHandle<JSDateTimeFormat> jsDateTimeFormat = JSHandle<JSDateTimeFormat>(thread, constructorResult);
322 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
323 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
324 ecmaRuntimeCallInfo->SetThis(jsDateTimeFormat.GetTaggedValue());
325 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());
326
327 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
328 JSTaggedValue result = BuiltinsDateTimeFormat::FormatToParts(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(), 16U);
334 }
335
336 // DateTimeFormat_004
HWTEST_F_L0(BuiltinsDateTimeFormatTest,DateTimeFormat_004)337 HWTEST_F_L0(BuiltinsDateTimeFormatTest, DateTimeFormat_004)
338 {
339 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
340 JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("zh-CN"));
341 auto jsObj = JSHandle<JSObject>(thread, JSDateTimeFormatForObj_002(thread));
342 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
343 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
344 JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
345 JSHandle<JSTaggedValue> fullValue(factory->NewFromASCII("full"));
346 JSHandle<JSTaggedValue> falseValue(thread, JSTaggedValue(false));
347 JSHandle<JSTaggedValue> dateStyleValue(factory->NewFromASCII("dateStyle"));
348 JSHandle<JSTaggedValue> timeStyleeValue(factory->NewFromASCII("timeStyle"));
349 JSHandle<JSTaggedValue> hour12Value(factory->NewFromASCII("hour12"));
350 JSHandle<JSTaggedValue> timeZone(factory->NewFromASCII("timeZone"));
351 JSHandle<JSTaggedValue> timeZoneValue(factory->NewFromASCII("UTC"));
352 JSObject::SetProperty(thread, optionsObj, dateStyleValue, fullValue);
353 JSObject::SetProperty(thread, optionsObj, timeStyleeValue, fullValue);
354 JSObject::SetProperty(thread, optionsObj, hour12Value, falseValue);
355 JSObject::SetProperty(thread, optionsObj, timeZone, timeZoneValue);
356 auto constructorResult = JSDateTimeFormatConstructor(thread, optionsObj, localesString);
357 JSHandle<EcmaString> resultStr =
358 JSDateTimeFormat::FormatDateTime(thread, JSHandle<JSDateTimeFormat>(thread, constructorResult), 0.0);
359 EXPECT_STREQ("1970年1月1日星期四 协调世界时 00:00:00", EcmaStringAccessor(resultStr).ToCString().c_str());
360 }
361 } // namespace panda::test