]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/callimts.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / callimts.cpp
1 /***********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1997-2015, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ***********************************************************************/
6
7 #include "unicode/utypes.h"
8
9 #if !UCONFIG_NO_FORMATTING
10
11 #include "callimts.h"
12 #include "caltest.h"
13 #include "unicode/calendar.h"
14 #include "unicode/gregocal.h"
15 #include "unicode/datefmt.h"
16 #include "unicode/smpdtfmt.h"
17 #include "cstring.h"
18 #include "mutex.h"
19 #include "putilimp.h"
20 #include "simplethread.h"
21
22 U_NAMESPACE_USE
23 void CalendarLimitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
24 {
25 if (exec) logln("TestSuite TestCalendarLimit");
26 switch (index) {
27 // Re-enable this later
28 case 0:
29 name = "TestCalendarExtremeLimit";
30 if (exec) {
31 logln("TestCalendarExtremeLimit---"); logln("");
32 TestCalendarExtremeLimit();
33 }
34 break;
35 case 1:
36 name = "TestLimits";
37 if (exec) {
38 logln("TestLimits---"); logln("");
39 TestLimits();
40 }
41 break;
42
43 default: name = ""; break;
44 }
45 }
46
47
48 // *****************************************************************************
49 // class CalendarLimitTest
50 // *****************************************************************************
51
52 // -------------------------------------
53 void
54 CalendarLimitTest::test(UDate millis, icu::Calendar* cal, icu::DateFormat* fmt)
55 {
56 static const UDate kDrift = 1e-10;
57 UErrorCode exception = U_ZERO_ERROR;
58 UnicodeString theDate;
59 UErrorCode status = U_ZERO_ERROR;
60 cal->setTime(millis, exception);
61 if (U_SUCCESS(exception)) {
62 fmt->format(millis, theDate);
63 UDate dt = fmt->parse(theDate, status);
64 // allow a small amount of error (drift)
65 if(! withinErr(dt, millis, kDrift)) {
66 errln("FAIL:round trip for large milli, got: %.1lf wanted: %.1lf. (delta %.2lf greater than %.2lf)",
67 dt, millis, uprv_fabs(millis-dt), uprv_fabs(dt*kDrift));
68 logln(UnicodeString(" ") + theDate + " " + CalendarTest::calToStr(*cal));
69 } else {
70 logln(UnicodeString("OK: got ") + dt + ", wanted " + millis);
71 logln(UnicodeString(" ") + theDate);
72 }
73 }
74 }
75
76 // -------------------------------------
77
78 // bug 986c: deprecate nextDouble/previousDouble
79 //|double
80 //|CalendarLimitTest::nextDouble(double a)
81 //|{
82 //| return uprv_nextDouble(a, TRUE);
83 //|}
84 //|
85 //|double
86 //|CalendarLimitTest::previousDouble(double a)
87 //|{
88 //| return uprv_nextDouble(a, FALSE);
89 //|}
90
91 UBool
92 CalendarLimitTest::withinErr(double a, double b, double err)
93 {
94 return ( uprv_fabs(a - b) < uprv_fabs(a * err) );
95 }
96
97 void
98 CalendarLimitTest::TestCalendarExtremeLimit()
99 {
100 UErrorCode status = U_ZERO_ERROR;
101 Calendar *cal = Calendar::createInstance(status);
102 if (failure(status, "Calendar::createInstance", TRUE)) return;
103 cal->adoptTimeZone(TimeZone::createTimeZone("GMT"));
104 DateFormat *fmt = DateFormat::createDateTimeInstance();
105 if(!fmt || !cal) {
106 dataerrln("can't open cal and/or fmt");
107 return;
108 }
109 fmt->adoptCalendar(cal);
110 ((SimpleDateFormat*) fmt)->applyPattern("HH:mm:ss.SSS Z, EEEE, MMMM d, yyyy G");
111
112
113 // This test used to test the algorithmic limits of the dates that
114 // GregorianCalendar could handle. However, the algorithm has
115 // been rewritten completely since then and the prior limits no
116 // longer apply. Instead, we now do basic round-trip testing of
117 // some extreme (but still manageable) dates.
118 UDate m;
119 logln("checking 1e16..1e17");
120 for ( m = 1e16; m < 1e17; m *= 1.1) {
121 test(m, cal, fmt);
122 }
123 logln("checking -1e14..-1e15");
124 for ( m = -1e14; m > -1e15; m *= 1.1) {
125 test(m, cal, fmt);
126 }
127
128 // This is 2^52 - 1, the largest allowable mantissa with a 0
129 // exponent in a 64-bit double
130 UDate VERY_EARLY_MILLIS = - 4503599627370495.0;
131 UDate VERY_LATE_MILLIS = 4503599627370495.0;
132
133 // I am removing the previousDouble and nextDouble calls below for
134 // two reasons: 1. As part of jitterbug 986, I am deprecating
135 // these methods and removing calls to them. 2. This test is a
136 // non-critical boundary behavior test.
137 test(VERY_EARLY_MILLIS, cal, fmt);
138 //test(previousDouble(VERY_EARLY_MILLIS), cal, fmt);
139 test(VERY_LATE_MILLIS, cal, fmt);
140 //test(nextDouble(VERY_LATE_MILLIS), cal, fmt);
141 delete fmt;
142 }
143
144 namespace {
145
146 struct TestCase {
147 const char *type;
148 UBool hasLeapMonth;
149 UDate actualTestStart;
150 int32_t actualTestEnd;
151 };
152
153 const UDate DEFAULT_START = 944006400000.0; // 1999-12-01T00:00Z
154 const int32_t DEFAULT_END = -120; // Default for non-quick is run 2 minutes
155
156 TestCase TestCases[] = {
157 {"gregorian", FALSE, DEFAULT_START, DEFAULT_END},
158 {"japanese", FALSE, 596937600000.0, DEFAULT_END}, // 1988-12-01T00:00Z, Showa 63
159 {"buddhist", FALSE, DEFAULT_START, DEFAULT_END},
160 {"roc", FALSE, DEFAULT_START, DEFAULT_END},
161 {"persian", FALSE, DEFAULT_START, DEFAULT_END},
162 {"islamic-civil", FALSE, DEFAULT_START, DEFAULT_END},
163 {"islamic", FALSE, DEFAULT_START, 800000}, // Approx. 2250 years from now, after which
164 // some rounding errors occur in Islamic calendar
165 {"hebrew", TRUE, DEFAULT_START, DEFAULT_END},
166 {"chinese", TRUE, DEFAULT_START, DEFAULT_END},
167 {"dangi", TRUE, DEFAULT_START, DEFAULT_END},
168 {"indian", FALSE, DEFAULT_START, DEFAULT_END},
169 {"coptic", FALSE, DEFAULT_START, DEFAULT_END},
170 {"ethiopic", FALSE, DEFAULT_START, DEFAULT_END},
171 {"ethiopic-amete-alem", FALSE, DEFAULT_START, DEFAULT_END}
172 };
173
174 struct {
175 int32_t fIndex;
176 UBool next (int32_t &rIndex) {
177 Mutex lock;
178 if (fIndex >= UPRV_LENGTHOF(TestCases)) {
179 return FALSE;
180 }
181 rIndex = fIndex++;
182 return TRUE;
183 }
184 void reset() {
185 fIndex = 0;
186 }
187 } gTestCaseIterator;
188
189 } // anonymous name space
190
191 void
192 CalendarLimitTest::TestLimits(void) {
193 gTestCaseIterator.reset();
194
195 ThreadPool<CalendarLimitTest> threads(this, threadCount, &CalendarLimitTest::TestLimitsThread);
196 threads.start();
197 threads.join();
198 }
199
200
201 void CalendarLimitTest::TestLimitsThread(int32_t threadNum) {
202 logln("thread %d starting", threadNum);
203 int32_t testIndex = 0;
204 LocalPointer<Calendar> cal;
205 while (gTestCaseIterator.next(testIndex)) {
206 TestCase &testCase = TestCases[testIndex];
207 logln("begin test of %s calendar.", testCase.type);
208 UErrorCode status = U_ZERO_ERROR;
209 char buf[64];
210 uprv_strcpy(buf, "root@calendar=");
211 strcat(buf, testCase.type);
212 cal.adoptInstead(Calendar::createInstance(buf, status));
213 if (failure(status, "Calendar::createInstance", TRUE)) {
214 continue;
215 }
216 if (uprv_strcmp(cal->getType(), testCase.type) != 0) {
217 errln((UnicodeString)"FAIL: Wrong calendar type: " + cal->getType()
218 + " Requested: " + testCase.type);
219 continue;
220 }
221 doTheoreticalLimitsTest(*(cal.getAlias()), testCase.hasLeapMonth);
222 doLimitsTest(*(cal.getAlias()), testCase.actualTestStart, testCase.actualTestEnd);
223 logln("end test of %s calendar.", testCase.type);
224 }
225 }
226
227
228 void
229 CalendarLimitTest::doTheoreticalLimitsTest(Calendar& cal, UBool leapMonth) {
230 const char* calType = cal.getType();
231
232 int32_t nDOW = cal.getMaximum(UCAL_DAY_OF_WEEK);
233 int32_t maxDOY = cal.getMaximum(UCAL_DAY_OF_YEAR);
234 int32_t lmaxDOW = cal.getLeastMaximum(UCAL_DAY_OF_YEAR);
235 int32_t maxWOY = cal.getMaximum(UCAL_WEEK_OF_YEAR);
236 int32_t lmaxWOY = cal.getLeastMaximum(UCAL_WEEK_OF_YEAR);
237 int32_t maxM = cal.getMaximum(UCAL_MONTH) + 1;
238 int32_t lmaxM = cal.getLeastMaximum(UCAL_MONTH) + 1;
239 int32_t maxDOM = cal.getMaximum(UCAL_DAY_OF_MONTH);
240 int32_t lmaxDOM = cal.getLeastMaximum(UCAL_DAY_OF_MONTH);
241 int32_t maxDOWIM = cal.getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
242 int32_t lmaxDOWIM = cal.getLeastMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
243 int32_t maxWOM = cal.getMaximum(UCAL_WEEK_OF_MONTH);
244 int32_t lmaxWOM = cal.getLeastMaximum(UCAL_WEEK_OF_MONTH);
245 int32_t minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek();
246
247 // Day of year
248 int32_t expected;
249 if (!leapMonth) {
250 expected = maxM*maxDOM;
251 if (maxDOY > expected) {
252 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_YEAR is too big: "
253 + maxDOY + "/expected: <=" + expected);
254 }
255 expected = lmaxM*lmaxDOM;
256 if (lmaxDOW < expected) {
257 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_YEAR is too small: "
258 + lmaxDOW + "/expected: >=" + expected);
259 }
260 }
261
262 // Week of year
263 expected = maxDOY/nDOW + 1;
264 if (maxWOY > expected) {
265 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_YEAR is too big: "
266 + maxWOY + "/expected: <=" + expected);
267 }
268 expected = lmaxDOW/nDOW;
269 if (lmaxWOY < expected) {
270 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_YEAR is too small: "
271 + lmaxWOY + "/expected >=" + expected);
272 }
273
274 // Day of week in month
275 expected = (maxDOM + nDOW - 1)/nDOW;
276 if (maxDOWIM != expected) {
277 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
278 + maxDOWIM + "/expected: " + expected);
279 }
280 expected = (lmaxDOM + nDOW - 1)/nDOW;
281 if (lmaxDOWIM != expected) {
282 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
283 + lmaxDOWIM + "/expected: " + expected);
284 }
285
286 // Week of month
287 expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW;
288 if (maxWOM != expected) {
289 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_MONTH is incorrect: "
290 + maxWOM + "/expected: " + expected);
291 }
292 expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW;
293 if (lmaxWOM != expected) {
294 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_MONTH is incorrect: "
295 + lmaxWOM + "/expected: " + expected);
296 }
297 }
298
299 void
300 CalendarLimitTest::doLimitsTest(Calendar& cal, UDate startDate, int32_t endTime) {
301 int32_t testTime = quick ? ( endTime / 40 ) : endTime;
302 doLimitsTest(cal, NULL /*default fields*/, startDate, testTime);
303 }
304
305 void
306 CalendarLimitTest::doLimitsTest(Calendar& cal,
307 const int32_t* fieldsToTest,
308 UDate startDate,
309 int32_t testDuration) {
310 static const int32_t FIELDS[] = {
311 UCAL_ERA,
312 UCAL_YEAR,
313 UCAL_MONTH,
314 UCAL_WEEK_OF_YEAR,
315 UCAL_WEEK_OF_MONTH,
316 UCAL_DAY_OF_MONTH,
317 UCAL_DAY_OF_YEAR,
318 UCAL_DAY_OF_WEEK_IN_MONTH,
319 UCAL_YEAR_WOY,
320 UCAL_EXTENDED_YEAR,
321 -1,
322 };
323
324 static const char* FIELD_NAME[] = {
325 "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH",
326 "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK",
327 "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
328 "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
329 "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
330 "JULIAN_DAY", "MILLISECONDS_IN_DAY",
331 "IS_LEAP_MONTH"
332 };
333
334 UErrorCode status = U_ZERO_ERROR;
335 int32_t i, j;
336 UnicodeString ymd;
337
338 GregorianCalendar greg(status);
339 if (failure(status, "new GregorianCalendar")) {
340 return;
341 }
342 greg.setTime(startDate, status);
343 if (failure(status, "GregorianCalendar::setTime")) {
344 return;
345 }
346 logln((UnicodeString)"Start: " + startDate);
347
348 if (fieldsToTest == NULL) {
349 fieldsToTest = FIELDS;
350 }
351
352
353 // Keep a record of minima and maxima that we actually see.
354 // These are kept in an array of arrays of hashes.
355 int32_t limits[UCAL_FIELD_COUNT][4];
356 for (j = 0; j < UCAL_FIELD_COUNT; j++) {
357 limits[j][0] = INT32_MAX;
358 limits[j][1] = INT32_MIN;
359 limits[j][2] = INT32_MAX;
360 limits[j][3] = INT32_MIN;
361 }
362
363 // This test can run for a long time; show progress.
364 UDate millis = ucal_getNow();
365 UDate mark = millis + 5000; // 5 sec
366 millis -= testDuration * 1000; // stop time if testDuration<0
367
368 for (i = 0;
369 testDuration > 0 ? i < testDuration
370 : ucal_getNow() < millis;
371 ++i) {
372 if (ucal_getNow() >= mark) {
373 logln((UnicodeString)"(" + i + " days)");
374 mark += 5000; // 5 sec
375 }
376 UDate testMillis = greg.getTime(status);
377
378 if (testMillis == 2768943600000.0) {
379 // unusual failure, get day of month 0.
380 // doesn't happen with runConfigure and make check inside
381 // icuSources directory (i.e. using ICU build without wrapper makefile)
382 continue;
383 }
384
385 cal.setTime(testMillis, status);
386 cal.setMinimalDaysInFirstWeek(1);
387 if (failure(status, "Calendar set/getTime")) {
388 return;
389 }
390 for (j = 0; fieldsToTest[j] >= 0; ++j) {
391 UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
392 int32_t v = cal.get(f, status);
393 int32_t minActual = cal.getActualMinimum(f, status);
394 int32_t maxActual = cal.getActualMaximum(f, status);
395 int32_t minLow = cal.getMinimum(f);
396 int32_t minHigh = cal.getGreatestMinimum(f);
397 int32_t maxLow = cal.getLeastMaximum(f);
398 int32_t maxHigh = cal.getMaximum(f);
399
400 if (limits[j][0] > minActual) {
401 // the minimum
402 limits[j][0] = minActual;
403 }
404 if (limits[j][1] < minActual) {
405 // the greatest minimum
406 limits[j][1] = minActual;
407 }
408 if (limits[j][2] > maxActual) {
409 // the least maximum
410 limits[j][2] = maxActual;
411 }
412 if (limits[j][3] < maxActual) {
413 // the maximum
414 limits[j][3] = maxActual;
415 }
416
417 if (minActual < minLow || minActual > minHigh) {
418 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
419 ymdToString(cal, ymd) +
420 " Range for min of " + FIELD_NAME[f] + "(" + f +
421 ")=" + minLow + ".." + minHigh +
422 ", actual_min=" + minActual);
423 }
424 if (maxActual < maxLow || maxActual > maxHigh) {
425 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
426 ymdToString(cal, ymd) +
427 " Range for max of " + FIELD_NAME[f] + "(" + f +
428 ")=" + maxLow + ".." + maxHigh +
429 ", actual_max=" + maxActual);
430 }
431 if (v < minActual || v > maxActual) {
432 // timebomb per #9967, fix with #9972
433 if ( uprv_strcmp(cal.getType(), "dangi") == 0 &&
434 testMillis >= 1865635198000.0 &&
435 logKnownIssue("9972", "as per #9967")) { // Feb 2029 gregorian, end of dangi 4361
436 logln((UnicodeString)"Fail: [" + cal.getType() + "] " +
437 ymdToString(cal, ymd) +
438 " " + FIELD_NAME[f] + "(" + f + ")=" + v +
439 ", actual=" + minActual + ".." + maxActual +
440 ", allowed=(" + minLow + ".." + minHigh + ")..(" +
441 maxLow + ".." + maxHigh + ")");
442 } else {
443 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
444 ymdToString(cal, ymd) +
445 " " + FIELD_NAME[f] + "(" + f + ")=" + v +
446 ", actual=" + minActual + ".." + maxActual +
447 ", allowed=(" + minLow + ".." + minHigh + ")..(" +
448 maxLow + ".." + maxHigh + ")");
449 }
450 }
451 }
452 greg.add(UCAL_DAY_OF_YEAR, 1, status);
453 if (failure(status, "Calendar::add")) {
454 return;
455 }
456 }
457
458 // Check actual maxima and minima seen against ranges returned
459 // by API.
460 UnicodeString buf;
461 for (j = 0; fieldsToTest[j] >= 0; ++j) {
462 int32_t rangeLow, rangeHigh;
463 UBool fullRangeSeen = TRUE;
464 UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
465
466 buf.remove();
467 buf.append((UnicodeString)"[" + cal.getType() + "] " + FIELD_NAME[f]);
468
469 // Minumum
470 rangeLow = cal.getMinimum(f);
471 rangeHigh = cal.getGreatestMinimum(f);
472 if (limits[j][0] != rangeLow || limits[j][1] != rangeHigh) {
473 fullRangeSeen = FALSE;
474 }
475 buf.append((UnicodeString)" minima range=" + rangeLow + ".." + rangeHigh);
476 buf.append((UnicodeString)" minima actual=" + limits[j][0] + ".." + limits[j][1]);
477
478 // Maximum
479 rangeLow = cal.getLeastMaximum(f);
480 rangeHigh = cal.getMaximum(f);
481 if (limits[j][2] != rangeLow || limits[j][3] != rangeHigh) {
482 fullRangeSeen = FALSE;
483 }
484 buf.append((UnicodeString)" maxima range=" + rangeLow + ".." + rangeHigh);
485 buf.append((UnicodeString)" maxima actual=" + limits[j][2] + ".." + limits[j][3]);
486
487 if (fullRangeSeen) {
488 logln((UnicodeString)"OK: " + buf);
489 } else {
490 // This may or may not be an error -- if the range of dates
491 // we scan over doesn't happen to contain a minimum or
492 // maximum, it doesn't mean some other range won't.
493 logln((UnicodeString)"Warning: " + buf);
494 }
495 }
496
497 logln((UnicodeString)"End: " + greg.getTime(status));
498 }
499
500 UnicodeString&
501 CalendarLimitTest::ymdToString(const Calendar& cal, UnicodeString& str) {
502 UErrorCode status = U_ZERO_ERROR;
503 str.remove();
504 str.append((UnicodeString)"" + cal.get(UCAL_EXTENDED_YEAR, status)
505 + "/" + (cal.get(UCAL_MONTH, status) + 1)
506 + (cal.get(UCAL_IS_LEAP_MONTH, status) == 1 ? "(leap)" : "")
507 + "/" + cal.get(UCAL_DATE, status)
508 + " " + cal.get(UCAL_HOUR_OF_DAY, status)
509 + ":" + cal.get(UCAL_MINUTE, status)
510 + " zone(hrs) " + cal.get(UCAL_ZONE_OFFSET, status)/(60.0*60.0*1000.0)
511 + " dst(hrs) " + cal.get(UCAL_DST_OFFSET, status)/(60.0*60.0*1000.0)
512 + ", time(millis)=" + cal.getTime(status));
513 return str;
514 }
515
516 #endif /* #if !UCONFIG_NO_FORMATTING */
517
518 // eof