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