1 /***********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1997-2011, 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
~IntlTestNumberFormat()18 IntlTestNumberFormat::~IntlTestNumberFormat() {}
19
formattableTypeName(Formattable::Type t)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 */
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)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
testLocale(const Locale & locale,const UnicodeString & localeName)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
randDouble()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 **/
randLong()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 */
getSafeDouble(double smallerThanMax)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
testFormat()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 defined(OS390) || defined(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 #ifdef 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 #ifndef 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
tryIt(double aNumber)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
tryIt(int32_t aNumber)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("**** 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
testAvailableLocales()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
monsterTest()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