OLD | NEW |
(Empty) | |
| 1 /*********************************************************************** |
| 2 * COPYRIGHT: |
| 3 * Copyright (c) 1997-2009, 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 "unicode/decimfmt.h" |
| 12 #include "tsnmfmt.h" |
| 13 #include "putilimp.h" |
| 14 #include <float.h> |
| 15 #include <stdlib.h> |
| 16 |
| 17 IntlTestNumberFormat::~IntlTestNumberFormat() {} |
| 18 |
| 19 static const char * formattableTypeName(Formattable::Type t) |
| 20 { |
| 21 switch(t) { |
| 22 case Formattable::kDate: return "kDate"; |
| 23 case Formattable::kDouble: return "kDouble"; |
| 24 case Formattable::kLong: return "kLong"; |
| 25 case Formattable::kString: return "kString"; |
| 26 case Formattable::kArray: return "kArray"; |
| 27 case Formattable::kInt64: return "kInt64"; |
| 28 default: return "??unknown??"; |
| 29 } |
| 30 } |
| 31 |
| 32 /** |
| 33 * This test does round-trip testing (format -> parse -> format -> parse -> etc.
) of |
| 34 * NumberFormat. |
| 35 */ |
| 36 void IntlTestNumberFormat::runIndexedTest( int32_t index, UBool exec, const char
* &name, char* /*par*/ ) |
| 37 { |
| 38 |
| 39 if (exec) logln((UnicodeString)"TestSuite NumberFormat"); |
| 40 switch (index) { |
| 41 case 0: name = "createInstance"; |
| 42 if (exec) |
| 43 { |
| 44 logln(name); |
| 45 fStatus = U_ZERO_ERROR; |
| 46 fFormat = NumberFormat::createInstance(fStatus); |
| 47 testFormat(/*par*/); |
| 48 } |
| 49 break; |
| 50 |
| 51 case 1: name = "DefaultLocale"; |
| 52 if (exec) testLocale(/*par, */Locale::getDefault(), name); |
| 53 break; |
| 54 |
| 55 case 2: name = "testAvailableLocales"; |
| 56 if (exec) { |
| 57 logln(name); |
| 58 testAvailableLocales(/*par*/); |
| 59 } |
| 60 break; |
| 61 |
| 62 case 3: name = "monsterTest"; |
| 63 if (exec) { |
| 64 logln(name); |
| 65 monsterTest(/*par*/); |
| 66 } |
| 67 break; |
| 68 |
| 69 default: name = ""; break; |
| 70 } |
| 71 } |
| 72 |
| 73 void |
| 74 IntlTestNumberFormat::testLocale(/* char* par, */const Locale& locale, const Uni
codeString& localeName) |
| 75 { |
| 76 const char* name; |
| 77 |
| 78 fLocale = locale; |
| 79 name = "Number test"; |
| 80 logln((UnicodeString)name + " (" + localeName + ")"); |
| 81 fStatus = U_ZERO_ERROR; |
| 82 fFormat = NumberFormat::createInstance(locale, fStatus); |
| 83 testFormat(/* par */); |
| 84 |
| 85 name = "Currency test"; |
| 86 logln((UnicodeString)name + " (" + localeName + ")"); |
| 87 fStatus = U_ZERO_ERROR; |
| 88 fFormat = NumberFormat::createCurrencyInstance(locale, fStatus); |
| 89 testFormat(/* par */); |
| 90 |
| 91 name = "Percent test"; |
| 92 logln((UnicodeString)name + " (" + localeName + ")"); |
| 93 fStatus = U_ZERO_ERROR; |
| 94 fFormat = NumberFormat::createPercentInstance(locale, fStatus); |
| 95 testFormat(/* par */); |
| 96 } |
| 97 |
| 98 double IntlTestNumberFormat::randDouble() |
| 99 { |
| 100 // Assume 8-bit (or larger) rand values. Also assume |
| 101 // that the system rand() function is very poor, which it always is. |
| 102 // Call srand(currentTime) in intltest to make it truly random. |
| 103 double d; |
| 104 uint32_t i; |
| 105 char* poke = (char*)&d; |
| 106 do { |
| 107 for (i=0; i < sizeof(double); ++i) |
| 108 { |
| 109 poke[i] = (char)(rand() & 0xFF); |
| 110 } |
| 111 } while (uprv_isNaN(d) || uprv_isInfinite(d) |
| 112 || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d))); |
| 113 |
| 114 return d; |
| 115 } |
| 116 |
| 117 /* |
| 118 * Return a random uint32_t |
| 119 **/ |
| 120 uint32_t IntlTestNumberFormat::randLong() |
| 121 { |
| 122 // Assume 8-bit (or larger) rand values. Also assume |
| 123 // that the system rand() function is very poor, which it always is. |
| 124 // Call srand(currentTime) in intltest to make it truly random. |
| 125 uint32_t d; |
| 126 uint32_t i; |
| 127 char* poke = (char*)&d; |
| 128 for (i=0; i < sizeof(uint32_t); ++i) |
| 129 { |
| 130 poke[i] = (char)(rand() & 0xFF); |
| 131 } |
| 132 return d; |
| 133 } |
| 134 |
| 135 |
| 136 /* Make sure that we don't get something too large and multiply into infinity. |
| 137 @param smallerThanMax the requested maximum value smaller than DBL_MAX */ |
| 138 double IntlTestNumberFormat::getSafeDouble(double smallerThanMax) { |
| 139 double it; |
| 140 double high = (DBL_MAX/smallerThanMax)/10.0; |
| 141 double low = -high; |
| 142 do { |
| 143 it = randDouble(); |
| 144 } while (low > it || it > high); |
| 145 return it; |
| 146 } |
| 147 |
| 148 void |
| 149 IntlTestNumberFormat::testFormat(/* char* par */) |
| 150 { |
| 151 if (U_FAILURE(fStatus)) |
| 152 { |
| 153 dataerrln((UnicodeString)"**** FAIL: createXxxInstance failed. - " + u_e
rrorName(fStatus)); |
| 154 if (fFormat != 0) |
| 155 errln("**** FAIL: Non-null format returned by createXxxInstance upon
failure."); |
| 156 delete fFormat; |
| 157 fFormat = 0; |
| 158 return; |
| 159 } |
| 160 |
| 161 if (fFormat == 0) |
| 162 { |
| 163 errln((UnicodeString)"**** FAIL: Null format returned by createXxxInstan
ce."); |
| 164 return; |
| 165 } |
| 166 |
| 167 UnicodeString str; |
| 168 |
| 169 // Assume it's a DecimalFormat and get some info |
| 170 DecimalFormat *s = (DecimalFormat*)fFormat; |
| 171 logln((UnicodeString)" Pattern " + s->toPattern(str)); |
| 172 |
| 173 #if defined(OS390) || defined(OS400) |
| 174 tryIt(-2.02147304840132e-68); |
| 175 tryIt(3.88057859588817e-68); // Test rounding when only some digits are show
n because exponent is close to -maxfrac |
| 176 tryIt(-2.64651110485945e+65); // Overflows to +INF when shown as a percent |
| 177 tryIt(9.29526819488338e+64); // Ok -- used to fail? |
| 178 #else |
| 179 tryIt(-2.02147304840132e-100); |
| 180 tryIt(3.88057859588817e-096); // Test rounding when only some digits are sho
wn because exponent is close to -maxfrac |
| 181 tryIt(-2.64651110485945e+306); // Overflows to +INF when shown as a percent |
| 182 tryIt(9.29526819488338e+250); // Ok -- used to fail? |
| 183 #endif |
| 184 |
| 185 // These PASS now, with the sprintf/atof based format-parse. |
| 186 |
| 187 // These fail due to round-off |
| 188 // The least significant digit drops by one during each format-parse cycle. |
| 189 // Both numbers DON'T have a round-off problem when multiplied by 100! (show
n as %) |
| 190 #ifdef OS390 |
| 191 tryIt(-9.18228054496402e+64); |
| 192 tryIt(-9.69413034454191e+64); |
| 193 #else |
| 194 tryIt(-9.18228054496402e+255); |
| 195 tryIt(-9.69413034454191e+273); |
| 196 #endif |
| 197 |
| 198 #ifndef OS390 |
| 199 tryIt(1.234e-200); |
| 200 tryIt(-2.3e-168); |
| 201 |
| 202 tryIt(uprv_getNaN()); |
| 203 tryIt(uprv_getInfinity()); |
| 204 tryIt(-uprv_getInfinity()); |
| 205 #endif |
| 206 |
| 207 tryIt((int32_t)251887531); |
| 208 tryIt(5e-20 / 9); |
| 209 tryIt(5e20 / 9); |
| 210 tryIt(1.234e-50); |
| 211 tryIt(9.99999999999996); |
| 212 tryIt(9.999999999999996); |
| 213 |
| 214 tryIt((int32_t)INT32_MIN); |
| 215 tryIt((int32_t)INT32_MAX); |
| 216 tryIt((double)INT32_MIN); |
| 217 tryIt((double)INT32_MAX); |
| 218 tryIt((double)INT32_MIN - 1.0); |
| 219 tryIt((double)INT32_MAX + 1.0); |
| 220 |
| 221 tryIt(5.0 / 9.0 * 1e-20); |
| 222 tryIt(4.0 / 9.0 * 1e-20); |
| 223 tryIt(5.0 / 9.0 * 1e+20); |
| 224 tryIt(4.0 / 9.0 * 1e+20); |
| 225 |
| 226 tryIt(2147483647.); |
| 227 tryIt((int32_t)0); |
| 228 tryIt(0.0); |
| 229 tryIt((int32_t)1); |
| 230 tryIt((int32_t)10); |
| 231 tryIt((int32_t)100); |
| 232 tryIt((int32_t)-1); |
| 233 tryIt((int32_t)-10); |
| 234 tryIt((int32_t)-100); |
| 235 tryIt((int32_t)-1913860352); |
| 236 |
| 237 for (int32_t z=0; z<10; ++z) |
| 238 { |
| 239 double d = randFraction() * 2e10 - 1e10; |
| 240 tryIt(d); |
| 241 } |
| 242 |
| 243 double it = getSafeDouble(100000.0); |
| 244 |
| 245 tryIt(0.0); |
| 246 tryIt(it); |
| 247 tryIt((int32_t)0); |
| 248 tryIt(uprv_floor(it)); |
| 249 tryIt((int32_t)randLong()); |
| 250 |
| 251 // try again |
| 252 it = getSafeDouble(100.0); |
| 253 tryIt(it); |
| 254 tryIt(uprv_floor(it)); |
| 255 tryIt((int32_t)randLong()); |
| 256 |
| 257 // try again with very large numbers |
| 258 it = getSafeDouble(100000000000.0); |
| 259 tryIt(it); |
| 260 |
| 261 // try again with very large numbers |
| 262 // and without going outside of the int32_t range |
| 263 it = randFraction() * INT32_MAX; |
| 264 tryIt(it); |
| 265 tryIt((int32_t)uprv_floor(it)); |
| 266 |
| 267 delete fFormat; |
| 268 } |
| 269 |
| 270 void |
| 271 IntlTestNumberFormat::tryIt(double aNumber) |
| 272 { |
| 273 const int32_t DEPTH = 10; |
| 274 Formattable number[DEPTH]; |
| 275 UnicodeString string[DEPTH]; |
| 276 |
| 277 int32_t numberMatch = 0; |
| 278 int32_t stringMatch = 0; |
| 279 UnicodeString errMsg; |
| 280 int32_t i; |
| 281 for (i=0; i<DEPTH; ++i) |
| 282 { |
| 283 errMsg.truncate(0); // if non-empty, we failed this iteration |
| 284 UErrorCode status = U_ZERO_ERROR; |
| 285 string[i] = "(n/a)"; // "format was never done" value |
| 286 if (i == 0) { |
| 287 number[i].setDouble(aNumber); |
| 288 } else { |
| 289 fFormat->parse(string[i-1], number[i], status); |
| 290 if (U_FAILURE(status)) { |
| 291 number[i].setDouble(1234.5); // "parse failed" value |
| 292 errMsg = "**** FAIL: Parse of " + prettify(string[i-1]) + " fail
ed."; |
| 293 --i; // don't show empty last line: "1234.5 F> (n/a) P>" |
| 294 break; |
| 295 } |
| 296 } |
| 297 // Convert from long to double |
| 298 if (number[i].getType() == Formattable::kLong) |
| 299 number[i].setDouble(number[i].getLong()); |
| 300 else if (number[i].getType() == Formattable::kInt64) |
| 301 number[i].setDouble((double)number[i].getInt64()); |
| 302 else if (number[i].getType() != Formattable::kDouble) |
| 303 { |
| 304 errMsg = ("**** FAIL: Parse of " + prettify(string[i-1]) |
| 305 + " returned non-numeric Formattable, type " + UnicodeString(for
mattableTypeName(number[i].getType())) |
| 306 + ", Locale=" + UnicodeString(fLocale.getName()) |
| 307 + ", longValue=" + number[i].getLong()); |
| 308 break; |
| 309 } |
| 310 string[i].truncate(0); |
| 311 fFormat->format(number[i].getDouble(), string[i]); |
| 312 if (i > 0) |
| 313 { |
| 314 if (numberMatch == 0 && number[i] == number[i-1]) |
| 315 numberMatch = i; |
| 316 else if (numberMatch > 0 && number[i] != number[i-1]) |
| 317 { |
| 318 errMsg = ("**** FAIL: Numeric mismatch after match."); |
| 319 break; |
| 320 } |
| 321 if (stringMatch == 0 && string[i] == string[i-1]) |
| 322 stringMatch = i; |
| 323 else if (stringMatch > 0 && string[i] != string[i-1]) |
| 324 { |
| 325 errMsg = ("**** FAIL: String mismatch after match."); |
| 326 break; |
| 327 } |
| 328 } |
| 329 if (numberMatch > 0 && stringMatch > 0) |
| 330 break; |
| 331 } |
| 332 if (i == DEPTH) |
| 333 --i; |
| 334 |
| 335 if (stringMatch > 2 || numberMatch > 2) |
| 336 { |
| 337 errMsg = ("**** FAIL: No string and/or number match within 2 iterations.
"); |
| 338 } |
| 339 |
| 340 if (errMsg.length() != 0) |
| 341 { |
| 342 for (int32_t k=0; k<=i; ++k) |
| 343 { |
| 344 logln((UnicodeString)"" + k + ": " + number[k].getDouble() + " F> "
+ |
| 345 prettify(string[k]) + " P> "); |
| 346 } |
| 347 errln(errMsg); |
| 348 } |
| 349 } |
| 350 |
| 351 void |
| 352 IntlTestNumberFormat::tryIt(int32_t aNumber) |
| 353 { |
| 354 Formattable number(aNumber); |
| 355 UnicodeString stringNum; |
| 356 UErrorCode status = U_ZERO_ERROR; |
| 357 |
| 358 fFormat->format(number, stringNum, status); |
| 359 if (U_FAILURE(status)) |
| 360 { |
| 361 errln("**** FAIL: Formatting " + aNumber); |
| 362 return; |
| 363 } |
| 364 fFormat->parse(stringNum, number, status); |
| 365 if (U_FAILURE(status)) |
| 366 { |
| 367 errln("**** FAIL: Parse of " + prettify(stringNum) + " failed."); |
| 368 return; |
| 369 } |
| 370 if (number.getType() != Formattable::kLong) |
| 371 { |
| 372 errln("**** FAIL: Parse of " + prettify(stringNum) |
| 373 + " returned non-long Formattable, type " + UnicodeString(formattabl
eTypeName(number.getType())) |
| 374 + ", Locale=" + UnicodeString(fLocale.getName()) |
| 375 + ", doubleValue=" + number.getDouble() |
| 376 + ", longValue=" + number.getLong() |
| 377 + ", origValue=" + aNumber |
| 378 ); |
| 379 } |
| 380 if (number.getLong() != aNumber) { |
| 381 errln("**** FAIL: Parse of " + prettify(stringNum) + " failed. Got:" + n
umber.getLong() |
| 382 + " Expected:" + aNumber); |
| 383 } |
| 384 } |
| 385 |
| 386 void IntlTestNumberFormat::testAvailableLocales(/* char* par */) |
| 387 { |
| 388 int32_t count = 0; |
| 389 const Locale* locales = NumberFormat::getAvailableLocales(count); |
| 390 logln((UnicodeString)"" + count + " available locales"); |
| 391 if (locales && count) |
| 392 { |
| 393 UnicodeString name; |
| 394 UnicodeString all; |
| 395 for (int32_t i=0; i<count; ++i) |
| 396 { |
| 397 if (i!=0) |
| 398 all += ", "; |
| 399 all += locales[i].getName(); |
| 400 } |
| 401 logln(all); |
| 402 } |
| 403 else |
| 404 dataerrln((UnicodeString)"**** FAIL: Zero available locales or null arra
y pointer"); |
| 405 } |
| 406 |
| 407 void IntlTestNumberFormat::monsterTest(/* char* par */) |
| 408 { |
| 409 const char *SEP = "=========================================================
===\n"; |
| 410 int32_t count; |
| 411 const Locale* allLocales = NumberFormat::getAvailableLocales(count); |
| 412 Locale* locales = (Locale*)allLocales; |
| 413 Locale quickLocales[6]; |
| 414 if (allLocales && count) |
| 415 { |
| 416 if (quick && count > 6) { |
| 417 logln("quick test: testing just 6 locales!"); |
| 418 count = 6; |
| 419 locales = quickLocales; |
| 420 locales[0] = allLocales[0]; |
| 421 locales[1] = allLocales[1]; |
| 422 locales[2] = allLocales[2]; |
| 423 // In a quick test, make sure we test locales that use |
| 424 // currency prefix, currency suffix, and choice currency |
| 425 // logic. Otherwise bugs in these areas can slip through. |
| 426 locales[3] = Locale("ar", "AE", ""); |
| 427 locales[4] = Locale("cs", "CZ", ""); |
| 428 locales[5] = Locale("en", "IN", ""); |
| 429 } |
| 430 for (int32_t i=0; i<count; ++i) |
| 431 { |
| 432 UnicodeString name(locales[i].getName(), ""); |
| 433 logln(SEP); |
| 434 testLocale(/* par, */locales[i], name); |
| 435 } |
| 436 } |
| 437 |
| 438 logln(SEP); |
| 439 } |
| 440 |
| 441 #endif /* #if !UCONFIG_NO_FORMATTING */ |
OLD | NEW |