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