• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 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 
~IntlTestNumberFormat()20 IntlTestNumberFormat::~IntlTestNumberFormat() {}
21 
formattableTypeName(Formattable::Type t)22 static 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  */
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)39 void 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 
76 void
testLocale(const Locale & locale,const UnicodeString & localeName)77 IntlTestNumberFormat::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 
randDouble()109 double 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  **/
randLong()131 uint32_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 */
getSafeDouble(double smallerThanMax)149 double 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 
159 void
testFormat()160 IntlTestNumberFormat::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 
283 void
tryIt(double aNumber)284 IntlTestNumberFormat::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 
364 void
tryIt(int32_t aNumber)365 IntlTestNumberFormat::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 
testAvailableLocales()399 void 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 
monsterTest()420 void 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