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