Home | History | Annotate | Download | only in intltest
      1 /***********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1997-2013, 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, icu::Calendar* cal, icu::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. (delta %.2lf greater than %.2lf)",
     65                 dt, millis, uprv_fabs(millis-dt), uprv_fabs(dt*kDrift));
     66           logln(UnicodeString("   ") + theDate + " " + CalendarTest::calToStr(*cal));
     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 Z, EEEE, MMMM d, yyyy 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 minutes
    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         {"dangi",           TRUE,       DEFAULT_START, DEFAULT_END},
    163         {"indian",          FALSE,      DEFAULT_START, DEFAULT_END},
    164         {"coptic",          FALSE,      DEFAULT_START, DEFAULT_END},
    165         {"ethiopic",        FALSE,      DEFAULT_START, DEFAULT_END},
    166         {"ethiopic-amete-alem", FALSE,  DEFAULT_START, DEFAULT_END},
    167         {NULL,              FALSE,      0, 0}
    168     };
    169 
    170     int16_t i = 0;
    171     char buf[64];
    172 
    173     for (i = 0; TestCases[i].type; i++) {
    174         UErrorCode status = U_ZERO_ERROR;
    175         uprv_strcpy(buf, "root@calendar=");
    176         strcat(buf, TestCases[i].type);
    177         Calendar *cal = Calendar::createInstance(buf, status);
    178         if (failure(status, "Calendar::createInstance", TRUE)) {
    179             continue;
    180         }
    181         if (uprv_strcmp(cal->getType(), TestCases[i].type) != 0) {
    182             errln((UnicodeString)"FAIL: Wrong calendar type: " + cal->getType()
    183                 + " Requested: " + TestCases[i].type);
    184             delete cal;
    185             continue;
    186         }
    187         // Do the test
    188         doTheoreticalLimitsTest(*cal, TestCases[i].hasLeapMonth);
    189         doLimitsTest(*cal, TestCases[i].actualTestStart,TestCases[i].actualTestEnd);
    190         delete cal;
    191     }
    192 }
    193 
    194 void
    195 CalendarLimitTest::doTheoreticalLimitsTest(Calendar& cal, UBool leapMonth) {
    196     const char* calType = cal.getType();
    197 
    198     int32_t nDOW = cal.getMaximum(UCAL_DAY_OF_WEEK);
    199     int32_t maxDOY = cal.getMaximum(UCAL_DAY_OF_YEAR);
    200     int32_t lmaxDOW = cal.getLeastMaximum(UCAL_DAY_OF_YEAR);
    201     int32_t maxWOY = cal.getMaximum(UCAL_WEEK_OF_YEAR);
    202     int32_t lmaxWOY = cal.getLeastMaximum(UCAL_WEEK_OF_YEAR);
    203     int32_t maxM = cal.getMaximum(UCAL_MONTH) + 1;
    204     int32_t lmaxM = cal.getLeastMaximum(UCAL_MONTH) + 1;
    205     int32_t maxDOM = cal.getMaximum(UCAL_DAY_OF_MONTH);
    206     int32_t lmaxDOM = cal.getLeastMaximum(UCAL_DAY_OF_MONTH);
    207     int32_t maxDOWIM = cal.getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
    208     int32_t lmaxDOWIM = cal.getLeastMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
    209     int32_t maxWOM = cal.getMaximum(UCAL_WEEK_OF_MONTH);
    210     int32_t lmaxWOM = cal.getLeastMaximum(UCAL_WEEK_OF_MONTH);
    211     int32_t minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek();
    212 
    213     // Day of year
    214     int32_t expected;
    215     if (!leapMonth) {
    216         expected = maxM*maxDOM;
    217         if (maxDOY > expected) {
    218             errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_YEAR is too big: "
    219                 + maxDOY + "/expected: <=" + expected);
    220         }
    221         expected = lmaxM*lmaxDOM;
    222         if (lmaxDOW < expected) {
    223             errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_YEAR is too small: "
    224                 + lmaxDOW + "/expected: >=" + expected);
    225         }
    226     }
    227 
    228     // Week of year
    229     expected = maxDOY/nDOW + 1;
    230     if (maxWOY > expected) {
    231         errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_YEAR is too big: "
    232             + maxWOY + "/expected: <=" + expected);
    233     }
    234     expected = lmaxDOW/nDOW;
    235     if (lmaxWOY < expected) {
    236         errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_YEAR is too small: "
    237             + lmaxWOY + "/expected >=" + expected);
    238     }
    239 
    240     // Day of week in month
    241     expected = (maxDOM + nDOW - 1)/nDOW;
    242     if (maxDOWIM != expected) {
    243         errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
    244             + maxDOWIM + "/expected: " + expected);
    245     }
    246     expected = (lmaxDOM + nDOW - 1)/nDOW;
    247     if (lmaxDOWIM != expected) {
    248         errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
    249             + lmaxDOWIM + "/expected: " + expected);
    250     }
    251 
    252     // Week of month
    253     expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW;
    254     if (maxWOM != expected) {
    255         errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_MONTH is incorrect: "
    256             + maxWOM + "/expected: " + expected);
    257     }
    258     expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW;
    259     if (lmaxWOM != expected) {
    260         errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_MONTH is incorrect: "
    261             + lmaxWOM + "/expected: " + expected);
    262     }
    263 }
    264 
    265 void
    266 CalendarLimitTest::doLimitsTest(Calendar& cal, UDate startDate, int32_t endTime) {
    267     int32_t testTime = quick ? ( endTime / 40 ) : endTime;
    268     doLimitsTest(cal, NULL /*default fields*/, startDate, testTime);
    269 }
    270 
    271 void
    272 CalendarLimitTest::doLimitsTest(Calendar& cal,
    273                                 const int32_t* fieldsToTest,
    274                                 UDate startDate,
    275                                 int32_t testDuration) {
    276     static const int32_t FIELDS[] = {
    277         UCAL_ERA,
    278         UCAL_YEAR,
    279         UCAL_MONTH,
    280         UCAL_WEEK_OF_YEAR,
    281         UCAL_WEEK_OF_MONTH,
    282         UCAL_DAY_OF_MONTH,
    283         UCAL_DAY_OF_YEAR,
    284         UCAL_DAY_OF_WEEK_IN_MONTH,
    285         UCAL_YEAR_WOY,
    286         UCAL_EXTENDED_YEAR,
    287         -1,
    288     };
    289 
    290     static const char* FIELD_NAME[] = {
    291         "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH",
    292         "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK",
    293         "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
    294         "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
    295         "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
    296         "JULIAN_DAY", "MILLISECONDS_IN_DAY",
    297         "IS_LEAP_MONTH"
    298     };
    299 
    300     UErrorCode status = U_ZERO_ERROR;
    301     int32_t i, j;
    302     UnicodeString ymd;
    303 
    304     GregorianCalendar greg(status);
    305     if (failure(status, "new GregorianCalendar")) {
    306         return;
    307     }
    308     greg.setTime(startDate, status);
    309     if (failure(status, "GregorianCalendar::setTime")) {
    310         return;
    311     }
    312     logln((UnicodeString)"Start: " + startDate);
    313 
    314     if (fieldsToTest == NULL) {
    315         fieldsToTest = FIELDS;
    316     }
    317 
    318 
    319     // Keep a record of minima and maxima that we actually see.
    320     // These are kept in an array of arrays of hashes.
    321     int32_t limits[UCAL_FIELD_COUNT][4];
    322     for (j = 0; j < UCAL_FIELD_COUNT; j++) {
    323         limits[j][0] = INT32_MAX;
    324         limits[j][1] = INT32_MIN;
    325         limits[j][2] = INT32_MAX;
    326         limits[j][3] = INT32_MIN;
    327     }
    328 
    329     // This test can run for a long time; show progress.
    330     UDate millis = ucal_getNow();
    331     UDate mark = millis + 5000; // 5 sec
    332     millis -= testDuration * 1000; // stop time if testDuration<0
    333 
    334     for (i = 0;
    335          testDuration > 0 ? i < testDuration
    336                         : ucal_getNow() < millis;
    337          ++i) {
    338         if (ucal_getNow() >= mark) {
    339             logln((UnicodeString)"(" + i + " days)");
    340             mark += 5000; // 5 sec
    341         }
    342         UDate testMillis = greg.getTime(status);
    343         cal.setTime(testMillis, status);
    344         cal.setMinimalDaysInFirstWeek(1);
    345         if (failure(status, "Calendar set/getTime")) {
    346             return;
    347         }
    348         for (j = 0; fieldsToTest[j] >= 0; ++j) {
    349             UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
    350             int32_t v = cal.get(f, status);
    351             int32_t minActual = cal.getActualMinimum(f, status);
    352             int32_t maxActual = cal.getActualMaximum(f, status);
    353             int32_t minLow = cal.getMinimum(f);
    354             int32_t minHigh = cal.getGreatestMinimum(f);
    355             int32_t maxLow = cal.getLeastMaximum(f);
    356             int32_t maxHigh = cal.getMaximum(f);
    357 
    358             if (limits[j][0] > minActual) {
    359                 // the minimum
    360                 limits[j][0] = minActual;
    361             }
    362             if (limits[j][1] < minActual) {
    363                 // the greatest minimum
    364                 limits[j][1] = minActual;
    365             }
    366             if (limits[j][2] > maxActual) {
    367                 // the least maximum
    368                 limits[j][2] = maxActual;
    369             }
    370             if (limits[j][3] < maxActual) {
    371                 // the maximum
    372                 limits[j][3] = maxActual;
    373             }
    374 
    375             if (minActual < minLow || minActual > minHigh) {
    376                 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
    377                       ymdToString(cal, ymd) +
    378                       " Range for min of " + FIELD_NAME[f] + "(" + f +
    379                       ")=" + minLow + ".." + minHigh +
    380                       ", actual_min=" + minActual);
    381             }
    382             if (maxActual < maxLow || maxActual > maxHigh) {
    383                 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
    384                       ymdToString(cal, ymd) +
    385                       " Range for max of " + FIELD_NAME[f] + "(" + f +
    386                       ")=" + maxLow + ".." + maxHigh +
    387                       ", actual_max=" + maxActual);
    388             }
    389             if (v < minActual || v > maxActual) {
    390                 // timebomb per #9967, fix with #9972
    391                 if ( uprv_strcmp(cal.getType(), "dangi") == 0 &&
    392                         testMillis >= 1865635198000.0  &&
    393                      logKnownIssue("9972", "as per #9967")) { // Feb 2029 gregorian, end of dangi 4361
    394                     logln((UnicodeString)"Fail: [" + cal.getType() + "] " +
    395                           ymdToString(cal, ymd) +
    396                           " " + FIELD_NAME[f] + "(" + f + ")=" + v +
    397                           ", actual=" + minActual + ".." + maxActual +
    398                           ", allowed=(" + minLow + ".." + minHigh + ")..(" +
    399                           maxLow + ".." + maxHigh + ")");
    400                 } else {
    401                     errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
    402                           ymdToString(cal, ymd) +
    403                           " " + FIELD_NAME[f] + "(" + f + ")=" + v +
    404                           ", actual=" + minActual + ".." + maxActual +
    405                           ", allowed=(" + minLow + ".." + minHigh + ")..(" +
    406                           maxLow + ".." + maxHigh + ")");
    407                 }
    408             }
    409         }
    410         greg.add(UCAL_DAY_OF_YEAR, 1, status);
    411         if (failure(status, "Calendar::add")) {
    412             return;
    413         }
    414     }
    415 
    416     // Check actual maxima and minima seen against ranges returned
    417     // by API.
    418     UnicodeString buf;
    419     for (j = 0; fieldsToTest[j] >= 0; ++j) {
    420         int32_t rangeLow, rangeHigh;
    421         UBool fullRangeSeen = TRUE;
    422         UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
    423 
    424         buf.remove();
    425         buf.append((UnicodeString)"[" + cal.getType() + "] " + FIELD_NAME[f]);
    426 
    427         // Minumum
    428         rangeLow = cal.getMinimum(f);
    429         rangeHigh = cal.getGreatestMinimum(f);
    430         if (limits[j][0] != rangeLow || limits[j][1] != rangeHigh) {
    431             fullRangeSeen = FALSE;
    432         }
    433         buf.append((UnicodeString)" minima range=" + rangeLow + ".." + rangeHigh);
    434         buf.append((UnicodeString)" minima actual=" + limits[j][0] + ".." + limits[j][1]);
    435 
    436         // Maximum
    437         rangeLow = cal.getLeastMaximum(f);
    438         rangeHigh = cal.getMaximum(f);
    439         if (limits[j][2] != rangeLow || limits[j][3] != rangeHigh) {
    440             fullRangeSeen = FALSE;
    441         }
    442         buf.append((UnicodeString)" maxima range=" + rangeLow + ".." + rangeHigh);
    443         buf.append((UnicodeString)" maxima actual=" + limits[j][2] + ".." + limits[j][3]);
    444 
    445         if (fullRangeSeen) {
    446             logln((UnicodeString)"OK: " + buf);
    447         } else {
    448             // This may or may not be an error -- if the range of dates
    449             // we scan over doesn't happen to contain a minimum or
    450             // maximum, it doesn't mean some other range won't.
    451             logln((UnicodeString)"Warning: " + buf);
    452         }
    453     }
    454 
    455     logln((UnicodeString)"End: " + greg.getTime(status));
    456 }
    457 
    458 UnicodeString&
    459 CalendarLimitTest::ymdToString(const Calendar& cal, UnicodeString& str) {
    460     UErrorCode status = U_ZERO_ERROR;
    461     str.remove();
    462     str.append((UnicodeString)"" + cal.get(UCAL_EXTENDED_YEAR, status)
    463         + "/" + (cal.get(UCAL_MONTH, status) + 1)
    464         + (cal.get(UCAL_IS_LEAP_MONTH, status) == 1 ? "(leap)" : "")
    465         + "/" + cal.get(UCAL_DATE, status)
    466         + " " + cal.get(UCAL_HOUR_OF_DAY, status)
    467         + ":" + cal.get(UCAL_MINUTE, status)
    468         + " zone(hrs) " + cal.get(UCAL_ZONE_OFFSET, status)/(60.0*60.0*1000.0)
    469         + " dst(hrs) " + cal.get(UCAL_DST_OFFSET, status)/(60.0*60.0*1000.0)
    470         + ", time(millis)=" + cal.getTime(status));
    471     return str;
    472 }
    473 
    474 #endif /* #if !UCONFIG_NO_FORMATTING */
    475 
    476 // eof
    477