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