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