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