Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 | |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 |
| 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 |
| 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 | |
| 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 |
| 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 | |
| 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; |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 156 | UBool dump = false; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 157 | #if defined (U_CAL_DEBUG) |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 158 | dump = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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) + |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 180 | ": Parse of " + prettify(string[i-1], false) + " failed."); |
| 181 | dump = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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]); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 192 | dump = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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]); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 202 | dump = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 + ")."); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 216 | dump = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 |
| 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 | |
| 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 | |
| 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 */ |