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 UnicodeString& 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_errorName(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 createXxxInstance."); 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 shown 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 shown 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! (shown 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]) + " failed."; 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(formattableTypeName(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(formattableTypeName(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:" + number.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 array 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 */ 442