1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /***********************************************************************
4 * Copyright (c) 1997-2009, International Business Machines Corporation
5 * and others. All Rights Reserved.
6 ***********************************************************************/
7
8 #include "unicode/utypes.h"
9
10 #if !UCONFIG_NO_FORMATTING
11
12 #include "unicode/datefmt.h"
13 #include "unicode/smpdtfmt.h"
14 #include "tsdate.h"
15 #include "putilimp.h"
16 #include "cstring.h"
17
18 #include <float.h>
19 #include <stdlib.h>
20 #include <math.h>
21
~IntlTestDateFormat()22 IntlTestDateFormat::~IntlTestDateFormat() {}
23
24 /**
25 * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of
26 * DateFormat.
27 */
28 // par is ignored throughout this file
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)29 void IntlTestDateFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
30 {
31 if (exec) logln("TestSuite DateFormat");
32 switch (index) {
33 case 0: name = "GenericTest";
34 if (exec) {
35 logln(name);
36 fFormat = DateFormat::createInstance();
37 fTestName = "createInstance";
38 fLimit = 3;
39 testFormat(/* par */);
40 }
41 break;
42 case 1: name = "DefaultLocale";
43 if (exec) {
44 logln(name);
45 testLocale(/*par, */Locale::getDefault(), "Default Locale");
46 }
47 break;
48
49 case 2: name = "TestAvailableLocales";
50 if (exec) {
51 logln(name);
52 testAvailableLocales(/* par */);
53 }
54 break;
55
56 case 3: name = "MonsterTest";
57 if (exec) {
58 logln(name);
59 monsterTest(/*par*/);
60 }
61 break;
62
63 default: name = ""; break;
64 }
65 }
66
67 void
testLocale(const Locale & locale,const UnicodeString & localeName)68 IntlTestDateFormat::testLocale(/*char* par, */const Locale& locale, const UnicodeString& localeName)
69 {
70 DateFormat::EStyle timeStyle, dateStyle;
71
72 // For patterns including only time information and a timezone, it may take
73 // up to three iterations, since the timezone may shift as the year number
74 // is determined. For other patterns, 2 iterations should suffice.
75 fLimit = 3;
76
77 for(timeStyle = (DateFormat::EStyle)0;
78 timeStyle < (DateFormat::EStyle)4;
79 timeStyle = (DateFormat::EStyle) (timeStyle+1))
80 {
81 fTestName = (UnicodeString) "Time test " + (int32_t) timeStyle + " (" + localeName + ")";
82 fFormat = DateFormat::createTimeInstance(timeStyle, locale);
83 testFormat(/* par */);
84 }
85
86 fLimit = 2;
87
88 for(dateStyle = (DateFormat::EStyle)0;
89 dateStyle < (DateFormat::EStyle)4;
90 dateStyle = (DateFormat::EStyle) (dateStyle+1))
91 {
92 fTestName = (UnicodeString) "Date test " + (int32_t) dateStyle + " (" + localeName + ")";
93 fFormat = DateFormat::createDateInstance(dateStyle, locale);
94 testFormat(/* par */);
95 }
96
97 for(dateStyle = (DateFormat::EStyle)0;
98 dateStyle < (DateFormat::EStyle)4;
99 dateStyle = (DateFormat::EStyle) (dateStyle+1))
100 {
101 for(timeStyle = (DateFormat::EStyle)0;
102 timeStyle < (DateFormat::EStyle)4;
103 timeStyle = (DateFormat::EStyle) (timeStyle+1))
104 {
105 fTestName = (UnicodeString) "DateTime test " + (int32_t) dateStyle + "/" + (int32_t) timeStyle + " (" + localeName + ")";
106 fFormat = DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale);
107 testFormat(/* par */);
108 }
109 }
110 }
111
testFormat()112 void IntlTestDateFormat::testFormat(/* char* par */)
113 {
114 if (fFormat == 0)
115 {
116 dataerrln("FAIL: DateFormat creation failed");
117 return;
118 }
119
120 describeTest();
121
122 UDate now = Calendar::getNow();
123 tryDate(0);
124 tryDate(1278161801778.0);
125 tryDate(5264498352317.0); // Sunday, October 28, 2136 8:39:12 AM PST
126 tryDate(9516987689250.0); // In the year 2271
127 tryDate(now);
128 // Shift 6 months into the future, AT THE SAME TIME OF DAY.
129 // This will test the DST handling.
130 tryDate(now + 6.0*30*ONEDAY);
131
132 UDate limit = now * 10; // Arbitrary limit
133 for (int32_t i=0; i<3; ++i)
134 tryDate(uprv_floor(randDouble() * limit));
135
136 delete fFormat;
137 }
138
139 void
describeTest()140 IntlTestDateFormat::describeTest()
141 {
142 // Assume it's a SimpleDateFormat and get some info
143 SimpleDateFormat *s = (SimpleDateFormat*)fFormat;
144 UnicodeString str;
145 logln(fTestName + " Pattern " + s->toPattern(str));
146 }
147
tryDate(UDate theDate)148 void IntlTestDateFormat::tryDate(UDate theDate)
149 {
150 const int32_t DEPTH = 10;
151 UDate date[DEPTH];
152 UnicodeString string[DEPTH];
153
154 int32_t dateMatch = 0;
155 int32_t stringMatch = 0;
156 UBool dump = false;
157 #if defined (U_CAL_DEBUG)
158 dump = true;
159 #endif
160 int32_t i;
161
162 date[0] = theDate;
163 fFormat->format(theDate, string[0]);
164
165 UErrorCode status = U_ZERO_ERROR;
166 const char* locID = "??";
167 Locale loc = fFormat->getCalendar()->getLocale(ULOC_VALID_LOCALE, status);
168 if (U_SUCCESS(status)) {
169 locID = loc.getName();
170 }
171
172 for (i=1; i<DEPTH; ++i)
173 {
174 status = U_ZERO_ERROR;
175 date[i] = fFormat->parse(string[i-1], status);
176 if (U_FAILURE(status))
177 {
178 describeTest();
179 errln("**** FAIL, locale " + UnicodeString(locID,-1,US_INV) +
180 ": Parse of " + prettify(string[i-1], false) + " failed.");
181 dump = true;
182 break;
183 }
184 fFormat->format(date[i], string[i]);
185 if (dateMatch == 0 && date[i] == date[i-1])
186 dateMatch = i;
187 else if (dateMatch > 0 && date[i] != date[i-1])
188 {
189 describeTest();
190 errln("**** FAIL, locale " + UnicodeString(locID,-1,US_INV) +
191 ": Date mismatch after match for " + string[i]);
192 dump = true;
193 break;
194 }
195 if (stringMatch == 0 && string[i] == string[i-1])
196 stringMatch = i;
197 else if (stringMatch > 0 && string[i] != string[i-1])
198 {
199 describeTest();
200 errln("**** FAIL, locale " + UnicodeString(locID,-1,US_INV) +
201 ": String mismatch after match for " + string[i]);
202 dump = true;
203 break;
204 }
205 if (dateMatch > 0 && stringMatch > 0)
206 break;
207 }
208 if (i == DEPTH)
209 --i;
210
211 if (stringMatch > fLimit || dateMatch > fLimit)
212 {
213 describeTest();
214 errln((UnicodeString)"**** FAIL: No string and/or date match within " + fLimit
215 + " iterations for the Date " + string[0] + "\t(" + theDate + ").");
216 dump = true;
217 }
218
219 if (dump)
220 {
221 for (int32_t k=0; k<=i; ++k)
222 {
223 logln((UnicodeString)"" + k + ": " + date[k] + " F> " +
224 string[k] + " P> ");
225 }
226 }
227 }
228
229 // Return a random double from 0.01 to 1, inclusive
randDouble()230 double IntlTestDateFormat::randDouble()
231 {
232 // Assume 8-bit (or larger) rand values. Also assume
233 // that the system rand() function is very poor, which it always is.
234 double d=0.0;
235 uint32_t i;
236 char* poke = (char*)&d;
237 do {
238 do {
239 for (i=0; i < sizeof(double); ++i)
240 {
241 poke[i] = (char)(rand() & 0xFF);
242 }
243 } while (uprv_isNaN(d) || uprv_isInfinite(d));
244
245 if (d < 0.0)
246 d = -d;
247 if (d > 0.0)
248 {
249 double e = uprv_floor(log10(d));
250 if (e < -2.0)
251 d *= uprv_pow10((int32_t)(-e-2));
252 else if (e > -1.0)
253 d /= uprv_pow10((int32_t)(e+1));
254 }
255 // While this is not a real normalized number make another one.
256 } while (uprv_isNaN(d) || uprv_isInfinite(d)
257 || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d)));
258 return d;
259 }
260
testAvailableLocales()261 void IntlTestDateFormat::testAvailableLocales(/* char* par */)
262 {
263 int32_t count = 0;
264 const Locale* locales = DateFormat::getAvailableLocales(count);
265 logln((UnicodeString)"" + count + " available locales");
266 if (locales && count)
267 {
268 UnicodeString name;
269 UnicodeString all;
270 for (int32_t i=0; i<count; ++i)
271 {
272 if (i!=0) all += ", ";
273 all += locales[i].getName();
274 }
275 logln(all);
276 }
277 else dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer");
278 }
279
monsterTest()280 void IntlTestDateFormat::monsterTest(/*char *par*/)
281 {
282 int32_t count;
283 const Locale* locales = DateFormat::getAvailableLocales(count);
284 if (locales && count)
285 {
286 if (quick && count > 3) {
287 logln("quick test: testing just 3 locales!");
288 count = 3;
289 }
290 for (int32_t i=0; i<count; ++i)
291 {
292 UnicodeString name = UnicodeString(locales[i].getName(), "");
293 logln((UnicodeString)"Testing " + name + "...");
294 testLocale(/*par, */locales[i], name);
295 }
296 }
297 }
298
299 #endif /* #if !UCONFIG_NO_FORMATTING */
300