• 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-2016, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  ********************************************************************/
8 /********************************************************************************
9 *
10 * File CNUMTST.C
11 *
12 *     Madhu Katragadda              Creation
13 *
14 * Modification History:
15 *
16 *   Date        Name        Description
17 *   06/24/99    helena      Integrated Alan's NF enhancements and Java2 bug fixes
18 *   07/15/99    helena      Ported to HPUX 10/11 CC.
19 *********************************************************************************
20 */
21 
22 /* C API TEST FOR NUMBER FORMAT */
23 
24 #include "unicode/utypes.h"
25 
26 #if !UCONFIG_NO_FORMATTING
27 
28 #include "unicode/uloc.h"
29 #include "unicode/umisc.h"
30 #include "unicode/unum.h"
31 #include "unicode/unumsys.h"
32 #include "unicode/ustring.h"
33 #include "unicode/udisplaycontext.h"
34 
35 #include "cintltst.h"
36 #include "cnumtst.h"
37 #include "cmemory.h"
38 #include "cstring.h"
39 #include "putilimp.h"
40 #include "uassert.h"
41 #include <stdio.h>
42 #include <stdlib.h>
43 
tagAssert(const char * f,int32_t l,const char * msg)44 static const char *tagAssert(const char *f, int32_t l, const char *msg) {
45     static char _fileline[1000];
46     sprintf(_fileline, "%s:%d: ASSERT_TRUE(%s)", f, l, msg);
47     return _fileline;
48 }
49 
50 #define ASSERT_TRUE(x)   assertTrue(tagAssert(__FILE__, __LINE__, #x), (x))
51 
52 void addNumForTest(TestNode** root);
53 static void TestTextAttributeCrash(void);
54 static void TestNBSPInPattern(void);
55 static void TestInt64Parse(void);
56 static void TestParseCurrency(void);
57 static void TestMaxInt(void);
58 static void TestNoExponent(void);
59 static void TestSignAlwaysShown(void);
60 static void TestMinimumGroupingDigits(void);
61 static void TestParseCaseSensitive(void);
62 static void TestUFormattable(void);
63 static void TestUNumberingSystem(void);
64 static void TestCurrencyIsoPluralFormat(void);
65 static void TestContext(void);
66 static void TestCurrencyUsage(void);
67 static void TestCurrFmtNegSameAsPositive(void);
68 static void TestVariousStylesAndAttributes(void);
69 static void TestParseCurrPatternWithDecStyle(void);
70 static void TestFormatForFields(void);
71 static void TestRBNFRounding(void);
72 static void Test12052_NullPointer(void);
73 static void TestParseCases(void);
74 static void TestSetMaxFracAndRoundIncr(void);
75 static void TestIgnorePadding(void);
76 static void TestSciNotationMaxFracCap(void);
77 static void TestMinIntMinFracZero(void);
78 static void Test21479_ExactCurrency(void);
79 
80 #define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x)
81 
addNumForTest(TestNode ** root)82 void addNumForTest(TestNode** root)
83 {
84     TESTCASE(TestNumberFormat);
85     TESTCASE(TestSpelloutNumberParse);
86     TESTCASE(TestSignificantDigits);
87     TESTCASE(TestSigDigRounding);
88     TESTCASE(TestNumberFormatPadding);
89     TESTCASE(TestInt64Format);
90     TESTCASE(TestNonExistentCurrency);
91     TESTCASE(TestCurrencyRegression);
92     TESTCASE(TestTextAttributeCrash);
93     TESTCASE(TestRBNFFormat);
94     TESTCASE(TestRBNFRounding);
95     TESTCASE(TestNBSPInPattern);
96     TESTCASE(TestInt64Parse);
97     TESTCASE(TestParseZero);
98     TESTCASE(TestParseCurrency);
99     TESTCASE(TestCloneWithRBNF);
100     TESTCASE(TestMaxInt);
101     TESTCASE(TestNoExponent);
102     TESTCASE(TestSignAlwaysShown);
103     TESTCASE(TestMinimumGroupingDigits);
104     TESTCASE(TestParseCaseSensitive);
105     TESTCASE(TestUFormattable);
106     TESTCASE(TestUNumberingSystem);
107     TESTCASE(TestCurrencyIsoPluralFormat);
108     TESTCASE(TestContext);
109     TESTCASE(TestCurrencyUsage);
110     TESTCASE(TestCurrFmtNegSameAsPositive);
111     TESTCASE(TestVariousStylesAndAttributes);
112     TESTCASE(TestParseCurrPatternWithDecStyle);
113     TESTCASE(TestFormatForFields);
114     TESTCASE(Test12052_NullPointer);
115     TESTCASE(TestParseCases);
116     TESTCASE(TestSetMaxFracAndRoundIncr);
117     TESTCASE(TestIgnorePadding);
118     TESTCASE(TestSciNotationMaxFracCap);
119     TESTCASE(TestMinIntMinFracZero);
120     TESTCASE(Test21479_ExactCurrency);
121 }
122 
123 /* test Parse int 64 */
124 
TestInt64Parse()125 static void TestInt64Parse()
126 {
127 
128     UErrorCode st = U_ZERO_ERROR;
129     UErrorCode* status = &st;
130 
131     const char* st1 = "009223372036854775808";
132     const int size = 21;
133     UChar text[21];
134 
135 
136     UNumberFormat* nf;
137 
138     int64_t a;
139 
140     u_charsToUChars(st1, text, size);
141     nf = unum_open(UNUM_DEFAULT, NULL, -1, NULL, NULL, status);
142 
143     if(U_FAILURE(*status))
144     {
145         log_data_err("Error in unum_open() %s \n", myErrorName(*status));
146         return;
147     }
148 
149     log_verbose("About to test unum_parseInt64() with out of range number\n");
150 
151     a = unum_parseInt64(nf, text, size, 0, status);
152     (void)a;     /* Suppress set but not used warning. */
153 
154 
155     if(!U_FAILURE(*status))
156     {
157         log_err("Error in unum_parseInt64(): %s \n", myErrorName(*status));
158     }
159     else
160     {
161         log_verbose("unum_parseInt64() successful\n");
162     }
163 
164     unum_close(nf);
165     return;
166 }
167 
168 /* test Number Format API */
TestNumberFormat()169 static void TestNumberFormat()
170 {
171     UChar *result=NULL;
172     UChar temp1[512];
173     UChar temp2[512];
174 
175     UChar temp[5];
176 
177     UChar prefix[5];
178     UChar suffix[5];
179     UChar symbol[20];
180     int32_t resultlength;
181     int32_t resultlengthneeded;
182     int32_t parsepos;
183     double d1 = -1.0;
184     int32_t l1;
185     double d = -10456.37;
186     double a = 1234.56, a1 = 1235.0;
187     int32_t l = 100000000;
188     UFieldPosition pos1;
189     UFieldPosition pos2;
190     int32_t numlocales;
191     int32_t i;
192 
193     UNumberFormatAttribute attr;
194     UNumberFormatSymbol symType = UNUM_DECIMAL_SEPARATOR_SYMBOL;
195     int32_t newvalue;
196     UErrorCode status=U_ZERO_ERROR;
197     UNumberFormatStyle style= UNUM_DEFAULT;
198     UNumberFormat *pattern;
199     UNumberFormat *def, *fr, *cur_def, *cur_fr, *per_def, *per_fr,
200                   *cur_frpattern, *myclone, *spellout_def;
201 
202     /* Testing unum_open() with various Numberformat styles and locales*/
203     status = U_ZERO_ERROR;
204     log_verbose("Testing  unum_open() with default style and locale\n");
205     def=unum_open(style, NULL,0,NULL, NULL,&status);
206 
207     /* Might as well pack it in now if we can't even get a default NumberFormat... */
208     if(U_FAILURE(status))
209     {
210         log_data_err("Error in creating default NumberFormat using unum_open(): %s (Are you missing data?)\n", myErrorName(status));
211         return;
212     }
213 
214     log_verbose("\nTesting unum_open() with french locale and default style(decimal)\n");
215     fr=unum_open(style,NULL,0, "fr_FR",NULL, &status);
216     if(U_FAILURE(status))
217         log_err("Error: could not create NumberFormat (french): %s\n", myErrorName(status));
218 
219     log_verbose("\nTesting unum_open(currency,NULL,status)\n");
220     style=UNUM_CURRENCY;
221     /* Can't hardcode the result to assume the default locale is "en_US". */
222     cur_def=unum_open(style, NULL,0,"en_US", NULL, &status);
223     if(U_FAILURE(status))
224         log_err("Error: could not create NumberFormat using \n unum_open(currency, NULL, &status) %s\n",
225                         myErrorName(status) );
226 
227     log_verbose("\nTesting unum_open(currency, frenchlocale, status)\n");
228     cur_fr=unum_open(style,NULL,0, "fr_FR", NULL, &status);
229     if(U_FAILURE(status))
230         log_err("Error: could not create NumberFormat using unum_open(currency, french, &status): %s\n",
231                 myErrorName(status));
232 
233     log_verbose("\nTesting unum_open(percent, NULL, status)\n");
234     style=UNUM_PERCENT;
235     per_def=unum_open(style,NULL,0, NULL,NULL, &status);
236     if(U_FAILURE(status))
237         log_err("Error: could not create NumberFormat using unum_open(percent, NULL, &status): %s\n", myErrorName(status));
238 
239     log_verbose("\nTesting unum_open(percent,frenchlocale, status)\n");
240     per_fr=unum_open(style, NULL,0,"fr_FR", NULL,&status);
241     if(U_FAILURE(status))
242         log_err("Error: could not create NumberFormat using unum_open(percent, french, &status): %s\n", myErrorName(status));
243 
244     log_verbose("\nTesting unum_open(spellout, NULL, status)");
245     style=UNUM_SPELLOUT;
246     spellout_def=unum_open(style, NULL, 0, "en_US", NULL, &status);
247     if(U_FAILURE(status))
248         log_err("Error: could not create NumberFormat using unum_open(spellout, NULL, &status): %s\n", myErrorName(status));
249 
250     /* Testing unum_clone(..) */
251     log_verbose("\nTesting unum_clone(fmt, status)");
252     status = U_ZERO_ERROR;
253     myclone = unum_clone(def,&status);
254     if(U_FAILURE(status))
255         log_err("Error: could not clone unum_clone(def, &status): %s\n", myErrorName(status));
256     else
257     {
258         log_verbose("unum_clone() successful\n");
259     }
260 
261     /*Testing unum_getAvailable() and unum_countAvailable()*/
262     log_verbose("\nTesting getAvailableLocales and countAvailable()\n");
263     numlocales=unum_countAvailable();
264     if(numlocales < 0)
265         log_err("error in countAvailable");
266     else{
267         log_verbose("unum_countAvialable() successful\n");
268         log_verbose("The no: of locales where number formattting is applicable is %d\n", numlocales);
269     }
270     for(i=0;i<numlocales;i++)
271     {
272         log_verbose("%s\n", unum_getAvailable(i));
273         if (unum_getAvailable(i) == 0)
274             log_err("No locale for which number formatting patterns are applicable\n");
275         else
276             log_verbose("A locale %s for which number formatting patterns are applicable\n",unum_getAvailable(i));
277     }
278 
279 
280     /*Testing unum_format() and unum_formatdouble()*/
281     u_uastrcpy(temp1, "$100,000,000.00");
282 
283     log_verbose("\nTesting unum_format() \n");
284     resultlength=0;
285     pos1.field = UNUM_INTEGER_FIELD;
286     resultlengthneeded=unum_format(cur_def, l, NULL, resultlength, &pos1, &status);
287     if(status==U_BUFFER_OVERFLOW_ERROR)
288     {
289         status=U_ZERO_ERROR;
290         resultlength=resultlengthneeded+1;
291         result=(UChar*)malloc(sizeof(UChar) * resultlength);
292 /*        for (i = 0; i < 100000; i++) */
293         {
294             unum_format(cur_def, l, result, resultlength, &pos1, &status);
295         }
296     }
297 
298     if(U_FAILURE(status))
299     {
300         log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status) );
301     }
302     if(u_strcmp(result, temp1)==0)
303         log_verbose("Pass: Number formatting using unum_format() successful\n");
304     else
305         log_err("Fail: Error in number Formatting using unum_format()\n");
306     if(pos1.beginIndex == 1 && pos1.endIndex == 12)
307         log_verbose("Pass: Complete number formatting using unum_format() successful\n");
308     else
309         log_err("Fail: Error in complete number Formatting using unum_format()\nGot: b=%d end=%d\nExpected: b=1 end=12\n",
310                 pos1.beginIndex, pos1.endIndex);
311 
312     free(result);
313     result = 0;
314 
315     log_verbose("\nTesting unum_formatDouble()\n");
316     u_uastrcpy(temp1, "-$10,456.37");
317     resultlength=0;
318     pos2.field = UNUM_FRACTION_FIELD;
319     resultlengthneeded=unum_formatDouble(cur_def, d, NULL, resultlength, &pos2, &status);
320     if(status==U_BUFFER_OVERFLOW_ERROR)
321     {
322         status=U_ZERO_ERROR;
323         resultlength=resultlengthneeded+1;
324         result=(UChar*)malloc(sizeof(UChar) * resultlength);
325 /*        for (i = 0; i < 100000; i++) */
326         {
327             unum_formatDouble(cur_def, d, result, resultlength, &pos2, &status);
328         }
329     }
330     if(U_FAILURE(status))
331     {
332         log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status));
333     }
334     if(result && u_strcmp(result, temp1)==0)
335         log_verbose("Pass: Number Formatting using unum_formatDouble() Successful\n");
336     else {
337       log_err("FAIL: Error in number formatting using unum_formatDouble() - got '%s' expected '%s'\n",
338               aescstrdup(result, -1), aescstrdup(temp1, -1));
339     }
340     if(pos2.beginIndex == 9 && pos2.endIndex == 11)
341         log_verbose("Pass: Complete number formatting using unum_format() successful\n");
342     else
343         log_err("Fail: Error in complete number Formatting using unum_formatDouble()\nGot: b=%d end=%d\nExpected: b=9 end=11",
344                 pos1.beginIndex, pos1.endIndex);
345 
346 
347     /* Testing unum_parse() and unum_parseDouble() */
348     log_verbose("\nTesting unum_parseDouble()\n");
349 /*    for (i = 0; i < 100000; i++)*/
350     parsepos=0;
351     if (result != NULL) {
352       d1=unum_parseDouble(cur_def, result, u_strlen(result), &parsepos, &status);
353     } else {
354       log_err("result is NULL\n");
355     }
356     if(U_FAILURE(status)) {
357       log_err("parse of '%s' failed. Parsepos=%d. The error is  : %s\n", aescstrdup(result,u_strlen(result)),parsepos, myErrorName(status));
358     }
359 
360     if(d1!=d)
361         log_err("Fail: Error in parsing\n");
362     else
363         log_verbose("Pass: parsing successful\n");
364     if (result)
365         free(result);
366     result = 0;
367 
368     status = U_ZERO_ERROR;
369     /* Testing unum_formatDoubleCurrency / unum_parseDoubleCurrency */
370     log_verbose("\nTesting unum_formatDoubleCurrency\n");
371     u_uastrcpy(temp1, "Y1,235");
372     temp1[0] = 0xA5; /* Yen sign */
373     u_uastrcpy(temp, "JPY");
374     resultlength=0;
375     pos2.field = UNUM_INTEGER_FIELD;
376     resultlengthneeded=unum_formatDoubleCurrency(cur_def, a, temp, NULL, resultlength, &pos2, &status);
377     if (status==U_BUFFER_OVERFLOW_ERROR) {
378         status=U_ZERO_ERROR;
379         resultlength=resultlengthneeded+1;
380         result=(UChar*)malloc(sizeof(UChar) * resultlength);
381         unum_formatDoubleCurrency(cur_def, a, temp, result, resultlength, &pos2, &status);
382     }
383     if (U_FAILURE(status)) {
384         log_err("Error in formatting using unum_formatDoubleCurrency(.....): %s\n", myErrorName(status));
385     }
386     if (result && u_strcmp(result, temp1)==0) {
387         log_verbose("Pass: Number Formatting using unum_formatDoubleCurrency() Successful\n");
388     } else {
389         log_err("FAIL: Error in number formatting using unum_formatDoubleCurrency() - got '%s' expected '%s'\n",
390                 aescstrdup(result, -1), aescstrdup(temp1, -1));
391     }
392     if (pos2.beginIndex == 1 && pos2.endIndex == 6) {
393         log_verbose("Pass: Complete number formatting using unum_format() successful\n");
394     } else {
395         log_err("Fail: Error in complete number Formatting using unum_formatDouble()\nGot: b=%d end=%d\nExpected: b=1 end=6\n",
396                 pos1.beginIndex, pos1.endIndex);
397     }
398 
399     log_verbose("\nTesting unum_parseDoubleCurrency\n");
400     parsepos=0;
401     if (result == NULL) {
402         log_err("result is NULL\n");
403     }
404     else {
405         d1=unum_parseDoubleCurrency(cur_def, result, u_strlen(result), &parsepos, temp2, &status);
406         if (U_FAILURE(status)) {
407           log_err("parseDoubleCurrency '%s' failed. The error is  : %s\n", aescstrdup(result, u_strlen(result)), myErrorName(status));
408         }
409         /* Note: a==1234.56, but on parse expect a1=1235.0 */
410         if (d1!=a1) {
411             log_err("Fail: Error in parsing currency, got %f, expected %f\n", d1, a1);
412         } else {
413             log_verbose("Pass: parsed currency amount successfully\n");
414         }
415         if (u_strcmp(temp2, temp)==0) {
416             log_verbose("Pass: parsed correct currency\n");
417         } else {
418             log_err("Fail: parsed incorrect currency\n");
419         }
420     }
421     status = U_ZERO_ERROR; /* reset */
422 
423     free(result);
424     result = 0;
425 
426 
427 /* performance testing */
428     u_uastrcpy(temp1, "$462.12345");
429     resultlength=u_strlen(temp1);
430 /*    for (i = 0; i < 100000; i++) */
431     {
432         parsepos=0;
433         d1=unum_parseDouble(cur_def, temp1, resultlength, &parsepos, &status);
434     }
435     if(U_FAILURE(status))
436     {
437         log_err("parseDouble('%s') failed. The error is  : %s\n", aescstrdup(temp1, resultlength), myErrorName(status));
438     }
439 
440     /*
441      * Note: "for strict standard conformance all operations and constants are now supposed to be
442               evaluated in precision of long double".  So,  we assign a1 before comparing to a double. Bug #7932.
443      */
444     a1 = 462.12345;
445 
446     if(d1!=a1)
447         log_err("Fail: Error in parsing\n");
448     else
449         log_verbose("Pass: parsing successful\n");
450 
451 free(result);
452 
453     u_uastrcpy(temp1, "($10,456.3E1])");
454     parsepos=0;
455     d1=unum_parseDouble(cur_def, temp1, u_strlen(temp1), &parsepos, &status);
456     if(U_SUCCESS(status))
457     {
458         log_err("Error in unum_parseDouble(..., %s, ...): %s\n", temp1, myErrorName(status));
459     }
460 
461 
462     log_verbose("\nTesting unum_format()\n");
463     status=U_ZERO_ERROR;
464     resultlength=0;
465     parsepos=0;
466     resultlengthneeded=unum_format(per_fr, l, NULL, resultlength, &pos1, &status);
467     if(status==U_BUFFER_OVERFLOW_ERROR)
468     {
469         status=U_ZERO_ERROR;
470         resultlength=resultlengthneeded+1;
471         result=(UChar*)malloc(sizeof(UChar) * resultlength);
472 /*        for (i = 0; i < 100000; i++)*/
473         {
474             unum_format(per_fr, l, result, resultlength, &pos1, &status);
475         }
476     }
477     if(U_FAILURE(status))
478     {
479         log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status));
480     }
481 
482 
483     log_verbose("\nTesting unum_parse()\n");
484 /*    for (i = 0; i < 100000; i++) */
485     {
486         parsepos=0;
487         l1=unum_parse(per_fr, result, u_strlen(result), &parsepos, &status);
488     }
489     if(U_FAILURE(status))
490     {
491         log_err("parse failed. The error is  : %s\n", myErrorName(status));
492     }
493 
494     if(l1!=l)
495         log_err("Fail: Error in parsing\n");
496     else
497         log_verbose("Pass: parsing successful\n");
498 
499 free(result);
500 
501     /* create a number format using unum_openPattern(....)*/
502     log_verbose("\nTesting unum_openPattern()\n");
503     u_uastrcpy(temp1, "#,##0.0#;(#,##0.0#)");
504     pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), NULL, NULL,&status);
505     if(U_FAILURE(status))
506     {
507         log_err("error in unum_openPattern(): %s\n", myErrorName(status) );
508     }
509     else
510         log_verbose("Pass: unum_openPattern() works fine\n");
511 
512     /*test for unum_toPattern()*/
513     log_verbose("\nTesting unum_toPattern()\n");
514     resultlength=0;
515     resultlengthneeded=unum_toPattern(pattern, FALSE, NULL, resultlength, &status);
516     if(status==U_BUFFER_OVERFLOW_ERROR)
517     {
518         status=U_ZERO_ERROR;
519         resultlength=resultlengthneeded+1;
520         result=(UChar*)malloc(sizeof(UChar) * resultlength);
521         unum_toPattern(pattern, FALSE, result, resultlength, &status);
522     }
523     if(U_FAILURE(status))
524     {
525         log_err("error in extracting the pattern from UNumberFormat: %s\n", myErrorName(status));
526     }
527     else
528     {
529         if(u_strcmp(result, temp1)!=0)
530             log_err("FAIL: Error in extracting the pattern using unum_toPattern()\n");
531         else
532             log_verbose("Pass: extracted the pattern correctly using unum_toPattern()\n");
533 free(result);
534     }
535 
536     /*Testing unum_getSymbols() and unum_setSymbols()*/
537     log_verbose("\nTesting unum_getSymbols and unum_setSymbols()\n");
538     /*when we try to change the symbols of french to default we need to apply the pattern as well to fetch correct results */
539     resultlength=0;
540     resultlengthneeded=unum_toPattern(cur_def, FALSE, NULL, resultlength, &status);
541     if(status==U_BUFFER_OVERFLOW_ERROR)
542     {
543         status=U_ZERO_ERROR;
544         resultlength=resultlengthneeded+1;
545         result=(UChar*)malloc(sizeof(UChar) * resultlength);
546         unum_toPattern(cur_def, FALSE, result, resultlength, &status);
547     }
548     if(U_FAILURE(status))
549     {
550         log_err("error in extracting the pattern from UNumberFormat: %s\n", myErrorName(status));
551     }
552 
553     status=U_ZERO_ERROR;
554     cur_frpattern=unum_open(UNUM_IGNORE,result, u_strlen(result), "fr_FR",NULL, &status);
555     if(U_FAILURE(status))
556     {
557         log_err("error in unum_openPattern(): %s\n", myErrorName(status));
558     }
559 
560 free(result);
561 
562     /*getting the symbols of cur_def */
563     /*set the symbols of cur_frpattern to cur_def */
564     for (symType = UNUM_DECIMAL_SEPARATOR_SYMBOL; symType < UNUM_FORMAT_SYMBOL_COUNT; symType++) {
565         status=U_ZERO_ERROR;
566         unum_getSymbol(cur_def, symType, temp1, sizeof(temp1), &status);
567         unum_setSymbol(cur_frpattern, symType, temp1, -1, &status);
568         if(U_FAILURE(status))
569         {
570             log_err("Error in get/set symbols: %s\n", myErrorName(status));
571         }
572     }
573 
574     /*format to check the result */
575     resultlength=0;
576     resultlengthneeded=unum_format(cur_def, l, NULL, resultlength, &pos1, &status);
577     if(status==U_BUFFER_OVERFLOW_ERROR)
578     {
579         status=U_ZERO_ERROR;
580         resultlength=resultlengthneeded+1;
581         result=(UChar*)malloc(sizeof(UChar) * resultlength);
582         unum_format(cur_def, l, result, resultlength, &pos1, &status);
583     }
584     if(U_FAILURE(status))
585     {
586         log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status));
587     }
588 
589     if(U_FAILURE(status)){
590         log_err("Fail: error in unum_setSymbols: %s\n", myErrorName(status));
591     }
592     unum_applyPattern(cur_frpattern, FALSE, result, u_strlen(result),NULL,NULL);
593 
594     for (symType = UNUM_DECIMAL_SEPARATOR_SYMBOL; symType < UNUM_FORMAT_SYMBOL_COUNT; symType++) {
595         status=U_ZERO_ERROR;
596         unum_getSymbol(cur_def, symType, temp1, sizeof(temp1), &status);
597         unum_getSymbol(cur_frpattern, symType, temp2, sizeof(temp2), &status);
598         if(U_FAILURE(status) || u_strcmp(temp1, temp2) != 0)
599         {
600             log_err("Fail: error in getting symbols\n");
601         }
602         else
603             log_verbose("Pass: get and set symbols successful\n");
604     }
605 
606     /*format and check with the previous result */
607 
608     resultlength=0;
609     resultlengthneeded=unum_format(cur_frpattern, l, NULL, resultlength, &pos1, &status);
610     if(status==U_BUFFER_OVERFLOW_ERROR)
611     {
612         status=U_ZERO_ERROR;
613         resultlength=resultlengthneeded+1;
614         unum_format(cur_frpattern, l, temp1, resultlength, &pos1, &status);
615     }
616     if(U_FAILURE(status))
617     {
618         log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status));
619     }
620     /* TODO:
621      * This test fails because we have not called unum_applyPattern().
622      * Currently, such an applyPattern() does not exist on the C API, and
623      * we have jitterbug 411 for it.
624      * Since it is close to the 1.5 release, I (markus) am disabling this test just
625      * for this release (I added the test itself only last week).
626      * For the next release, we need to fix this.
627      * Then, remove the uprv_strcmp("1.5", ...) and this comment, and the include "cstring.h" at the beginning of this file.
628      */
629     if(u_strcmp(result, temp1) != 0) {
630         log_err("Formatting failed after setting symbols. result=%s temp1=%s\n", result, temp1);
631     }
632 
633 
634     /*----------- */
635 
636 free(result);
637 
638     /* Testing unum_get/setSymbol() */
639     for(i = 0; i < UNUM_FORMAT_SYMBOL_COUNT; ++i) {
640         symbol[0] = (UChar)(0x41 + i);
641         symbol[1] = (UChar)(0x61 + i);
642         unum_setSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, 2, &status);
643         if(U_FAILURE(status)) {
644             log_err("Error from unum_setSymbol(%d): %s\n", i, myErrorName(status));
645             return;
646         }
647     }
648     for(i = 0; i < UNUM_FORMAT_SYMBOL_COUNT; ++i) {
649         resultlength = unum_getSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, UPRV_LENGTHOF(symbol), &status);
650         if(U_FAILURE(status)) {
651             log_err("Error from unum_getSymbol(%d): %s\n", i, myErrorName(status));
652             return;
653         }
654         if(resultlength != 2 || symbol[0] != 0x41 + i || symbol[1] != 0x61 + i) {
655             log_err("Failure in unum_getSymbol(%d): got unexpected symbol\n", i);
656         }
657     }
658     /*try getting from a bogus symbol*/
659     unum_getSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, UPRV_LENGTHOF(symbol), &status);
660     if(U_SUCCESS(status)){
661         log_err("Error : Expected U_ILLEGAL_ARGUMENT_ERROR for bogus symbol");
662     }
663     if(U_FAILURE(status)){
664         if(status != U_ILLEGAL_ARGUMENT_ERROR){
665             log_err("Error: Expected U_ILLEGAL_ARGUMENT_ERROR for bogus symbol, Got %s\n", myErrorName(status));
666         }
667     }
668     status=U_ZERO_ERROR;
669 
670     /* Testing unum_getTextAttribute() and unum_setTextAttribute()*/
671     log_verbose("\nTesting getting and setting text attributes\n");
672     resultlength=5;
673     unum_getTextAttribute(cur_fr, UNUM_NEGATIVE_SUFFIX, temp, resultlength, &status);
674     if(U_FAILURE(status))
675     {
676         log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status));
677     }
678     unum_setTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, temp, u_strlen(temp), &status);
679     if(U_FAILURE(status))
680     {
681         log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status));
682     }
683     unum_getTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, suffix, resultlength, &status);
684     if(U_FAILURE(status))
685     {
686         log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status));
687     }
688     if(u_strcmp(suffix,temp)!=0)
689         log_err("Fail:Error in setTextAttribute or getTextAttribute in setting and getting suffix\n");
690     else
691         log_verbose("Pass: setting and getting suffix works fine\n");
692     /*set it back to normal */
693     u_uastrcpy(temp,"$");
694     unum_setTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, temp, u_strlen(temp), &status);
695 
696     /*checking some more text setter conditions */
697     u_uastrcpy(prefix, "+");
698     unum_setTextAttribute(def, UNUM_POSITIVE_PREFIX, prefix, u_strlen(prefix) , &status);
699     if(U_FAILURE(status))
700     {
701         log_err("error in setting the text attributes : %s\n", myErrorName(status));
702     }
703     unum_getTextAttribute(def, UNUM_POSITIVE_PREFIX, temp, resultlength, &status);
704     if(U_FAILURE(status))
705     {
706         log_err("error in getting the text attributes : %s\n", myErrorName(status));
707     }
708 
709     if(u_strcmp(prefix, temp)!=0)
710         log_err("ERROR: get and setTextAttributes with positive prefix failed\n");
711     else
712         log_verbose("Pass: get and setTextAttributes with positive prefix works fine\n");
713 
714     u_uastrcpy(prefix, "+");
715     unum_setTextAttribute(def, UNUM_NEGATIVE_PREFIX, prefix, u_strlen(prefix), &status);
716     if(U_FAILURE(status))
717     {
718         log_err("error in setting the text attributes : %s\n", myErrorName(status));
719     }
720     unum_getTextAttribute(def, UNUM_NEGATIVE_PREFIX, temp, resultlength, &status);
721     if(U_FAILURE(status))
722     {
723         log_err("error in getting the text attributes : %s\n", myErrorName(status));
724     }
725     if(u_strcmp(prefix, temp)!=0)
726         log_err("ERROR: get and setTextAttributes with negative prefix failed\n");
727     else
728         log_verbose("Pass: get and setTextAttributes with negative prefix works fine\n");
729 
730     u_uastrcpy(suffix, "+");
731     unum_setTextAttribute(def, UNUM_NEGATIVE_SUFFIX, suffix, u_strlen(suffix) , &status);
732     if(U_FAILURE(status))
733     {
734         log_err("error in setting the text attributes: %s\n", myErrorName(status));
735     }
736 
737     unum_getTextAttribute(def, UNUM_NEGATIVE_SUFFIX, temp, resultlength, &status);
738     if(U_FAILURE(status))
739     {
740         log_err("error in getting the text attributes : %s\n", myErrorName(status));
741     }
742     if(u_strcmp(suffix, temp)!=0)
743         log_err("ERROR: get and setTextAttributes with negative suffix failed\n");
744     else
745         log_verbose("Pass: get and settextAttributes with negative suffix works fine\n");
746 
747     u_uastrcpy(suffix, "++");
748     unum_setTextAttribute(def, UNUM_POSITIVE_SUFFIX, suffix, u_strlen(suffix) , &status);
749     if(U_FAILURE(status))
750     {
751         log_err("error in setting the text attributes: %s\n", myErrorName(status));
752     }
753 
754     unum_getTextAttribute(def, UNUM_POSITIVE_SUFFIX, temp, resultlength, &status);
755     if(U_FAILURE(status))
756     {
757         log_err("error in getting the text attributes : %s\n", myErrorName(status));
758     }
759     if(u_strcmp(suffix, temp)!=0)
760         log_err("ERROR: get and setTextAttributes with negative suffix failed\n");
761     else
762         log_verbose("Pass: get and settextAttributes with negative suffix works fine\n");
763 
764     /*Testing unum_getAttribute and  unum_setAttribute() */
765     log_verbose("\nTesting get and set Attributes\n");
766     attr=UNUM_GROUPING_SIZE;
767     newvalue=unum_getAttribute(def, attr);
768     newvalue=2;
769     unum_setAttribute(def, attr, newvalue);
770     if(unum_getAttribute(def,attr)!=2)
771         log_err("Fail: error in setting and getting attributes for UNUM_GROUPING_SIZE\n");
772     else
773         log_verbose("Pass: setting and getting attributes for UNUM_GROUPING_SIZE works fine\n");
774 
775     attr=UNUM_MULTIPLIER;
776     newvalue=unum_getAttribute(def, attr);
777     newvalue=8;
778     unum_setAttribute(def, attr, newvalue);
779     if(unum_getAttribute(def,attr) != 8)
780         log_err("error in setting and getting attributes for UNUM_MULTIPLIER\n");
781     else
782         log_verbose("Pass:setting and getting attributes for UNUM_MULTIPLIER works fine\n");
783 
784     attr=UNUM_SECONDARY_GROUPING_SIZE;
785     newvalue=unum_getAttribute(def, attr);
786     newvalue=2;
787     unum_setAttribute(def, attr, newvalue);
788     if(unum_getAttribute(def,attr) != 2)
789         log_err("error in setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE: got %d\n",
790                 unum_getAttribute(def,attr));
791     else
792         log_verbose("Pass:setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE works fine\n");
793 
794     /*testing set and get Attributes extensively */
795     log_verbose("\nTesting get and set attributes extensively\n");
796     for(attr=UNUM_PARSE_INT_ONLY; attr<= UNUM_PADDING_POSITION; attr=(UNumberFormatAttribute)((int32_t)attr + 1) )
797     {
798         newvalue=unum_getAttribute(fr, attr);
799         unum_setAttribute(def, attr, newvalue);
800         if(unum_getAttribute(def,attr)!=unum_getAttribute(fr, attr))
801             log_err("error in setting and getting attributes\n");
802         else
803             log_verbose("Pass: attributes set and retrieved successfully\n");
804     }
805 
806     /*testing spellout format to make sure we can use it successfully.*/
807     log_verbose("\nTesting spellout format\n");
808     if (spellout_def)
809     {
810         static const int32_t values[] = { 0, -5, 105, 1005, 105050 };
811         for (i = 0; i < UPRV_LENGTHOF(values); ++i) {
812             UChar buffer[128];
813             int32_t len;
814             int32_t value = values[i];
815             status = U_ZERO_ERROR;
816             len = unum_format(spellout_def, value, buffer, UPRV_LENGTHOF(buffer), NULL, &status);
817             if(U_FAILURE(status)) {
818                 log_err("Error in formatting using unum_format(spellout_fmt, ...): %s\n", myErrorName(status));
819             } else {
820                 int32_t pp = 0;
821                 int32_t parseResult;
822                 /*ustrToAstr(buffer, len, logbuf, UPRV_LENGTHOF(logbuf));*/
823                 log_verbose("formatted %d as '%s', length: %d\n", value, aescstrdup(buffer, len), len);
824 
825                 parseResult = unum_parse(spellout_def, buffer, len, &pp, &status);
826                 if (U_FAILURE(status)) {
827                     log_err("Error in parsing using unum_format(spellout_fmt, ...): %s\n", myErrorName(status));
828                 } else if (parseResult != value) {
829                     log_err("unum_format result %d != value %d\n", parseResult, value);
830                 }
831             }
832         }
833     }
834     else {
835         log_err("Spellout format is unavailable\n");
836     }
837 
838     {    /* Test for ticket #7079 */
839         UNumberFormat* dec_en;
840         UChar groupingSep[] = { 0 };
841         UChar numPercent[] = { 0x0031, 0x0032, 0x0025, 0 }; /* "12%" */
842         double parseResult = 0.0;
843 
844         status=U_ZERO_ERROR;
845         dec_en = unum_open(UNUM_DECIMAL, NULL, 0, "en_US", NULL, &status);
846         unum_setAttribute(dec_en, UNUM_LENIENT_PARSE, 0);
847         unum_setSymbol(dec_en, UNUM_GROUPING_SEPARATOR_SYMBOL, groupingSep, 0, &status);
848         parseResult = unum_parseDouble(dec_en, numPercent, -1, NULL, &status);
849         /* Without the fix in #7079, the above call will hang */
850         if ( U_FAILURE(status) || parseResult != 12.0 ) {
851             log_err("unum_parseDouble with empty groupingSep: status %s, parseResult %f not 12.0\n",
852                     myErrorName(status), parseResult);
853         } else {
854             log_verbose("unum_parseDouble with empty groupingSep: no hang, OK\n");
855         }
856         unum_close(dec_en);
857     }
858 
859     {   /* Test parse & format of big decimals.  Use a number with too many digits to fit in a double,
860                                          to verify that it is taking the pure decimal path. */
861         UNumberFormat *fmt;
862         const char *bdpattern = "#,##0.#########";
863         const char *numInitial     = "12345678900987654321.1234567896";
864         const char *numFormatted  = "12,345,678,900,987,654,321.12345679";
865         const char *parseExpected = "1.234567890098765432112345679E+19";
866         const char *parseExpected2 = "3.4567890098765432112345679E+17";
867         int32_t resultSize    = 0;
868         int32_t parsePos      = 0;     /* Output parameter for Parse operations. */
869         #define DESTCAPACITY 100
870         UChar dest[DESTCAPACITY];
871         char  desta[DESTCAPACITY];
872         UFieldPosition fieldPos = {0};
873 
874         /* Format */
875 
876         status = U_ZERO_ERROR;
877         u_uastrcpy(dest, bdpattern);
878         fmt = unum_open(UNUM_PATTERN_DECIMAL, dest, -1, "en", NULL /*parseError*/, &status);
879         if (U_FAILURE(status)) log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
880 
881         resultSize = unum_formatDecimal(fmt, numInitial, -1, dest, DESTCAPACITY, NULL, &status);
882         if (U_FAILURE(status)) {
883             log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
884         }
885         u_austrncpy(desta, dest, DESTCAPACITY);
886         if (strcmp(numFormatted, desta) != 0) {
887             log_err("File %s, Line %d, (expected, acutal) =  (\"%s\", \"%s\")\n",
888                     __FILE__, __LINE__, numFormatted, desta);
889         }
890         if ((int32_t)strlen(numFormatted) != resultSize) {
891             log_err("File %s, Line %d, (expected, actual) = (%d, %d)\n",
892                      __FILE__, __LINE__, (int32_t)strlen(numFormatted), resultSize);
893         }
894 
895         /* Format with a FieldPosition parameter */
896 
897         fieldPos.field = UNUM_DECIMAL_SEPARATOR_FIELD;
898         resultSize = unum_formatDecimal(fmt, numInitial, -1, dest, DESTCAPACITY, &fieldPos, &status);
899         if (U_FAILURE(status)) {
900             log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
901         }
902         u_austrncpy(desta, dest, DESTCAPACITY);
903         if (strcmp(numFormatted, desta) != 0) {
904             log_err("File %s, Line %d, (expected, acutal) =  (\"%s\", \"%s\")\n",
905                     __FILE__, __LINE__, numFormatted, desta);
906         }
907         if (fieldPos.beginIndex != 26) {  /* index of "." in formatted number */
908             log_err("File %s, Line %d, (expected, acutal) =  (%d, %d)\n",
909                     __FILE__, __LINE__, 0, fieldPos.beginIndex);
910         }
911         if (fieldPos.endIndex != 27) {
912             log_err("File %s, Line %d, (expected, acutal) =  (%d, %d)\n",
913                     __FILE__, __LINE__, 0, fieldPos.endIndex);
914         }
915 
916         /* Parse */
917 
918         status = U_ZERO_ERROR;
919         u_uastrcpy(dest, numFormatted);   /* Parse the expected output of the formatting test */
920         resultSize = unum_parseDecimal(fmt, dest, -1, NULL, desta, DESTCAPACITY, &status);
921         if (U_FAILURE(status)) {
922             log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
923         }
924         if (uprv_strcmp(parseExpected, desta) != 0) {
925             log_err("File %s, Line %d, (expected, actual) = (\"%s\", \"%s\")\n",
926                     __FILE__, __LINE__, parseExpected, desta);
927         } else {
928             log_verbose("File %s, Line %d, got expected = \"%s\"\n",
929                     __FILE__, __LINE__, desta);
930         }
931         if ((int32_t)strlen(parseExpected) != resultSize) {
932             log_err("File %s, Line %d, (expected, actual) = (%d, %d)\n",
933                     __FILE__, __LINE__, (int32_t)strlen(parseExpected), resultSize);
934         }
935 
936         /* Parse with a parsePos parameter */
937 
938         status = U_ZERO_ERROR;
939         u_uastrcpy(dest, numFormatted);   /* Parse the expected output of the formatting test */
940         parsePos = 3;                 /*      12,345,678,900,987,654,321.12345679         */
941                                       /* start parsing at the the third char              */
942         resultSize = unum_parseDecimal(fmt, dest, -1, &parsePos, desta, DESTCAPACITY, &status);
943         if (U_FAILURE(status)) {
944             log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
945         }
946         if (strcmp(parseExpected2, desta) != 0) {   /*  "3.4567890098765432112345679E+17" */
947             log_err("File %s, Line %d, (expected, actual) = (\"%s\", \"%s\")\n",
948                     __FILE__, __LINE__, parseExpected2, desta);
949         } else {
950             log_verbose("File %s, Line %d, got expected = \"%s\"\n",
951                     __FILE__, __LINE__, desta);
952         }
953         if ((int32_t)strlen(numFormatted) != parsePos) {
954             log_err("File %s, Line %d, parsePos (expected, actual) = (\"%d\", \"%d\")\n",
955                     __FILE__, __LINE__, (int32_t)strlen(parseExpected), parsePos);
956         }
957 
958         unum_close(fmt);
959     }
960 
961     status = U_ZERO_ERROR;
962     /* Test invalid symbol argument */
963     {
964         int32_t badsymbolLarge = UNUM_FORMAT_SYMBOL_COUNT + 1;
965         int32_t badsymbolSmall = -1;
966         UChar value[10];
967         int32_t valueLength = 10;
968         UNumberFormat *fmt = unum_open(UNUM_DEFAULT, NULL, 0, NULL, NULL, &status);
969         if (U_FAILURE(status)) {
970             log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status));
971         } else {
972             unum_getSymbol(fmt, (UNumberFormatSymbol)badsymbolLarge, NULL, 0, &status);
973             if (U_SUCCESS(status)) log_err("unum_getSymbol()'s status should be ILLEGAL_ARGUMENT with invalid symbol (> UNUM_FORMAT_SYMBOL_COUNT) argument\n");
974 
975             status = U_ZERO_ERROR;
976             unum_getSymbol(fmt, (UNumberFormatSymbol)badsymbolSmall, NULL, 0, &status);
977             if (U_SUCCESS(status)) log_err("unum_getSymbol()'s status should be ILLEGAL_ARGUMENT with invalid symbol (less than 0) argument\n");
978 
979             status = U_ZERO_ERROR;
980             unum_setSymbol(fmt, (UNumberFormatSymbol)badsymbolLarge, value, valueLength, &status);
981             if (U_SUCCESS(status)) log_err("unum_setSymbol()'s status should be ILLEGAL_ARGUMENT with invalid symbol (> UNUM_FORMAT_SYMBOL_COUNT) argument\n");
982 
983             status = U_ZERO_ERROR;
984             unum_setSymbol(fmt, (UNumberFormatSymbol)badsymbolSmall, value, valueLength, &status);
985             if (U_SUCCESS(status)) log_err("unum_setSymbol()'s status should be ILLEGAL_ARGUMENT with invalid symbol (less than 0) argument\n");
986 
987             unum_close(fmt);
988         }
989     }
990 
991 
992     /*closing the NumberFormat() using unum_close(UNumberFormat*)")*/
993     unum_close(def);
994     unum_close(fr);
995     unum_close(cur_def);
996     unum_close(cur_fr);
997     unum_close(per_def);
998     unum_close(per_fr);
999     unum_close(spellout_def);
1000     unum_close(pattern);
1001     unum_close(cur_frpattern);
1002     unum_close(myclone);
1003 
1004 }
1005 
TestParseZero(void)1006 static void TestParseZero(void)
1007 {
1008     UErrorCode errorCode = U_ZERO_ERROR;
1009     UChar input[] = {0x30, 0};   /*  Input text is decimal '0' */
1010     UChar pat[] = {0x0023,0x003b,0x0023,0}; /*  {'#', ';', '#', 0}; */
1011     double  dbl;
1012 
1013 #if 0
1014     UNumberFormat* unum = unum_open( UNUM_DECIMAL /*or UNUM_DEFAULT*/, NULL, -1, NULL, NULL, &errorCode);
1015 #else
1016     UNumberFormat* unum = unum_open( UNUM_PATTERN_DECIMAL /*needs pattern*/, pat, -1, NULL, NULL, &errorCode);
1017 #endif
1018 
1019     dbl = unum_parseDouble( unum, input, -1 /*u_strlen(input)*/, 0 /* 0 = start */, &errorCode );
1020     if (U_FAILURE(errorCode)) {
1021         log_data_err("Result - %s\n", u_errorName(errorCode));
1022     } else {
1023         log_verbose("Double: %f\n", dbl);
1024     }
1025     unum_close(unum);
1026 }
1027 
1028 static const UChar dollars2Sym[] = { 0x24,0x32,0x2E,0x30,0x30,0 }; /* $2.00 */
1029 static const UChar dollars4Sym[] = { 0x24,0x34,0 }; /* $4 */
1030 static const UChar dollarsUS4Sym[] = { 0x55,0x53,0x24,0x34,0 }; /* US$4 */
1031 static const UChar dollars9Sym[] = { 0x39,0xA0,0x24,0 }; /* 9 $ */
1032 static const UChar pounds3Sym[]  = { 0xA3,0x33,0x2E,0x30,0x30,0 }; /* [POUND]3.00 */
1033 static const UChar pounds5Sym[]  = { 0xA3,0x35,0 }; /* [POUND]5 */
1034 static const UChar pounds7Sym[]  = { 0x37,0xA0,0xA3,0 }; /* 7 [POUND] */
1035 static const UChar euros4Sym[]   = { 0x34,0x2C,0x30,0x30,0xA0,0x20AC,0 }; /* 4,00 [EURO] */
1036 static const UChar euros6Sym[]   = { 0x36,0xA0,0x20AC,0 }; /* 6 [EURO] */
1037 static const UChar euros8Sym[]   = { 0x20AC,0x38,0 }; /* [EURO]8 */
1038 static const UChar dollars4PluEn[] = { 0x34,0x20,0x55,0x53,0x20,0x64,0x6F,0x6C,0x6C,0x61,0x72,0x73,0 }; /* 4 US dollars*/
1039 static const UChar pounds5PluEn[]  = { 0x35,0x20,0x42,0x72,0x69,0x74,0x69,0x73,0x68,0x20,0x70,0x6F,0x75,0x6E,0x64,0x73,0x20,0x73,0x74,0x65,0x72,0x6C,0x69,0x6E,0x67,0 }; /* 5 British pounds sterling */
1040 static const UChar euros8PluEn[]   = { 0x38,0x20,0x65,0x75,0x72,0x6F,0x73,0 }; /* 8 euros*/
1041 static const UChar euros6PluFr[]   = { 0x36,0x20,0x65,0x75,0x72,0x6F,0x73,0 }; /* 6 euros*/
1042 
1043 typedef struct {
1044     const char *  locale;
1045     const char *  descrip;
1046     const UChar * currStr;
1047     const UChar * plurStr;
1048     UErrorCode    parsDoubExpectErr;
1049     int32_t       parsDoubExpectPos;
1050     double        parsDoubExpectVal;
1051     UErrorCode    parsCurrExpectErr;
1052     int32_t       parsCurrExpectPos;
1053     double        parsCurrExpectVal;
1054     const char *  parsCurrExpectCurr;
1055 } ParseCurrencyItem;
1056 
1057 static const ParseCurrencyItem parseCurrencyItems[] = {
1058     { "en_US", "dollars2", dollars2Sym, NULL,           U_ZERO_ERROR,  5, 2.0, U_ZERO_ERROR,  5, 2.0, "USD" },
1059     { "en_US", "dollars4", dollars4Sym, dollars4PluEn,  U_ZERO_ERROR,  2, 4.0, U_ZERO_ERROR,  2, 4.0, "USD" },
1060     { "en_US", "dollars9", dollars9Sym, NULL,           U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, ""    },
1061     { "en_US", "pounds3",  pounds3Sym,  NULL,           U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR,  5, 3.0, "GBP" },
1062     { "en_US", "pounds5",  pounds5Sym,  pounds5PluEn,   U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR,  2, 5.0, "GBP" },
1063     { "en_US", "pounds7",  pounds7Sym,  NULL,           U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, ""    },
1064     { "en_US", "euros8",   euros8Sym,   euros8PluEn,    U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR,  2, 8.0, "EUR" },
1065 
1066     { "en_GB", "pounds3",  pounds3Sym,  NULL,           U_ZERO_ERROR,  5, 3.0, U_ZERO_ERROR,  5, 3.0, "GBP" },
1067     { "en_GB", "pounds5",  pounds5Sym,  pounds5PluEn,   U_ZERO_ERROR,  2, 5.0, U_ZERO_ERROR,  2, 5.0, "GBP" },
1068     { "en_GB", "pounds7",  pounds7Sym,  NULL,           U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, ""    },
1069     { "en_GB", "euros4",   euros4Sym,   NULL,           U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, ""    },
1070     { "en_GB", "euros6",   euros6Sym,   NULL,           U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, ""    },
1071     { "en_GB", "euros8",   euros8Sym,    euros8PluEn,   U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR,  2, 8.0, "EUR" },
1072     { "en_GB", "dollars4", dollarsUS4Sym,dollars4PluEn, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR,  4, 4.0, "USD" },
1073 
1074     { "fr_FR", "euros4",   euros4Sym,   NULL,           U_ZERO_ERROR,  6, 4.0, U_ZERO_ERROR,  6, 4.0, "EUR" },
1075     { "fr_FR", "euros6",   euros6Sym,   euros6PluFr,    U_ZERO_ERROR,  3, 6.0, U_ZERO_ERROR,  3, 6.0, "EUR" },
1076     { "fr_FR", "euros8",   euros8Sym,   NULL,           U_PARSE_ERROR, 2, 0.0, U_PARSE_ERROR, 2, 0.0, ""    },
1077     { "fr_FR", "dollars2", dollars2Sym, NULL,           U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, ""    },
1078     { "fr_FR", "dollars4", dollars4Sym, NULL,           U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, ""    },
1079 
1080     { NULL,    NULL,       NULL,        NULL,           0,             0, 0.0, 0,             0, 0.0, NULL  }
1081 };
1082 
TestParseCurrency()1083 static void TestParseCurrency()
1084 {
1085     const ParseCurrencyItem * itemPtr;
1086     for (itemPtr = parseCurrencyItems; itemPtr->locale != NULL; ++itemPtr) {
1087         UNumberFormat* unum;
1088         UErrorCode status;
1089         double parseVal;
1090         int32_t parsePos;
1091         UChar parseCurr[4];
1092         char parseCurrB[4];
1093 
1094         status = U_ZERO_ERROR;
1095         unum = unum_open(UNUM_CURRENCY, NULL, 0, itemPtr->locale, NULL, &status);
1096         if (U_SUCCESS(status)) {
1097             const UChar * currStr = itemPtr->currStr;
1098             status = U_ZERO_ERROR;
1099             parsePos = 0;
1100             parseVal = unum_parseDouble(unum, currStr, -1, &parsePos, &status);
1101             if (status != itemPtr->parsDoubExpectErr || parsePos != itemPtr->parsDoubExpectPos || parseVal != itemPtr->parsDoubExpectVal) {
1102                 log_err("UNUM_CURRENCY parseDouble %s/%s, expect %s pos %d val %.1f, get %s pos %d val %.1f\n",
1103                         itemPtr->locale, itemPtr->descrip,
1104                         u_errorName(itemPtr->parsDoubExpectErr), itemPtr->parsDoubExpectPos, itemPtr->parsDoubExpectVal,
1105                         u_errorName(status), parsePos, parseVal );
1106             }
1107             status = U_ZERO_ERROR;
1108             parsePos = 0;
1109             parseCurr[0] = 0;
1110             parseVal = unum_parseDoubleCurrency(unum, currStr, -1, &parsePos, parseCurr, &status);
1111             u_austrncpy(parseCurrB, parseCurr, 4);
1112             if (status != itemPtr->parsCurrExpectErr || parsePos != itemPtr->parsCurrExpectPos || parseVal != itemPtr->parsCurrExpectVal ||
1113                     strncmp(parseCurrB, itemPtr->parsCurrExpectCurr, 4) != 0) {
1114                 log_err("UNUM_CURRENCY parseDoubleCurrency %s/%s, expect %s pos %d val %.1f cur %s, get %s pos %d val %.1f cur %s\n",
1115                         itemPtr->locale, itemPtr->descrip,
1116                         u_errorName(itemPtr->parsCurrExpectErr), itemPtr->parsCurrExpectPos, itemPtr->parsCurrExpectVal, itemPtr->parsCurrExpectCurr,
1117                         u_errorName(status), parsePos, parseVal, parseCurrB );
1118             }
1119             unum_close(unum);
1120         } else {
1121             log_data_err("unexpected error in unum_open UNUM_CURRENCY for locale %s: '%s'\n", itemPtr->locale, u_errorName(status));
1122         }
1123 
1124         if (itemPtr->plurStr != NULL) {
1125             status = U_ZERO_ERROR;
1126             unum = unum_open(UNUM_CURRENCY_PLURAL, NULL, 0, itemPtr->locale, NULL, &status);
1127             if (U_SUCCESS(status)) {
1128                 status = U_ZERO_ERROR;
1129                 parsePos = 0;
1130                 parseVal = unum_parseDouble(unum, itemPtr->plurStr, -1, &parsePos, &status);
1131                 if (status != itemPtr->parsDoubExpectErr || parseVal != itemPtr->parsDoubExpectVal) {
1132                     log_err("UNUM_CURRENCY parseDouble Plural %s/%s, expect %s val %.1f, get %s val %.1f\n",
1133                             itemPtr->locale, itemPtr->descrip,
1134                             u_errorName(itemPtr->parsDoubExpectErr), itemPtr->parsDoubExpectVal,
1135                             u_errorName(status), parseVal );
1136                 }
1137                 status = U_ZERO_ERROR;
1138                 parsePos = 0;
1139                 parseCurr[0] = 0;
1140                 parseVal = unum_parseDoubleCurrency(unum, itemPtr->plurStr, -1, &parsePos, parseCurr, &status);
1141                 u_austrncpy(parseCurrB, parseCurr, 4);
1142                 if (status != itemPtr->parsCurrExpectErr || parseVal != itemPtr->parsCurrExpectVal ||
1143                         strncmp(parseCurrB, itemPtr->parsCurrExpectCurr, 4) != 0) {
1144                     log_err("UNUM_CURRENCY parseDoubleCurrency Plural %s/%s, expect %s val %.1f cur %s, get %s val %.1f cur %s\n",
1145                             itemPtr->locale, itemPtr->descrip,
1146                             u_errorName(itemPtr->parsCurrExpectErr), itemPtr->parsCurrExpectVal, itemPtr->parsCurrExpectCurr,
1147                             u_errorName(status), parseVal, parseCurrB );
1148                 }
1149                 unum_close(unum);
1150             } else {
1151                 log_data_err("unexpected error in unum_open UNUM_CURRENCY_PLURAL for locale %s: '%s'\n", itemPtr->locale, u_errorName(status));
1152             }
1153         }
1154     }
1155 }
1156 
1157 typedef struct {
1158     const char *  testname;
1159     const char *  locale;
1160     const UChar * source;
1161     int32_t       startPos;
1162     int32_t       value;
1163     int32_t       endPos;
1164     UErrorCode    status;
1165 } SpelloutParseTest;
1166 
1167 static const UChar ustr_en0[]   = {0x7A, 0x65, 0x72, 0x6F, 0}; /* zero */
1168 static const UChar ustr_123[]   = {0x31, 0x32, 0x33, 0};       /* 123 */
1169 static const UChar ustr_en123[] = {0x6f, 0x6e, 0x65, 0x20, 0x68, 0x75, 0x6e, 0x64, 0x72, 0x65, 0x64,
1170                                    0x20, 0x74, 0x77, 0x65, 0x6e, 0x74, 0x79,
1171                                    0x2d, 0x74, 0x68, 0x72, 0x65, 0x65, 0}; /* one hundred twenty-three */
1172 static const UChar ustr_fr123[] = {0x63, 0x65, 0x6e, 0x74, 0x20, 0x76, 0x69, 0x6e, 0x67, 0x74, 0x2d,
1173                                    0x74, 0x72, 0x6f, 0x69, 0x73, 0};       /* cent vingt-trois */
1174 static const UChar ustr_ja123[] = {0x767e, 0x4e8c, 0x5341, 0x4e09, 0};     /* kanji 100(+)2(*)10(+)3 */
1175 
1176 static const SpelloutParseTest spelloutParseTests[] = {
1177     /* name    loc   src       start val  end status */
1178     { "en0",   "en", ustr_en0,    0,   0,  4, U_ZERO_ERROR },
1179     { "en0",   "en", ustr_en0,    2,   0,  2, U_PARSE_ERROR },
1180     { "en0",   "ja", ustr_en0,    0,   0,  0, U_PARSE_ERROR },
1181     { "123",   "en", ustr_123,    0, 123,  3, U_ZERO_ERROR },
1182     { "en123", "en", ustr_en123,  0, 123, 24, U_ZERO_ERROR },
1183     { "en123", "en", ustr_en123, 12,  23, 24, U_ZERO_ERROR },
1184     { "en123", "fr", ustr_en123, 16,   0, 16, U_PARSE_ERROR },
1185     { "fr123", "fr", ustr_fr123,  0, 123, 16, U_ZERO_ERROR },
1186     { "fr123", "fr", ustr_fr123,  5,  23, 16, U_ZERO_ERROR },
1187     { "fr123", "en", ustr_fr123,  0,   0,  0, U_PARSE_ERROR },
1188     { "ja123", "ja", ustr_ja123,  0, 123,  4, U_ZERO_ERROR },
1189     { "ja123", "ja", ustr_ja123,  1,  23,  4, U_ZERO_ERROR },
1190     { "ja123", "fr", ustr_ja123,  0,   0,  0, U_PARSE_ERROR },
1191     { NULL,    NULL, NULL,        0,   0,  0, 0 } /* terminator */
1192 };
1193 
TestSpelloutNumberParse()1194 static void TestSpelloutNumberParse()
1195 {
1196     const SpelloutParseTest * testPtr;
1197     for (testPtr = spelloutParseTests; testPtr->testname != NULL; ++testPtr) {
1198         UErrorCode status = U_ZERO_ERROR;
1199         int32_t value, position = testPtr->startPos;
1200         UNumberFormat *nf = unum_open(UNUM_SPELLOUT, NULL, 0, testPtr->locale, NULL, &status);
1201         if (U_FAILURE(status)) {
1202             log_err_status(status, "unum_open fails for UNUM_SPELLOUT with locale %s, status %s\n", testPtr->locale, myErrorName(status));
1203             continue;
1204         }
1205         status = U_ZERO_ERROR;
1206         value = unum_parse(nf, testPtr->source, -1, &position, &status);
1207         if ( value != testPtr->value || position != testPtr->endPos || status != testPtr->status ) {
1208             log_err("unum_parse SPELLOUT, locale %s, testname %s, startPos %d: for value / endPos / status, expected %d / %d / %s, got %d / %d / %s\n",
1209                     testPtr->locale, testPtr->testname, testPtr->startPos,
1210                     testPtr->value, testPtr->endPos, myErrorName(testPtr->status),
1211                     value, position, myErrorName(status) );
1212         }
1213         unum_close(nf);
1214     }
1215 }
1216 
TestSignificantDigits()1217 static void TestSignificantDigits()
1218 {
1219     UChar temp[128];
1220     int32_t resultlengthneeded;
1221     int32_t resultlength;
1222     UErrorCode status = U_ZERO_ERROR;
1223     UChar *result = NULL;
1224     UNumberFormat* fmt;
1225     double d = 123456.789;
1226 
1227     u_uastrcpy(temp, "###0.0#");
1228     fmt=unum_open(UNUM_IGNORE, temp, -1, "en", NULL, &status);
1229     if (U_FAILURE(status)) {
1230         log_data_err("got unexpected error for unum_open: '%s'\n", u_errorName(status));
1231         return;
1232     }
1233     unum_setAttribute(fmt, UNUM_SIGNIFICANT_DIGITS_USED, TRUE);
1234     unum_setAttribute(fmt, UNUM_MAX_SIGNIFICANT_DIGITS, 6);
1235 
1236     u_uastrcpy(temp, "123457");
1237     resultlength=0;
1238     resultlengthneeded=unum_formatDouble(fmt, d, NULL, resultlength, NULL, &status);
1239     if(status==U_BUFFER_OVERFLOW_ERROR)
1240     {
1241         status=U_ZERO_ERROR;
1242         resultlength=resultlengthneeded+1;
1243         result=(UChar*)malloc(sizeof(UChar) * resultlength);
1244         unum_formatDouble(fmt, d, result, resultlength, NULL, &status);
1245     }
1246     if(U_FAILURE(status))
1247     {
1248         log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status));
1249         return;
1250     }
1251     if(u_strcmp(result, temp)==0)
1252         log_verbose("Pass: Number Formatting using unum_formatDouble() Successful\n");
1253     else
1254         log_err("FAIL: Error in number formatting using unum_formatDouble()\n");
1255     free(result);
1256     unum_close(fmt);
1257 }
1258 
TestSigDigRounding()1259 static void TestSigDigRounding()
1260 {
1261     UErrorCode status = U_ZERO_ERROR;
1262     UChar expected[128];
1263     UChar result[128];
1264     char  temp1[128];
1265     char  temp2[128];
1266     UNumberFormat* fmt;
1267     double d = 123.4;
1268 
1269     fmt=unum_open(UNUM_DECIMAL, NULL, 0, "en", NULL, &status);
1270     if (U_FAILURE(status)) {
1271         log_data_err("got unexpected error for unum_open: '%s'\n", u_errorName(status));
1272         return;
1273     }
1274     unum_setAttribute(fmt, UNUM_LENIENT_PARSE, FALSE);
1275     unum_setAttribute(fmt, UNUM_SIGNIFICANT_DIGITS_USED, TRUE);
1276     unum_setAttribute(fmt, UNUM_MAX_SIGNIFICANT_DIGITS, 2);
1277     /* unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 0); */
1278 
1279     unum_setAttribute(fmt, UNUM_ROUNDING_MODE, UNUM_ROUND_UP);
1280     unum_setDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT, 20.0);
1281 
1282     (void)unum_formatDouble(fmt, d, result, UPRV_LENGTHOF(result), NULL, &status);
1283     if(U_FAILURE(status))
1284     {
1285         log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status));
1286         return;
1287     }
1288 
1289     u_uastrcpy(expected, "140");
1290     if(u_strcmp(result, expected)!=0)
1291         log_err("FAIL: Error in unum_formatDouble result %s instead of %s\n", u_austrcpy(temp1, result), u_austrcpy(temp2, expected) );
1292 
1293     unum_close(fmt);
1294 }
1295 
TestNumberFormatPadding()1296 static void TestNumberFormatPadding()
1297 {
1298     UChar *result=NULL;
1299     UChar temp1[512];
1300     UChar temp2[512];
1301 
1302     UErrorCode status=U_ZERO_ERROR;
1303     int32_t resultlength;
1304     int32_t resultlengthneeded;
1305     UNumberFormat *pattern;
1306     double d1;
1307     double d = -10456.37;
1308     UFieldPosition pos1;
1309     int32_t parsepos;
1310 
1311     /* create a number format using unum_openPattern(....)*/
1312     log_verbose("\nTesting unum_openPattern() with padding\n");
1313     u_uastrcpy(temp1, "*#,##0.0#*;(#,##0.0#)");
1314     status=U_ZERO_ERROR;
1315     pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), NULL, NULL,&status);
1316     if(U_SUCCESS(status))
1317     {
1318         log_err("error in unum_openPattern(%s): %s\n", temp1, myErrorName(status) );
1319     }
1320     else
1321     {
1322         unum_close(pattern);
1323     }
1324 
1325 /*    u_uastrcpy(temp1, "*x#,###,###,##0.0#;(*x#,###,###,##0.0#)"); */
1326     u_uastrcpy(temp1, "*x#,###,###,##0.0#;*x(###,###,##0.0#)"); // input pattern
1327     u_uastrcpy(temp2, "*x#########,##0.0#;(#########,##0.0#)"); // equivalent (?) output pattern
1328     status=U_ZERO_ERROR;
1329     pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), "en_US",NULL, &status);
1330     if(U_FAILURE(status))
1331     {
1332         log_err_status(status, "error in padding unum_openPattern(%s): %s\n", temp1, myErrorName(status) );
1333     }
1334     else {
1335         log_verbose("Pass: padding unum_openPattern() works fine\n");
1336 
1337         /*test for unum_toPattern()*/
1338         log_verbose("\nTesting padding unum_toPattern()\n");
1339         resultlength=0;
1340         resultlengthneeded=unum_toPattern(pattern, FALSE, NULL, resultlength, &status);
1341         if(status==U_BUFFER_OVERFLOW_ERROR)
1342         {
1343             status=U_ZERO_ERROR;
1344             resultlength=resultlengthneeded+1;
1345             result=(UChar*)malloc(sizeof(UChar) * resultlength);
1346             unum_toPattern(pattern, FALSE, result, resultlength, &status);
1347         }
1348         if(U_FAILURE(status))
1349         {
1350             log_err("error in extracting the padding pattern from UNumberFormat: %s\n", myErrorName(status));
1351         }
1352         else
1353         {
1354             if(u_strncmp(result, temp2, resultlengthneeded)!=0) {
1355                 log_err(
1356                         "FAIL: Error in extracting the padding pattern using unum_toPattern(): %d: %s != %s\n",
1357                         resultlengthneeded,
1358                         austrdup(temp2),
1359                         austrdup(result));
1360             } else {
1361                 log_verbose("Pass: extracted the padding pattern correctly using unum_toPattern()\n");
1362             }
1363         }
1364         free(result);
1365 /*        u_uastrcpy(temp1, "(xxxxxxx10,456.37)"); */
1366         u_uastrcpy(temp1, "xxxxx(10,456.37)");
1367         resultlength=0;
1368         pos1.field = UNUM_FRACTION_FIELD;
1369         resultlengthneeded=unum_formatDouble(pattern, d, NULL, resultlength, &pos1, &status);
1370         if(status==U_BUFFER_OVERFLOW_ERROR)
1371         {
1372             status=U_ZERO_ERROR;
1373             resultlength=resultlengthneeded+1;
1374             result=(UChar*)malloc(sizeof(UChar) * resultlength);
1375             unum_formatDouble(pattern, d, result, resultlength, NULL, &status);
1376         }
1377         if(U_FAILURE(status))
1378         {
1379             log_err("Error in formatting using unum_formatDouble(.....) with padding : %s\n", myErrorName(status));
1380         }
1381         else
1382         {
1383             if(u_strcmp(result, temp1)==0)
1384                 log_verbose("Pass: Number Formatting using unum_formatDouble() padding Successful\n");
1385             else
1386                 log_data_err("FAIL: Error in number formatting using unum_formatDouble() with padding\n");
1387             if(pos1.beginIndex == 13 && pos1.endIndex == 15)
1388                 log_verbose("Pass: Complete number formatting using unum_formatDouble() successful\n");
1389             else
1390                 log_err("Fail: Error in complete number Formatting using unum_formatDouble()\nGot: b=%d end=%d\nExpected: b=13 end=15\n",
1391                         pos1.beginIndex, pos1.endIndex);
1392 
1393 
1394             /* Testing unum_parse() and unum_parseDouble() */
1395             log_verbose("\nTesting padding unum_parseDouble()\n");
1396             parsepos=0;
1397             d1=unum_parseDouble(pattern, result, u_strlen(result), &parsepos, &status);
1398             if(U_FAILURE(status))
1399             {
1400                 log_err("padding parse failed. The error is : %s\n", myErrorName(status));
1401             }
1402 
1403             if(d1!=d)
1404                 log_err("Fail: Error in padding parsing\n");
1405             else
1406                 log_verbose("Pass: padding parsing successful\n");
1407 free(result);
1408         }
1409     }
1410 
1411     unum_close(pattern);
1412 }
1413 
1414 static UBool
withinErr(double a,double b,double err)1415 withinErr(double a, double b, double err) {
1416     return uprv_fabs(a - b) < uprv_fabs(a * err);
1417 }
1418 
TestInt64Format()1419 static void TestInt64Format() {
1420     UChar temp1[512];
1421     UChar result[512];
1422     UNumberFormat *fmt;
1423     UErrorCode status = U_ZERO_ERROR;
1424     const double doubleInt64Max = (double)U_INT64_MAX;
1425     const double doubleInt64Min = (double)U_INT64_MIN;
1426     const double doubleBig = 10.0 * (double)U_INT64_MAX;
1427     int32_t val32;
1428     int64_t val64;
1429     double  valDouble;
1430     int32_t parsepos;
1431 
1432     /* create a number format using unum_openPattern(....) */
1433     log_verbose("\nTesting Int64Format\n");
1434     u_uastrcpy(temp1, "#.#E0");
1435     fmt = unum_open(UNUM_IGNORE, temp1, u_strlen(temp1), "en_US", NULL, &status);
1436     if(U_FAILURE(status)) {
1437         log_data_err("error in unum_openPattern() - %s\n", myErrorName(status));
1438     } else {
1439         unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 20);
1440         unum_formatInt64(fmt, U_INT64_MAX, result, 512, NULL, &status);
1441         if (U_FAILURE(status)) {
1442             log_err("error in unum_format(): %s\n", myErrorName(status));
1443         } else {
1444             log_verbose("format int64max: '%s'\n", result);
1445             parsepos = 0;
1446             val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status);
1447             if (status != U_INVALID_FORMAT_ERROR) {
1448                 log_err("parse didn't report error: %s\n", myErrorName(status));
1449             } else if (val32 != INT32_MAX) {
1450                 log_err("parse didn't pin return value, got: %d\n", val32);
1451             }
1452 
1453             status = U_ZERO_ERROR;
1454             parsepos = 0;
1455             val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status);
1456             if (U_FAILURE(status)) {
1457                 log_err("parseInt64 returned error: %s\n", myErrorName(status));
1458             } else if (val64 != U_INT64_MAX) {
1459                 log_err("parseInt64 returned incorrect value, got: %ld\n", val64);
1460             }
1461 
1462             status = U_ZERO_ERROR;
1463             parsepos = 0;
1464             valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status);
1465             if (U_FAILURE(status)) {
1466                 log_err("parseDouble returned error: %s\n", myErrorName(status));
1467             } else if (valDouble != doubleInt64Max) {
1468                 log_err("parseDouble returned incorrect value, got: %g\n", valDouble);
1469             }
1470         }
1471 
1472         unum_formatInt64(fmt, U_INT64_MIN, result, 512, NULL, &status);
1473         if (U_FAILURE(status)) {
1474             log_err("error in unum_format(): %s\n", myErrorName(status));
1475         } else {
1476             log_verbose("format int64min: '%s'\n", result);
1477             parsepos = 0;
1478             val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status);
1479             if (status != U_INVALID_FORMAT_ERROR) {
1480                 log_err("parse didn't report error: %s\n", myErrorName(status));
1481             } else if (val32 != INT32_MIN) {
1482                 log_err("parse didn't pin return value, got: %d\n", val32);
1483             }
1484 
1485             status = U_ZERO_ERROR;
1486             parsepos = 0;
1487             val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status);
1488             if (U_FAILURE(status)) {
1489                 log_err("parseInt64 returned error: %s\n", myErrorName(status));
1490             } else if (val64 != U_INT64_MIN) {
1491                 log_err("parseInt64 returned incorrect value, got: %ld\n", val64);
1492             }
1493 
1494             status = U_ZERO_ERROR;
1495             parsepos = 0;
1496             valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status);
1497             if (U_FAILURE(status)) {
1498                 log_err("parseDouble returned error: %s\n", myErrorName(status));
1499             } else if (valDouble != doubleInt64Min) {
1500                 log_err("parseDouble returned incorrect value, got: %g\n", valDouble);
1501             }
1502         }
1503 
1504         unum_formatDouble(fmt, doubleBig, result, 512, NULL, &status);
1505         if (U_FAILURE(status)) {
1506             log_err("error in unum_format(): %s\n", myErrorName(status));
1507         } else {
1508             log_verbose("format doubleBig: '%s'\n", result);
1509             parsepos = 0;
1510             val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status);
1511             if (status != U_INVALID_FORMAT_ERROR) {
1512                 log_err("parse didn't report error: %s\n", myErrorName(status));
1513             } else if (val32 != INT32_MAX) {
1514                 log_err("parse didn't pin return value, got: %d\n", val32);
1515             }
1516 
1517             status = U_ZERO_ERROR;
1518             parsepos = 0;
1519             val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status);
1520             if (status != U_INVALID_FORMAT_ERROR) {
1521                 log_err("parseInt64 didn't report error error: %s\n", myErrorName(status));
1522             } else if (val64 != U_INT64_MAX) {
1523                 log_err("parseInt64 returned incorrect value, got: %ld\n", val64);
1524             }
1525 
1526             status = U_ZERO_ERROR;
1527             parsepos = 0;
1528             valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status);
1529             if (U_FAILURE(status)) {
1530                 log_err("parseDouble returned error: %s\n", myErrorName(status));
1531             } else if (!withinErr(valDouble, doubleBig, 1e-15)) {
1532                 log_err("parseDouble returned incorrect value, got: %g\n", valDouble);
1533             }
1534         }
1535 
1536         u_uastrcpy(result, "5.06e-27");
1537         parsepos = 0;
1538         valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status);
1539         if (U_FAILURE(status)) {
1540             log_err("parseDouble() returned error: %s\n", myErrorName(status));
1541         } else if (!withinErr(valDouble, 5.06e-27, 1e-15)) {
1542             log_err("parseDouble() returned incorrect value, got: %g\n", valDouble);
1543         }
1544     }
1545     unum_close(fmt);
1546 }
1547 
1548 
test_fmt(UNumberFormat * fmt,UBool isDecimal)1549 static void test_fmt(UNumberFormat* fmt, UBool isDecimal) {
1550     char temp[512];
1551     UChar buffer[512];
1552     int32_t BUFSIZE = UPRV_LENGTHOF(buffer);
1553     double vals[] = {
1554         -.2, 0, .2, 5.5, 15.2, 250, 123456789
1555     };
1556     int i;
1557 
1558     for (i = 0; i < UPRV_LENGTHOF(vals); ++i) {
1559         UErrorCode status = U_ZERO_ERROR;
1560         unum_formatDouble(fmt, vals[i], buffer, BUFSIZE, NULL, &status);
1561         if (U_FAILURE(status)) {
1562             log_err("failed to format: %g, returned %s\n", vals[i], u_errorName(status));
1563         } else {
1564             u_austrcpy(temp, buffer);
1565             log_verbose("formatting %g returned '%s'\n", vals[i], temp);
1566         }
1567     }
1568 
1569     /* check APIs now */
1570     {
1571         UErrorCode status = U_ZERO_ERROR;
1572         UParseError perr;
1573         u_uastrcpy(buffer, "#,##0.0#");
1574         unum_applyPattern(fmt, FALSE, buffer, -1, &perr, &status);
1575         if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) {
1576             log_err("got unexpected error for applyPattern: '%s'\n", u_errorName(status));
1577         }
1578     }
1579 
1580     {
1581         int isLenient = unum_getAttribute(fmt, UNUM_LENIENT_PARSE);
1582         log_verbose("lenient: 0x%x\n", isLenient);
1583         if (isLenient != FALSE) {
1584             log_err("didn't expect lenient value: %d\n", isLenient);
1585         }
1586 
1587         unum_setAttribute(fmt, UNUM_LENIENT_PARSE, TRUE);
1588         isLenient = unum_getAttribute(fmt, UNUM_LENIENT_PARSE);
1589         if (isLenient != TRUE) {
1590             log_err("didn't expect lenient value after set: %d\n", isLenient);
1591         }
1592     }
1593 
1594     {
1595         double val2;
1596         double val = unum_getDoubleAttribute(fmt, UNUM_LENIENT_PARSE);
1597         if (val != -1) {
1598             log_err("didn't expect double attribute\n");
1599         }
1600         val = unum_getDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT);
1601         if ((val == -1) == isDecimal) {
1602             log_err("didn't expect -1 rounding increment\n");
1603         }
1604         unum_setDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT, val+.5);
1605         val2 = unum_getDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT);
1606         if (isDecimal && (val2 - val != .5)) {
1607             log_err("set rounding increment had no effect on decimal format");
1608         }
1609     }
1610 
1611     {
1612         UErrorCode status = U_ZERO_ERROR;
1613         int len = unum_getTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, BUFSIZE, &status);
1614         if (isDecimal ? (status != U_UNSUPPORTED_ERROR) : U_FAILURE(status)) {
1615             log_err("got unexpected error for get default ruleset: '%s'\n", u_errorName(status));
1616         }
1617         if (U_SUCCESS(status)) {
1618             u_austrcpy(temp, buffer);
1619             log_verbose("default ruleset: '%s'\n", temp);
1620         }
1621 
1622         status = U_ZERO_ERROR;
1623         len = unum_getTextAttribute(fmt, UNUM_PUBLIC_RULESETS, buffer, BUFSIZE, &status);
1624         if (isDecimal ? (status != U_UNSUPPORTED_ERROR) : U_FAILURE(status)) {
1625             log_err("got unexpected error for get public rulesets: '%s'\n", u_errorName(status));
1626         }
1627         if (U_SUCCESS(status)) {
1628             u_austrcpy(temp, buffer);
1629             log_verbose("public rulesets: '%s'\n", temp);
1630 
1631             /* set the default ruleset to the first one found, and retry */
1632 
1633             if (len > 0) {
1634                 for (i = 0; i < len && temp[i] != ';'; ++i){}
1635                 if (i < len) {
1636                     buffer[i] = 0;
1637                     unum_setTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, -1, &status);
1638                     if (U_FAILURE(status)) {
1639                         log_err("unexpected error setting default ruleset: '%s'\n", u_errorName(status));
1640                     } else {
1641                         int len2 = unum_getTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, BUFSIZE, &status);
1642                         if (U_FAILURE(status)) {
1643                             log_err("could not fetch default ruleset: '%s'\n", u_errorName(status));
1644                         } else if (len2 != i) {
1645                             u_austrcpy(temp, buffer);
1646                             log_err("unexpected ruleset len: %d ex: %d val: %s\n", len2, i, temp);
1647                         } else {
1648                             for (i = 0; i < UPRV_LENGTHOF(vals); ++i) {
1649                                 status = U_ZERO_ERROR;
1650                                 unum_formatDouble(fmt, vals[i], buffer, BUFSIZE, NULL, &status);
1651                                 if (U_FAILURE(status)) {
1652                                     log_err("failed to format: %g, returned %s\n", vals[i], u_errorName(status));
1653                                 } else {
1654                                     u_austrcpy(temp, buffer);
1655                                     log_verbose("formatting %g returned '%s'\n", vals[i], temp);
1656                                 }
1657                             }
1658                         }
1659                     }
1660                 }
1661             }
1662         }
1663     }
1664 
1665     {
1666         UErrorCode status = U_ZERO_ERROR;
1667         unum_toPattern(fmt, FALSE, buffer, BUFSIZE, &status);
1668         if (U_SUCCESS(status)) {
1669             u_austrcpy(temp, buffer);
1670             log_verbose("pattern: '%s'\n", temp);
1671         } else if (status != U_BUFFER_OVERFLOW_ERROR) {
1672             log_err("toPattern failed unexpectedly: %s\n", u_errorName(status));
1673         } else {
1674             log_verbose("pattern too long to display\n");
1675         }
1676     }
1677 
1678     {
1679         UErrorCode status = U_ZERO_ERROR;
1680         int len = unum_getSymbol(fmt, UNUM_CURRENCY_SYMBOL, buffer, BUFSIZE, &status);
1681         if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) {
1682             log_err("unexpected error getting symbol: '%s'\n", u_errorName(status));
1683         }
1684 
1685         unum_setSymbol(fmt, UNUM_CURRENCY_SYMBOL, buffer, len, &status);
1686         if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) {
1687             log_err("unexpected error setting symbol: '%s'\n", u_errorName(status));
1688         }
1689     }
1690 }
1691 
TestNonExistentCurrency()1692 static void TestNonExistentCurrency() {
1693     UNumberFormat *format;
1694     UErrorCode status = U_ZERO_ERROR;
1695     UChar currencySymbol[8];
1696     static const UChar QQQ[] = {0x51, 0x51, 0x51, 0};
1697 
1698     /* Get a non-existent currency and make sure it returns the correct currency code. */
1699     format = unum_open(UNUM_CURRENCY, NULL, 0, "th_TH@currency=QQQ", NULL, &status);
1700     if (format == NULL || U_FAILURE(status)) {
1701         log_data_err("unum_open did not return expected result for non-existent requested currency: '%s' (Are you missing data?)\n", u_errorName(status));
1702     }
1703     else {
1704         unum_getSymbol(format,
1705                 UNUM_CURRENCY_SYMBOL,
1706                 currencySymbol,
1707                 UPRV_LENGTHOF(currencySymbol),
1708                 &status);
1709         if (u_strcmp(currencySymbol, QQQ) != 0) {
1710             log_err("unum_open set the currency to QQQ\n");
1711         }
1712     }
1713     unum_close(format);
1714 }
1715 
TestRBNFFormat()1716 static void TestRBNFFormat() {
1717     UErrorCode status;
1718     UParseError perr;
1719     UChar pat[1024];
1720     UChar tempUChars[512];
1721     UNumberFormat *formats[5];
1722     int COUNT = UPRV_LENGTHOF(formats);
1723     int i;
1724 
1725     for (i = 0; i < COUNT; ++i) {
1726         formats[i] = 0;
1727     }
1728 
1729     /* instantiation */
1730     status = U_ZERO_ERROR;
1731     u_uastrcpy(pat, "#,##0.0#;(#,##0.0#)");
1732     formats[0] = unum_open(UNUM_PATTERN_DECIMAL, pat, -1, "en_US", &perr, &status);
1733     if (U_FAILURE(status)) {
1734         log_err_status(status, "unable to open decimal pattern -> %s\n", u_errorName(status));
1735         return;
1736     }
1737 
1738     status = U_ZERO_ERROR;
1739     formats[1] = unum_open(UNUM_SPELLOUT, NULL, 0, "en_US", &perr, &status);
1740     if (U_FAILURE(status)) {
1741         log_err_status(status, "unable to open spellout -> %s\n", u_errorName(status));
1742         return;
1743     }
1744 
1745     status = U_ZERO_ERROR;
1746     formats[2] = unum_open(UNUM_ORDINAL, NULL, 0, "en_US", &perr, &status);
1747     if (U_FAILURE(status)) {
1748         log_err_status(status, "unable to open ordinal -> %s\n", u_errorName(status));
1749         return;
1750     }
1751 
1752     status = U_ZERO_ERROR;
1753     formats[3] = unum_open(UNUM_DURATION, NULL, 0, "en_US", &perr, &status);
1754     if (U_FAILURE(status)) {
1755         log_err_status(status, "unable to open duration %s\n", u_errorName(status));
1756         return;
1757     }
1758 
1759     status = U_ZERO_ERROR;
1760     u_uastrcpy(pat,
1761         "%standard:\n"
1762         "-x: minus >>;\n"
1763         "x.x: << point >>;\n"
1764         "zero; one; two; three; four; five; six; seven; eight; nine;\n"
1765         "ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
1766         "seventeen; eighteen; nineteen;\n"
1767         "20: twenty[->>];\n"
1768         "30: thirty[->>];\n"
1769         "40: forty[->>];\n"
1770         "50: fifty[->>];\n"
1771         "60: sixty[->>];\n"
1772         "70: seventy[->>];\n"
1773         "80: eighty[->>];\n"
1774         "90: ninety[->>];\n"
1775         "100: =#,##0=;\n");
1776     u_uastrcpy(tempUChars,
1777         "%simple:\n"
1778         "=%standard=;\n"
1779         "20: twenty[ and change];\n"
1780         "30: thirty[ and change];\n"
1781         "40: forty[ and change];\n"
1782         "50: fifty[ and change];\n"
1783         "60: sixty[ and change];\n"
1784         "70: seventy[ and change];\n"
1785         "80: eighty[ and change];\n"
1786         "90: ninety[ and change];\n"
1787         "100: =#,##0=;\n"
1788         "%bogus:\n"
1789         "0.x: tiny;\n"
1790         "x.x: << point something;\n"
1791         "=%standard=;\n"
1792         "20: some reasonable number;\n"
1793         "100: some substantial number;\n"
1794         "100,000,000: some huge number;\n");
1795     /* This is to get around some compiler warnings about char * string length. */
1796     u_strcat(pat, tempUChars);
1797     formats[4] = unum_open(UNUM_PATTERN_RULEBASED, pat, -1, "en_US", &perr, &status);
1798     if (U_FAILURE(status)) {
1799         log_err_status(status, "unable to open rulebased pattern -> %s\n", u_errorName(status));
1800     }
1801     if (U_FAILURE(status)) {
1802         log_err_status(status, "Something failed with %s\n", u_errorName(status));
1803         return;
1804     }
1805 
1806     for (i = 0; i < COUNT; ++i) {
1807         log_verbose("\n\ntesting format %d\n", i);
1808         test_fmt(formats[i], (UBool)(i == 0));
1809     }
1810 
1811     #define FORMAT_BUF_CAPACITY 64
1812     {
1813         UChar fmtbuf[FORMAT_BUF_CAPACITY];
1814         int32_t len;
1815         double nanvalue = uprv_getNaN();
1816         status = U_ZERO_ERROR;
1817         len = unum_formatDouble(formats[1], nanvalue, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
1818         if (U_FAILURE(status)) {
1819             log_err_status(status, "unum_formatDouble NAN failed with %s\n", u_errorName(status));
1820         } else {
1821             UChar nansym[] = { 0x4E, 0x61, 0x4E, 0 }; /* NaN */
1822             if ( len != 3 || u_strcmp(fmtbuf, nansym) != 0 ) {
1823                 log_err("unum_formatDouble NAN produced wrong answer for en_US\n");
1824             }
1825         }
1826     }
1827 
1828     for (i = 0; i < COUNT; ++i) {
1829         unum_close(formats[i]);
1830     }
1831 }
1832 
TestRBNFRounding()1833 static void TestRBNFRounding() {
1834     UChar fmtbuf[FORMAT_BUF_CAPACITY];
1835     UChar expectedBuf[FORMAT_BUF_CAPACITY];
1836     int32_t len;
1837     UErrorCode status = U_ZERO_ERROR;
1838     UNumberFormat* fmt = unum_open(UNUM_SPELLOUT, NULL, 0, "en_US", NULL, &status);
1839     if (U_FAILURE(status)) {
1840         log_err_status(status, "unable to open spellout -> %s\n", u_errorName(status));
1841         return;
1842     }
1843     len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
1844     U_ASSERT(len < FORMAT_BUF_CAPACITY);
1845     (void)len;
1846     if (U_FAILURE(status)) {
1847         log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
1848     }
1849     u_uastrcpy(expectedBuf, "ten point one two three four five six seven eight nine");
1850     if (u_strcmp(expectedBuf, fmtbuf) != 0) {
1851         log_err("Wrong result for unrounded value\n");
1852     }
1853     unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 3);
1854     if (unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS) != 3) {
1855         log_err("UNUM_MAX_FRACTION_DIGITS was incorrectly ignored -> %d\n", unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS));
1856     }
1857     if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_UNNECESSARY) {
1858         log_err("UNUM_ROUNDING_MODE was set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
1859     }
1860     unum_setAttribute(fmt, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
1861     if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_HALFUP) {
1862         log_err("UNUM_ROUNDING_MODE was not set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
1863     }
1864     len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
1865     U_ASSERT(len < FORMAT_BUF_CAPACITY);
1866     if (U_FAILURE(status)) {
1867         log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
1868     }
1869     u_uastrcpy(expectedBuf, "ten point one two three");
1870     if (u_strcmp(expectedBuf, fmtbuf) != 0) {
1871         char temp[512];
1872         u_austrcpy(temp, fmtbuf);
1873         log_err("Wrong result for rounded value. Got: %s\n", temp);
1874     }
1875     unum_close(fmt);
1876 }
1877 
TestCurrencyRegression(void)1878 static void TestCurrencyRegression(void) {
1879 /*
1880  I've found a case where unum_parseDoubleCurrency is not doing what I
1881 expect.  The value I pass in is $1234567890q123460000.00 and this
1882 returns with a status of zero error & a parse pos of 22 (I would
1883 expect a parse error at position 11).
1884 
1885 I stepped into DecimalFormat::subparse() and it looks like it parses
1886 the first 10 digits and then stops parsing at the q but doesn't set an
1887 error. Then later in DecimalFormat::parse() the value gets crammed
1888 into a long (which greatly truncates the value).
1889 
1890 This is very problematic for me 'cause I try to remove chars that are
1891 invalid but this allows my users to enter bad chars and truncates
1892 their data!
1893 */
1894 
1895     UChar buf[1024];
1896     UChar currency[8];
1897     char acurrency[16];
1898     double d;
1899     UNumberFormat *cur;
1900     int32_t pos;
1901     UErrorCode status  = U_ZERO_ERROR;
1902     const int32_t expected = 11;
1903 
1904     currency[0]=0;
1905     u_uastrcpy(buf, "$1234567890q643210000.00");
1906     cur = unum_open(UNUM_CURRENCY, NULL,0,"en_US", NULL, &status);
1907 
1908     if(U_FAILURE(status)) {
1909         log_data_err("unum_open failed: %s (Are you missing data?)\n", u_errorName(status));
1910         return;
1911     }
1912 
1913     status = U_ZERO_ERROR; /* so we can test it later. */
1914     pos = 0;
1915 
1916     d = unum_parseDoubleCurrency(cur,
1917                          buf,
1918                          -1,
1919                          &pos, /* 0 = start */
1920                          currency,
1921                          &status);
1922 
1923     u_austrcpy(acurrency, currency);
1924 
1925     if(U_FAILURE(status) || (pos != expected)) {
1926         log_err("unum_parseDoubleCurrency should have failed with pos %d, but gave: value %.9f, err %s, pos=%d, currency [%s]\n",
1927             expected, d, u_errorName(status), pos, acurrency);
1928     } else {
1929         log_verbose("unum_parseDoubleCurrency failed, value %.9f err %s, pos %d, currency [%s]\n", d, u_errorName(status), pos, acurrency);
1930     }
1931 
1932     unum_close(cur);
1933 }
1934 
TestTextAttributeCrash(void)1935 static void TestTextAttributeCrash(void) {
1936     UChar ubuffer[64] = {0x0049,0x004E,0x0052,0};
1937     static const UChar expectedNeg[] = {0x0049,0x004E,0x0052,0x0031,0x0032,0x0033,0x0034,0x002E,0x0035,0};
1938     static const UChar expectedPos[] = {0x0031,0x0032,0x0033,0x0034,0x002E,0x0035,0};
1939     int32_t used;
1940     UErrorCode status = U_ZERO_ERROR;
1941     UNumberFormat *nf = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
1942     if (U_FAILURE(status)) {
1943         log_data_err("FAILED 1 -> %s (Are you missing data?)\n", u_errorName(status));
1944         return;
1945     }
1946     unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, 3, &status);
1947     /*
1948      * the usual negative prefix and suffix seem to be '($' and ')' at this point
1949      * also crashes if UNUM_NEGATIVE_SUFFIX is substituted for UNUM_NEGATIVE_PREFIX here
1950      */
1951     used = unum_getTextAttribute(nf, UNUM_NEGATIVE_PREFIX, ubuffer, 64, &status);
1952     unum_setTextAttribute(nf, UNUM_NEGATIVE_PREFIX, ubuffer, used, &status);
1953     if (U_FAILURE(status)) {
1954         log_err("FAILED 2\n"); exit(1);
1955     }
1956     log_verbose("attempting to format...\n");
1957     used = unum_formatDouble(nf, -1234.5, ubuffer, 64, NULL, &status);
1958     if (U_FAILURE(status) || 64 < used) {
1959         log_err("Failed formatting %s\n", u_errorName(status));
1960         return;
1961     }
1962     if (u_strcmp(expectedNeg, ubuffer) == 0) {
1963         log_err("Didn't get expected negative result\n");
1964     }
1965     used = unum_formatDouble(nf, 1234.5, ubuffer, 64, NULL, &status);
1966     if (U_FAILURE(status) || 64 < used) {
1967         log_err("Failed formatting %s\n", u_errorName(status));
1968         return;
1969     }
1970     if (u_strcmp(expectedPos, ubuffer) == 0) {
1971         log_err("Didn't get expected positive result\n");
1972     }
1973     unum_close(nf);
1974 }
1975 
TestNBSPPatternRtNum(const char * testcase,int line,UNumberFormat * nf,double myNumber)1976 static void TestNBSPPatternRtNum(const char *testcase, int line, UNumberFormat *nf, double myNumber) {
1977     UErrorCode status = U_ZERO_ERROR;
1978     UChar myString[20];
1979     char tmpbuf[200];
1980     double aNumber = -1.0;
1981     unum_formatDouble(nf, myNumber, myString, 20, NULL, &status);
1982     log_verbose("%s:%d: formatted %.2f into %s\n", testcase, line, myNumber, u_austrcpy(tmpbuf, myString));
1983     if(U_FAILURE(status)) {
1984       log_err("%s:%d: failed format of %.2g with %s\n", testcase, line, myNumber, u_errorName(status));
1985         return;
1986     }
1987     aNumber = unum_parse(nf, myString, -1, NULL, &status);
1988     if(U_FAILURE(status)) {
1989       log_err("%s:%d: failed parse with %s\n", testcase, line, u_errorName(status));
1990         return;
1991     }
1992     if(uprv_fabs(aNumber-myNumber)>.001) {
1993       log_err("FAIL: %s:%d formatted %.2f, parsed into %.2f\n", testcase, line, myNumber, aNumber);
1994     } else {
1995       log_verbose("PASS: %s:%d formatted %.2f, parsed into %.2f\n", testcase, line, myNumber, aNumber);
1996     }
1997 }
1998 
TestNBSPPatternRT(const char * testcase,UNumberFormat * nf)1999 static void TestNBSPPatternRT(const char *testcase, UNumberFormat *nf) {
2000   TestNBSPPatternRtNum(testcase, __LINE__, nf, 12345.);
2001   TestNBSPPatternRtNum(testcase, __LINE__, nf, -12345.);
2002 }
2003 
TestNBSPInPattern(void)2004 static void TestNBSPInPattern(void) {
2005     UErrorCode status = U_ZERO_ERROR;
2006     UNumberFormat* nf = NULL;
2007     const char *testcase;
2008 
2009 
2010     testcase="ar_AE UNUM_CURRENCY";
2011     nf  = unum_open(UNUM_CURRENCY, NULL, -1, "ar_AE", NULL, &status);
2012     if(U_FAILURE(status) || nf == NULL) {
2013       log_data_err("%s:%d: %s: unum_open failed with %s (Are you missing data?)\n", __FILE__, __LINE__, testcase, u_errorName(status));
2014         return;
2015     }
2016     TestNBSPPatternRT(testcase, nf);
2017 
2018     /* if we don't have CLDR 1.6 data, bring out the problem anyways */
2019     {
2020 #define SPECIAL_PATTERN "\\u00A4\\u00A4'\\u062f.\\u0625.\\u200f\\u00a0'###0.00"
2021         UChar pat[200];
2022         testcase = "ar_AE special pattern: " SPECIAL_PATTERN;
2023         u_unescape(SPECIAL_PATTERN, pat, UPRV_LENGTHOF(pat));
2024         unum_applyPattern(nf, FALSE, pat, -1, NULL, &status);
2025         if(U_FAILURE(status)) {
2026             log_err("%s: unum_applyPattern failed with %s\n", testcase, u_errorName(status));
2027         } else {
2028             TestNBSPPatternRT(testcase, nf);
2029         }
2030 #undef SPECIAL_PATTERN
2031     }
2032     unum_close(nf); status = U_ZERO_ERROR;
2033 
2034     testcase="ar_AE UNUM_DECIMAL";
2035     nf  = unum_open(UNUM_DECIMAL, NULL, -1, "ar_AE", NULL, &status);
2036     if(U_FAILURE(status)) {
2037         log_err("%s: unum_open failed with %s\n", testcase, u_errorName(status));
2038     }
2039     TestNBSPPatternRT(testcase, nf);
2040     unum_close(nf); status = U_ZERO_ERROR;
2041 
2042     testcase="ar_AE UNUM_PERCENT";
2043     nf  = unum_open(UNUM_PERCENT, NULL, -1, "ar_AE", NULL, &status);
2044     if(U_FAILURE(status)) {
2045         log_err("%s: unum_open failed with %s\n", testcase, u_errorName(status));
2046     }
2047     TestNBSPPatternRT(testcase, nf);
2048     unum_close(nf); status = U_ZERO_ERROR;
2049 
2050 
2051 
2052 }
TestCloneWithRBNF(void)2053 static void TestCloneWithRBNF(void) {
2054     UChar pattern[1024];
2055     UChar pat2[512];
2056     UErrorCode status = U_ZERO_ERROR;
2057     UChar buffer[256];
2058     UChar buffer_cloned[256];
2059     char temp1[256];
2060     char temp2[256];
2061     UNumberFormat *pform_cloned;
2062     UNumberFormat *pform;
2063 
2064     u_uastrcpy(pattern,
2065         "%main:\n"
2066         "0.x: >%%millis-only>;\n"
2067         "x.0: <%%duration<;\n"
2068         "x.x: <%%durationwithmillis<>%%millis-added>;\n"
2069         "-x: ->>;%%millis-only:\n"
2070         "1000: 00:00.<%%millis<;\n"
2071         "%%millis-added:\n"
2072         "1000: .<%%millis<;\n"
2073         "%%millis:\n"
2074         "0: =000=;\n"
2075         "%%duration:\n"
2076         "0: =%%seconds-only=;\n"
2077         "60: =%%min-sec=;\n"
2078         "3600: =%%hr-min-sec=;\n"
2079         "86400/86400: <%%ddaayyss<[, >>];\n"
2080         "%%durationwithmillis:\n"
2081         "0: =%%seconds-only=;\n"
2082         "60: =%%min-sec=;\n"
2083         "3600: =%%hr-min-sec=;\n"
2084         "86400/86400: <%%ddaayyss<, >>;\n");
2085     u_uastrcpy(pat2,
2086         "%%seconds-only:\n"
2087         "0: 0:00:=00=;\n"
2088         "%%min-sec:\n"
2089         "0: :=00=;\n"
2090         "0/60: 0:<00<>>;\n"
2091         "%%hr-min-sec:\n"
2092         "0: :=00=;\n"
2093         "60/60: <00<>>;\n"
2094         "3600/60: <0<:>>>;\n"
2095         "%%ddaayyss:\n"
2096         "0 days;\n"
2097         "1 day;\n"
2098         "=0= days;");
2099 
2100     /* This is to get around some compiler warnings about char * string length. */
2101     u_strcat(pattern, pat2);
2102 
2103     pform = unum_open(UNUM_PATTERN_RULEBASED, pattern, -1, "en_US", NULL, &status);
2104     unum_formatDouble(pform, 3600, buffer, 256, NULL, &status);
2105 
2106     pform_cloned = unum_clone(pform,&status);
2107     unum_formatDouble(pform_cloned, 3600, buffer_cloned, 256, NULL, &status);
2108 
2109     unum_close(pform);
2110     unum_close(pform_cloned);
2111 
2112     if (u_strcmp(buffer,buffer_cloned)) {
2113         log_data_err("Result from cloned formatter not identical to the original. Original: %s Cloned: %s - (Are you missing data?)",u_austrcpy(temp1, buffer),u_austrcpy(temp2,buffer_cloned));
2114     }
2115 }
2116 
2117 
TestNoExponent(void)2118 static void TestNoExponent(void) {
2119     UErrorCode status = U_ZERO_ERROR;
2120     UChar str[100];
2121     const char *cstr;
2122     UNumberFormat *fmt;
2123     int32_t pos;
2124     int32_t expect = 0;
2125     int32_t num;
2126 
2127     fmt = unum_open(UNUM_DECIMAL, NULL, -1, "en_US", NULL, &status);
2128 
2129     if(U_FAILURE(status) || fmt == NULL) {
2130         log_data_err("%s:%d: unum_open failed with %s (Are you missing data?)\n", __FILE__, __LINE__, u_errorName(status));
2131         return;
2132     }
2133 
2134     cstr = "10E6";
2135     u_uastrcpy(str, cstr);
2136     expect = 10000000;
2137     pos = 0;
2138     num = unum_parse(fmt, str, -1, &pos, &status);
2139     ASSERT_TRUE(pos==4);
2140     if(U_FAILURE(status)) {
2141         log_data_err("%s:%d: unum_parse failed with %s for %s (Are you missing data?)\n", __FILE__, __LINE__, u_errorName(status), cstr);
2142     } else if(expect!=num) {
2143         log_data_err("%s:%d: unum_parse failed, got %d expected %d for '%s'(Are you missing data?)\n", __FILE__, __LINE__, num, expect, cstr);
2144     } else {
2145         log_verbose("%s:%d: unum_parse returned %d for '%s'\n", __FILE__, __LINE__, num, cstr);
2146     }
2147 
2148     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_PARSE_NO_EXPONENT)==0);
2149 
2150     unum_setAttribute(fmt, UNUM_PARSE_NO_EXPONENT, 1); /* no error code */
2151     log_verbose("set UNUM_PARSE_NO_EXPONENT\n");
2152 
2153     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_PARSE_NO_EXPONENT)==1);
2154 
2155     pos = 0;
2156     expect=10;
2157     num = unum_parse(fmt, str, -1, &pos, &status);
2158     if(num==10000000) {
2159         log_err("%s:%d: FAIL: unum_parse should have returned 10, not 10000000 on %s after UNUM_PARSE_NO_EXPONENT\n", __FILE__, __LINE__, cstr);
2160     } else if(num==expect) {
2161         log_verbose("%s:%d: unum_parse gave %d for %s - good.\n", __FILE__, __LINE__, num, cstr);
2162     }
2163     ASSERT_TRUE(pos==2);
2164 
2165     status = U_ZERO_ERROR;
2166 
2167     unum_close(fmt);
2168 
2169     /* ok, now try scientific */
2170     fmt = unum_open(UNUM_SCIENTIFIC, NULL, -1, "en_US", NULL, &status);
2171     assertSuccess("unum_open(UNUM_SCIENTIFIC, ...)", &status);
2172 
2173     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_PARSE_NO_EXPONENT)==0);
2174 
2175     cstr = "10E6";
2176     u_uastrcpy(str, cstr);
2177     expect = 10000000;
2178     pos = 0;
2179     num = unum_parse(fmt, str, -1, &pos, &status);
2180     ASSERT_TRUE(pos==4);
2181     if(U_FAILURE(status)) {
2182         log_data_err("%s:%d: unum_parse failed with %s for %s (Are you missing data?)\n", __FILE__, __LINE__, u_errorName(status), cstr);
2183     } else if(expect!=num) {
2184         log_data_err("%s:%d: unum_parse failed, got %d expected %d for '%s'(Are you missing data?)\n", __FILE__, __LINE__, num, expect, cstr);
2185     } else {
2186         log_verbose("%s:%d: unum_parse returned %d for '%s'\n", __FILE__, __LINE__, num, cstr);
2187     }
2188 
2189     unum_setAttribute(fmt, UNUM_PARSE_NO_EXPONENT, 1); /* no error code */
2190     log_verbose("set UNUM_PARSE_NO_EXPONENT\n");
2191 
2192     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_PARSE_NO_EXPONENT)==1);
2193 
2194     // A scientific formatter should parse the exponent even if UNUM_PARSE_NO_EXPONENT is set
2195     cstr = "10E6";
2196     u_uastrcpy(str, cstr);
2197     expect = 10000000;
2198     pos = 0;
2199     num = unum_parse(fmt, str, -1, &pos, &status);
2200     ASSERT_TRUE(pos==4);
2201     if(U_FAILURE(status)) {
2202         log_data_err("%s:%d: unum_parse failed with %s for %s (Are you missing data?)\n", __FILE__, __LINE__, u_errorName(status), cstr);
2203     } else if(expect!=num) {
2204         log_data_err("%s:%d: unum_parse failed, got %d expected %d for '%s'(Are you missing data?)\n", __FILE__, __LINE__, num, expect, cstr);
2205     } else {
2206         log_verbose("%s:%d: unum_parse returned %d for '%s'\n", __FILE__, __LINE__, num, cstr);
2207     }
2208 
2209     unum_close(fmt);
2210 }
2211 
TestMaxInt(void)2212 static void TestMaxInt(void) {
2213     UErrorCode status = U_ZERO_ERROR;
2214     UChar pattern_hash[] = { 0x23, 0x00 }; /* "#" */
2215     UChar result1[1024] = { 0 }, result2[1024] = { 0 };
2216     int32_t len1, len2;
2217     UChar expect[] = { 0x0039, 0x0037, 0 };
2218     UNumberFormat *fmt = unum_open(
2219                   UNUM_PATTERN_DECIMAL,      /* style         */
2220                   &pattern_hash[0],          /* pattern       */
2221                   u_strlen(pattern_hash),    /* patternLength */
2222                   "en",
2223                   0,                         /* parseErr      */
2224                   &status);
2225     if(U_FAILURE(status) || fmt == NULL) {
2226         log_data_err("%s:%d: %s: unum_open failed with %s (Are you missing data?)\n", __FILE__, __LINE__, "TestMaxInt", u_errorName(status));
2227         return;
2228     }
2229 
2230     unum_setAttribute(fmt, UNUM_MAX_INTEGER_DIGITS, 2);
2231 
2232     status = U_ZERO_ERROR;
2233     /* #1 */
2234     len1 = unum_formatInt64(fmt, 1997, result1, 1024, NULL, &status);
2235     result1[len1]=0;
2236     if(U_FAILURE(status) || u_strcmp(expect, result1)) {
2237         log_err("unum_formatInt64 Expected %s but got %s status %s\n", austrdup(expect), austrdup(result1), u_errorName(status));
2238     }
2239 
2240     status = U_ZERO_ERROR;
2241     /* #2 */
2242     len2 = unum_formatDouble(fmt, 1997.0, result2, 1024, NULL, &status);
2243     result2[len2]=0;
2244     if(U_FAILURE(status) || u_strcmp(expect, result2)) {
2245         log_err("unum_formatDouble Expected %s but got %s status %s\n", austrdup(expect), austrdup(result2), u_errorName(status));
2246     }
2247 
2248 
2249 
2250     /* test UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS */
2251     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)==0);
2252 
2253     unum_setAttribute(fmt, UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS, 1);
2254     /* test UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS */
2255     ASSERT_TRUE(unum_getAttribute(fmt, UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)==1);
2256 
2257     status = U_ZERO_ERROR;
2258     /* max int digits still '2' */
2259     len1 = unum_formatInt64(fmt, 1997, result1, 1024, NULL, &status);
2260     ASSERT_TRUE(status==U_ILLEGAL_ARGUMENT_ERROR);
2261     status = U_ZERO_ERROR;
2262 
2263     /* But, formatting 97->'97' works fine. */
2264 
2265     /* #1 */
2266     len1 = unum_formatInt64(fmt, 97, result1, 1024, NULL, &status);
2267     result1[len1]=0;
2268     if(U_FAILURE(status) || u_strcmp(expect, result1)) {
2269         log_err("unum_formatInt64 Expected %s but got %s status %s\n", austrdup(expect), austrdup(result1), u_errorName(status));
2270     }
2271 
2272     status = U_ZERO_ERROR;
2273     /* #2 */
2274     len2 = unum_formatDouble(fmt, 97.0, result2, 1024, NULL, &status);
2275     result2[len2]=0;
2276     if(U_FAILURE(status) || u_strcmp(expect, result2)) {
2277         log_err("unum_formatDouble Expected %s but got %s status %s\n", austrdup(expect), austrdup(result2), u_errorName(status));
2278     }
2279 
2280 
2281     unum_close(fmt);
2282 }
2283 
TestSignAlwaysShown(void)2284 static void TestSignAlwaysShown(void) {
2285     UErrorCode status = U_ZERO_ERROR;
2286     UNumberFormat *fmt = unum_open(
2287                   UNUM_DECIMAL, /* style         */
2288                   NULL,         /* pattern       */
2289                   0,            /* patternLength */
2290                   "en-US",
2291                   NULL,         /* parseErr      */
2292                   &status);
2293     assertSuccess("Creating UNumberFormat", &status);
2294     unum_setAttribute(fmt, UNUM_SIGN_ALWAYS_SHOWN, 1);
2295     UChar result[100];
2296     unum_formatDouble(fmt, 42, result, 100, NULL, &status);
2297     assertSuccess("Formatting with UNumberFormat", &status);
2298     assertUEquals("Result with sign always shown", u"+42", result);
2299     unum_close(fmt);
2300 }
2301 
TestMinimumGroupingDigits(void)2302 static void TestMinimumGroupingDigits(void) {
2303     UErrorCode status = U_ZERO_ERROR;
2304     UNumberFormat *fmt = unum_open(
2305                   UNUM_DECIMAL, /* style         */
2306                   NULL,         /* pattern       */
2307                   0,            /* patternLength */
2308                   "en-US",
2309                   NULL,         /* parseErr      */
2310                   &status);
2311     assertSuccess("Creating UNumberFormat", &status);
2312     unum_setAttribute(fmt, UNUM_MINIMUM_GROUPING_DIGITS, 2);
2313     UChar result[100];
2314     unum_formatDouble(fmt, 1234, result, 100, NULL, &status);
2315     assertSuccess("Formatting with UNumberFormat A", &status);
2316     assertUEquals("Result with minimum grouping digits A", u"1234", result);
2317     unum_formatDouble(fmt, 12345, result, 100, NULL, &status);
2318     assertSuccess("Formatting with UNumberFormat B", &status);
2319     assertUEquals("Result with minimum grouping digits B", u"12,345", result);
2320     unum_close(fmt);
2321 }
2322 
TestParseCaseSensitive(void)2323 static void TestParseCaseSensitive(void) {
2324     UErrorCode status = U_ZERO_ERROR;
2325     UNumberFormat *fmt = unum_open(
2326                   UNUM_DECIMAL, /* style         */
2327                   NULL,         /* pattern       */
2328                   0,            /* patternLength */
2329                   "en-US",
2330                   NULL,         /* parseErr      */
2331                   &status);
2332     assertSuccess("Creating UNumberFormat", &status);
2333     double result = unum_parseDouble(fmt, u"1e2", -1, NULL, &status);
2334     assertSuccess("Parsing with UNumberFormat, case insensitive", &status);
2335     assertIntEquals("Result with case sensitive", 100, (int64_t)result);
2336     unum_setAttribute(fmt, UNUM_PARSE_CASE_SENSITIVE, 1);
2337     int32_t ppos = 0;
2338     result = unum_parseDouble(fmt, u"1e2", -1, &ppos, &status);
2339     assertSuccess("Parsing with UNumberFormat, case sensitive", &status);
2340     assertIntEquals("Position with case sensitive", 1, ppos);
2341     assertIntEquals("Result with case sensitive", 1, (int64_t)result);
2342     unum_close(fmt);
2343 }
2344 
TestUFormattable(void)2345 static void TestUFormattable(void) {
2346   UChar out2k[2048];
2347   // simple test for API docs
2348   {
2349     UErrorCode status = U_ZERO_ERROR;
2350     UNumberFormat *unum = unum_open(UNUM_DEFAULT, NULL, -1, "en_US_POSIX", NULL, &status);
2351     if(assertSuccessCheck("calling unum_open()", &status, TRUE)) {
2352       //! [unum_parseToUFormattable]
2353       const UChar str[] = { 0x0031, 0x0032, 0x0033, 0x0000 }; /* 123 */
2354       int32_t result = 0;
2355       UFormattable *ufmt = ufmt_open(&status);
2356       unum_parseToUFormattable(unum, ufmt, str, -1, NULL, &status);
2357       if (ufmt_isNumeric(ufmt)) {
2358           result = ufmt_getLong(ufmt, &status); /* == 123 */
2359       } /* else { ... } */
2360       ufmt_close(ufmt);
2361       //! [unum_parseToUFormattable]
2362       assertTrue("result == 123", (result == 123));
2363     }
2364     unum_close(unum);
2365   }
2366   // test with explicitly created ufmt_open
2367   {
2368     UChar buffer[2048];
2369     UErrorCode status = U_ZERO_ERROR;
2370     UFormattable *ufmt;
2371     UNumberFormat *unum;
2372     const char *pattern = "";
2373 
2374     ufmt = ufmt_open(&status);
2375     unum = unum_open(UNUM_DEFAULT, NULL, -1, "en_US_POSIX", NULL, &status);
2376     if(assertSuccessCheck("calling ufmt_open() || unum_open()", &status, TRUE)) {
2377 
2378       pattern = "31337";
2379       log_verbose("-- pattern: %s\n", pattern);
2380       u_uastrcpy(buffer, pattern);
2381       unum_parseToUFormattable(unum, ufmt, buffer, -1, NULL, &status);
2382       if(assertSuccess("unum_parseToUFormattable(31337)", &status)) {
2383         assertTrue("ufmt_getLong()=31337", ufmt_getLong(ufmt, &status) == 31337);
2384         assertTrue("ufmt_getType()=UFMT_LONG", ufmt_getType(ufmt, &status) == UFMT_LONG);
2385         log_verbose("long = %d\n", ufmt_getLong(ufmt, &status));
2386         assertSuccess("ufmt_getLong()", &status);
2387       }
2388       unum_formatUFormattable(unum, ufmt, out2k, 2048, NULL, &status);
2389       if(assertSuccess("unum_formatUFormattable(31337)", &status)) {
2390         assertEquals("unum_formatUFormattable r/t", austrdup(buffer), austrdup(out2k));
2391       }
2392 
2393       pattern = "3.14159";
2394       log_verbose("-- pattern: %s\n", pattern);
2395       u_uastrcpy(buffer, pattern);
2396       unum_parseToUFormattable(unum, ufmt, buffer, -1, NULL, &status);
2397       if(assertSuccess("unum_parseToUFormattable(3.14159)", &status)) {
2398         assertTrue("ufmt_getDouble()==3.14159", withinErr(ufmt_getDouble(ufmt, &status), 3.14159, 1e-15));
2399         assertSuccess("ufmt_getDouble()", &status);
2400         assertTrue("ufmt_getType()=UFMT_DOUBLE", ufmt_getType(ufmt, &status) == UFMT_DOUBLE);
2401         log_verbose("double = %g\n", ufmt_getDouble(ufmt, &status));
2402       }
2403       unum_formatUFormattable(unum, ufmt, out2k, 2048, NULL, &status);
2404       if(assertSuccess("unum_formatUFormattable(3.14159)", &status)) {
2405         assertEquals("unum_formatUFormattable r/t", austrdup(buffer), austrdup(out2k));
2406       }
2407     }
2408     ufmt_close(ufmt);
2409     unum_close(unum);
2410   }
2411 
2412   // test with auto-generated ufmt
2413   {
2414     UChar buffer[2048];
2415     UErrorCode status = U_ZERO_ERROR;
2416     UFormattable *ufmt = NULL;
2417     UNumberFormat *unum;
2418     const char *pattern = "73476730924573500000000"; // weight of the moon, kg
2419 
2420     log_verbose("-- pattern: %s (testing auto-opened UFormattable)\n", pattern);
2421     u_uastrcpy(buffer, pattern);
2422 
2423     unum = unum_open(UNUM_DEFAULT, NULL, -1, "en_US_POSIX", NULL, &status);
2424     if(assertSuccessCheck("calling unum_open()", &status, TRUE)) {
2425 
2426       ufmt = unum_parseToUFormattable(unum, NULL, /* will be ufmt_open()'ed for us */
2427                                    buffer, -1, NULL, &status);
2428       if(assertSuccess("unum_parseToUFormattable(weight of the moon)", &status)) {
2429         log_verbose("new formattable allocated at %p\n", (void*)ufmt);
2430         assertTrue("ufmt_isNumeric() TRUE", ufmt_isNumeric(ufmt));
2431         unum_formatUFormattable(unum, ufmt, out2k, 2048, NULL, &status);
2432         if(assertSuccess("unum_formatUFormattable(3.14159)", &status)) {
2433           assertEquals("unum_formatUFormattable r/t", austrdup(buffer), austrdup(out2k));
2434         }
2435 
2436         log_verbose("double: %g\n",  ufmt_getDouble(ufmt, &status));
2437         assertSuccess("ufmt_getDouble()", &status);
2438 
2439         log_verbose("long: %ld\n", ufmt_getLong(ufmt, &status));
2440         assertTrue("failure on ufmt_getLong() for huge number:", U_FAILURE(status));
2441         // status is now a failure due to ufmt_getLong() above.
2442         // the intltest does extensive r/t testing of Formattable vs. UFormattable.
2443       }
2444     }
2445 
2446     unum_close(unum);
2447     ufmt_close(ufmt); // was implicitly opened for us by the first unum_parseToUFormattable()
2448   }
2449 }
2450 
2451 typedef struct {
2452     const char*  locale;
2453     const char*  numsys;
2454     int32_t      radix;
2455     UBool        isAlgorithmic;
2456     const UChar* description;
2457 } NumSysTestItem;
2458 
2459 
2460 static const UChar latnDesc[]    = {0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0}; // 0123456789
2461 static const UChar romanDesc[]   = {0x25,0x72,0x6F,0x6D,0x61,0x6E,0x2D,0x75,0x70,0x70,0x65,0x72,0}; // %roman-upper
2462 static const UChar arabDesc[]    = {0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,0x0668,0x0669,0}; //
2463 static const UChar arabextDesc[] = {0x06F0,0x06F1,0x06F2,0x06F3,0x06F4,0x06F5,0x06F6,0x06F7,0x06F8,0x06F9,0}; //
2464 static const UChar hanidecDesc[] = {0x3007,0x4E00,0x4E8C,0x4E09,0x56DB,0x4E94,0x516D,0x4E03,0x516B,0x4E5D,0}; //
2465 static const UChar hantDesc[]    = {0x7A,0x68,0x5F,0x48,0x61,0x6E,0x74,0x2F,0x53,0x70,0x65,0x6C,0x6C,0x6F,0x75,0x74,
2466                                     0x52,0x75,0x6C,0x65,0x73,0x2F,0x25,0x73,0x70,0x65,0x6C,0x6C,0x6F,0x75,0x74,0x2D,
2467                                     0x63,0x61,0x72,0x64,0x69,0x6E,0x61,0x6C,0}; // zh_Hant/SpelloutRules/%spellout-cardinal
2468 
2469 static const NumSysTestItem numSysTestItems[] = {
2470     //locale                         numsys    radix isAlgo  description
2471     { "en",                          "latn",    10,  FALSE,  latnDesc },
2472     { "en@numbers=roman",            "roman",   10,  TRUE,   romanDesc },
2473     { "en@numbers=finance",          "latn",    10,  FALSE,  latnDesc },
2474     { "ar-EG",                       "arab",    10,  FALSE,  arabDesc },
2475     { "fa",                          "arabext", 10,  FALSE,  arabextDesc },
2476     { "zh_Hans@numbers=hanidec",     "hanidec", 10,  FALSE,  hanidecDesc },
2477     { "zh_Hant@numbers=traditional", "hant",    10,  TRUE,   hantDesc },
2478     { NULL,                          NULL,       0,  FALSE,  NULL },
2479 };
2480 enum { kNumSysDescripBufMax = 64 };
2481 
TestUNumberingSystem(void)2482 static void TestUNumberingSystem(void) {
2483     const NumSysTestItem * itemPtr;
2484     UNumberingSystem * unumsys;
2485     UEnumeration * uenum;
2486     const char * numsys;
2487     UErrorCode status;
2488 
2489     for (itemPtr = numSysTestItems; itemPtr->locale != NULL; itemPtr++) {
2490         status = U_ZERO_ERROR;
2491         unumsys = unumsys_open(itemPtr->locale, &status);
2492         if ( U_SUCCESS(status) ) {
2493             UChar ubuf[kNumSysDescripBufMax];
2494             int32_t ulen, radix = unumsys_getRadix(unumsys);
2495             UBool isAlgorithmic = unumsys_isAlgorithmic(unumsys);
2496             numsys = unumsys_getName(unumsys);
2497             if ( uprv_strcmp(numsys, itemPtr->numsys) != 0 || radix != itemPtr->radix || !isAlgorithmic != !itemPtr->isAlgorithmic ) {
2498                 log_data_err("unumsys name/radix/isAlgorithmic for locale %s, expected %s/%d/%d, got %s/%d/%d\n",
2499                         itemPtr->locale, itemPtr->numsys, itemPtr->radix, itemPtr->isAlgorithmic, numsys, radix, isAlgorithmic);
2500             }
2501             ulen = unumsys_getDescription(unumsys, ubuf, kNumSysDescripBufMax, &status);
2502             (void)ulen;   // Suppress variable not used warning.
2503             if ( U_FAILURE(status) || u_strcmp(ubuf, itemPtr->description) != 0 ) {
2504                 log_data_err("unumsys description for locale %s, description unexpected and/or status %\n", myErrorName(status));
2505             }
2506             unumsys_close(unumsys);
2507         } else {
2508             log_data_err("unumsys_open for locale %s fails with status %s\n", itemPtr->locale, myErrorName(status));
2509         }
2510     }
2511 
2512     for (int i=0; i<3; ++i) {
2513         // Run the test of unumsys_openAvailableNames() multiple times.
2514         // Helps verify the management of the internal cache of the names.
2515         status = U_ZERO_ERROR;
2516         uenum = unumsys_openAvailableNames(&status);
2517         if ( U_SUCCESS(status) ) {
2518             int32_t numsysCount = 0;
2519             // sanity check for a couple of number systems that must be in the enumeration
2520             UBool foundLatn = FALSE;
2521             UBool foundArab = FALSE;
2522             while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) {
2523                 status = U_ZERO_ERROR;
2524                 unumsys = unumsys_openByName(numsys, &status);
2525                 if ( U_SUCCESS(status) ) {
2526                     numsysCount++;
2527                     if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE;
2528                     if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE;
2529                     unumsys_close(unumsys);
2530                 } else {
2531                     log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n",
2532                             numsys, myErrorName(status));
2533                 }
2534             }
2535             uenum_close(uenum);
2536             if ( numsysCount < 40 || !foundLatn || !foundArab ) {
2537                 log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n",
2538                         numsysCount, foundLatn, foundArab);
2539             }
2540         } else {
2541             log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status));
2542         }
2543     }
2544 }
2545 
2546 /* plain-C version of test in numfmtst.cpp */
2547 enum { kUBufMax = 64 };
TestCurrencyIsoPluralFormat(void)2548 static void TestCurrencyIsoPluralFormat(void) {
2549     static const char* DATA[][8] = {
2550         // the data are:
2551         // locale,
2552         // currency amount to be formatted,
2553         // currency ISO code to be formatted,
2554         // format result using CURRENCYSTYLE,
2555         // format result using CURRENCY_STANDARD,
2556         // format result using CURRENCY_ACCOUNTING,
2557         // format result using ISOCURRENCYSTYLE,
2558         // format result using PLURALCURRENCYSTYLE,
2559 
2560         // locale             amount     ISOcode CURRENCYSTYLE         CURRENCY_STANDARD     CURRENCY_ACCOUNTING   ISOCURRENCYSTYLE          PLURALCURRENCYSTYLE
2561         {"en_US",             "1",        "USD", "$1.00",              "$1.00",              "$1.00",              "USD\\u00A01.00",        "1.00 US dollars"},
2562         {"en_US",             "1234.56",  "USD", "$1,234.56",          "$1,234.56",          "$1,234.56",          "USD\\u00A01,234.56",    "1,234.56 US dollars"},
2563         {"en_US@cf=account",  "1234.56",  "USD", "$1,234.56",          "$1,234.56",          "$1,234.56",          "USD\\u00A01,234.56",    "1,234.56 US dollars"},
2564         {"en_US",             "-1234.56", "USD", "-$1,234.56",         "-$1,234.56",         "($1,234.56)",        "-USD\\u00A01,234.56",   "-1,234.56 US dollars"},
2565         {"en_US@cf=account",  "-1234.56", "USD", "($1,234.56)",        "-$1,234.56",         "($1,234.56)",        "-USD\\u00A01,234.56",   "-1,234.56 US dollars"},
2566         {"en_US@cf=standard", "-1234.56", "USD", "-$1,234.56",         "-$1,234.56",         "($1,234.56)",        "-USD\\u00A01,234.56",   "-1,234.56 US dollars"},
2567         {"zh_CN",             "1",        "USD", "US$1.00",            "US$1.00",            "US$1.00",            "USD\\u00A01.00",        "1.00\\u00A0\\u7F8E\\u5143"},
2568         {"zh_CN",             "-1",       "USD", "-US$1.00",           "-US$1.00",           "(US$1.00)",          "-USD\\u00A01.00",       "-1.00\\u00A0\\u7F8E\\u5143"},
2569         {"zh_CN@cf=account",  "-1",       "USD", "(US$1.00)",          "-US$1.00",           "(US$1.00)",          "-USD\\u00A01.00",       "-1.00\\u00A0\\u7F8E\\u5143"},
2570         {"zh_CN@cf=standard", "-1",       "USD", "-US$1.00",           "-US$1.00",           "(US$1.00)",          "-USD\\u00A01.00",       "-1.00\\u00A0\\u7F8E\\u5143"},
2571         {"zh_CN",             "1234.56",  "USD", "US$1,234.56",        "US$1,234.56",        "US$1,234.56",        "USD\\u00A01,234.56",    "1,234.56\\u00A0\\u7F8E\\u5143"},
2572         // {"zh_CN",          "1",        "CHY", "CHY1.00",            "CHY1.00",            "CHY1.00",            "CHY1.00",        "1.00 CHY"}, // wrong ISO code
2573         // {"zh_CN",          "1234.56",  "CHY", "CHY1,234.56",        "CHY1,234.56",        "CHY1,234.56",        "CHY1,234.56",    "1,234.56 CHY"}, // wrong ISO code
2574         {"zh_CN",             "1",        "CNY", "\\u00A51.00",        "\\u00A51.00",        "\\u00A51.00",        "CNY\\u00A01.00",        "1.00\\u00A0\\u4EBA\\u6C11\\u5E01"},
2575         {"zh_CN",             "1234.56",  "CNY", "\\u00A51,234.56",    "\\u00A51,234.56",    "\\u00A51,234.56",    "CNY\\u00A01,234.56",    "1,234.56\\u00A0\\u4EBA\\u6C11\\u5E01"},
2576         {"ru_RU",             "1",        "RUB", "1,00\\u00A0\\u20BD", "1,00\\u00A0\\u20BD", "1,00\\u00A0\\u20BD", "1,00\\u00A0RUB",        "1,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E "
2577                                                                                                                                             "\\u0440\\u0443\\u0431\\u043B\\u044F"},
2578         {"ru_RU",             "2",        "RUB", "2,00\\u00A0\\u20BD", "2,00\\u00A0\\u20BD", "2,00\\u00A0\\u20BD", "2,00\\u00A0RUB",        "2,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E "
2579                                                                                                                                             "\\u0440\\u0443\\u0431\\u043B\\u044F"},
2580         {"ru_RU",             "5",        "RUB", "5,00\\u00A0\\u20BD", "5,00\\u00A0\\u20BD", "5,00\\u00A0\\u20BD", "5,00\\u00A0RUB",        "5,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E "
2581                                                                                                                                             "\\u0440\\u0443\\u0431\\u043B\\u044F"},
2582         // test locale without currency information
2583         {"root",              "-1.23",    "USD", "-US$\\u00A01.23",    "-US$\\u00A01.23",    "-US$\\u00A01.23",    "-USD\\u00A01.23", "-1.23 USD"},
2584         {"root@cf=account",   "-1.23",    "USD", "-US$\\u00A01.23",    "-US$\\u00A01.23",    "-US$\\u00A01.23",    "-USD\\u00A01.23", "-1.23 USD"},
2585         // test choice format
2586         {"es_AR",             "1",        "INR", "INR\\u00A01,00",     "INR\\u00A01,00",     "INR\\u00A01,00",     "INR\\u00A01,00",  "1,00 rupia india"},
2587     };
2588     static const UNumberFormatStyle currencyStyles[] = {
2589         UNUM_CURRENCY,
2590         UNUM_CURRENCY_STANDARD,
2591         UNUM_CURRENCY_ACCOUNTING,
2592         UNUM_CURRENCY_ISO,
2593         UNUM_CURRENCY_PLURAL
2594     };
2595 
2596     int32_t i, sIndex;
2597 
2598     for (i=0; i<UPRV_LENGTHOF(DATA); ++i) {
2599       const char* localeString = DATA[i][0];
2600       double numberToBeFormat = atof(DATA[i][1]);
2601       const char* currencyISOCode = DATA[i][2];
2602       for (sIndex = 0; sIndex < UPRV_LENGTHOF(currencyStyles); ++sIndex) {
2603         UNumberFormatStyle style = currencyStyles[sIndex];
2604         UErrorCode status = U_ZERO_ERROR;
2605         UChar currencyCode[4];
2606         UChar ubufResult[kUBufMax];
2607         UChar ubufExpected[kUBufMax];
2608         int32_t ulenRes;
2609 
2610         UNumberFormat* unumFmt = unum_open(style, NULL, 0, localeString, NULL, &status);
2611         if (U_FAILURE(status)) {
2612             log_data_err("FAIL: unum_open, locale %s, style %d - %s\n", localeString, (int)style, myErrorName(status));
2613             continue;
2614         }
2615         u_charsToUChars(currencyISOCode, currencyCode, 4);
2616         unum_setTextAttribute(unumFmt, UNUM_CURRENCY_CODE, currencyCode, 3, &status);
2617         if (U_FAILURE(status)) {
2618             log_err("FAIL: unum_setTextAttribute, locale %s, UNUM_CURRENCY_CODE %s\n", localeString, currencyISOCode);
2619         }
2620         ulenRes = unum_formatDouble(unumFmt, numberToBeFormat, ubufResult, kUBufMax, NULL, &status);
2621         if (U_FAILURE(status)) {
2622             log_err("FAIL: unum_formatDouble, locale %s, UNUM_CURRENCY_CODE %s - %s\n", localeString, currencyISOCode, myErrorName(status));
2623         } else {
2624             int32_t ulenExp = u_unescape(DATA[i][3 + sIndex], ubufExpected, kUBufMax);
2625             if (ulenRes != ulenExp || u_strncmp(ubufResult, ubufExpected, ulenExp) != 0) {
2626                 log_err("FAIL: unum_formatDouble, locale %s, UNUM_CURRENCY_CODE %s, expected %s, got something else\n",
2627                         localeString, currencyISOCode, DATA[i][3 + sIndex]);
2628             }
2629         }
2630         unum_close(unumFmt);
2631       }
2632     }
2633 }
2634 
2635 typedef struct {
2636     const char * locale;
2637     UNumberFormatStyle style;
2638     UDisplayContext context;
2639     const char * expectedResult;
2640 } TestContextItem;
2641 
2642 /* currently no locales have contextTransforms data for "symbol" type */
2643 static const TestContextItem tcItems[] = { /* results for 123.45 */
2644     { "sv", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    "ett\\u00ADhundra\\u00ADtjugo\\u00ADtre komma fyra fem" },
2645     { "sv", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "Ett\\u00ADhundra\\u00ADtjugo\\u00ADtre komma fyra fem" },
2646     { "sv", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU,       "ett\\u00ADhundra\\u00ADtjugo\\u00ADtre komma fyra fem" },
2647     { "sv", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_STANDALONE,            "ett\\u00ADhundra\\u00ADtjugo\\u00ADtre komma fyra fem" },
2648     { "en", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    "one hundred twenty-three point four five" },
2649     { "en", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "One hundred twenty-three point four five" },
2650     { "en", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU,       "One hundred twenty-three point four five" },
2651     { "en", UNUM_SPELLOUT, UDISPCTX_CAPITALIZATION_FOR_STANDALONE,            "One hundred twenty-three point four five" },
2652     { NULL, (UNumberFormatStyle)0, (UDisplayContext)0, NULL }
2653 };
2654 
TestContext(void)2655 static void TestContext(void) {
2656     UErrorCode status = U_ZERO_ERROR;
2657     const TestContextItem* itemPtr;
2658 
2659     UNumberFormat *unum = unum_open(UNUM_SPELLOUT, NULL, 0, "en", NULL, &status);
2660     if ( U_SUCCESS(status) ) {
2661         UDisplayContext context = unum_getContext(unum, UDISPCTX_TYPE_CAPITALIZATION, &status);
2662         if ( U_FAILURE(status) || context != UDISPCTX_CAPITALIZATION_NONE) {
2663             log_err("FAIL: Initial unum_getContext is not UDISPCTX_CAPITALIZATION_NONE\n");
2664             status = U_ZERO_ERROR;
2665         }
2666         unum_setContext(unum, UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
2667         context = unum_getContext(unum, UDISPCTX_TYPE_CAPITALIZATION, &status);
2668         if ( U_FAILURE(status) || context != UDISPCTX_CAPITALIZATION_FOR_STANDALONE) {
2669             log_err("FAIL: unum_getContext does not return the value set, UDISPCTX_CAPITALIZATION_FOR_STANDALONE\n");
2670         }
2671         unum_close(unum);
2672     } else {
2673         log_data_err("unum_open UNUM_SPELLOUT for en fails with status %s\n", myErrorName(status));
2674     }
2675 #if !UCONFIG_NO_NORMALIZATION && !UCONFIG_NO_BREAK_ITERATION
2676     for (itemPtr = tcItems; itemPtr->locale != NULL; itemPtr++) {
2677         UChar ubufResult[kUBufMax];
2678         int32_t ulenRes;
2679 
2680         status = U_ZERO_ERROR;
2681         unum = unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status);
2682         if (U_FAILURE(status)) {
2683             log_data_err("FAIL: unum_open, locale %s, style %d - %s\n",
2684                         itemPtr->locale, (int)itemPtr->style, myErrorName(status));
2685             continue;
2686         }
2687         unum_setContext(unum, itemPtr->context, &status);
2688         ulenRes = unum_formatDouble(unum, 123.45, ubufResult, kUBufMax, NULL, &status);
2689         if (U_FAILURE(status)) {
2690             log_err("FAIL: unum_formatDouble, locale %s, style %d, context %d - %s\n",
2691                     itemPtr->locale, (int)itemPtr->style, (int)itemPtr->context, myErrorName(status));
2692         } else {
2693             UChar ubufExpected[kUBufMax];
2694             int32_t ulenExp = u_unescape(itemPtr->expectedResult, ubufExpected, kUBufMax);
2695             if (ulenRes != ulenExp || u_strncmp(ubufResult, ubufExpected, ulenExp) != 0) {
2696                 char bbuf[kUBufMax*2];
2697                 u_austrncpy(bbuf, ubufResult, sizeof(bbuf));
2698                 log_err("FAIL: unum_formatDouble, locale %s, style %d, context %d, expected %d:\"%s\", got %d:\"%s\"\n",
2699                         itemPtr->locale, (int)itemPtr->style, (int)itemPtr->context, ulenExp,
2700                         itemPtr->expectedResult, ulenRes, bbuf);
2701             }
2702         }
2703         unum_close(unum);
2704     }
2705 #endif /* #if !UCONFIG_NO_NORMALIZATION && !UCONFIG_NO_BREAK_ITERATION */
2706 }
2707 
TestCurrencyUsage(void)2708 static void TestCurrencyUsage(void) {
2709     static const char* DATA[][2] = {
2710         /* the data are:
2711          * currency ISO code to be formatted,
2712          * format result using CURRENCYSTYLE with CASH purpose,-
2713          * Note that as of CLDR 26:-
2714          * - TWD switches from 0 decimals to 2; PKR still has 0, so change test to that
2715          * - CAD rounds to .05
2716          */
2717 
2718         {"PKR", "PKR\\u00A0124"},
2719         {"CAD", "CA$123.55"},
2720         {"USD", "$123.57"}
2721     };
2722 
2723     // 1st time for getter/setter, 2nd for factory method
2724     int32_t i;
2725     for(i=0; i<2; i++){
2726         const char* localeString = "en_US";
2727         double numberToBeFormat = 123.567;
2728         UNumberFormat* unumFmt;
2729         UNumberFormatStyle style = UNUM_CURRENCY;
2730         UErrorCode status = U_ZERO_ERROR;
2731         int32_t j;
2732 
2733         if(i == 1){ // change for factory method
2734             style = UNUM_CASH_CURRENCY;
2735         }
2736 
2737         unumFmt = unum_open(style, NULL, 0, localeString, NULL, &status);
2738         if (U_FAILURE(status)) {
2739             log_data_err("FAIL: unum_open, locale %s, style %d - %s\n",
2740                         localeString, (int)style, myErrorName(status));
2741             continue;
2742         }
2743 
2744         if(i == 0){ // this is for the getter/setter
2745             if(unum_getAttribute(unumFmt, UNUM_CURRENCY_USAGE) != UCURR_USAGE_STANDARD) {
2746                 log_err("FAIL: currency usage attribute is not UNUM_CURRENCY_STANDARD\n");
2747             }
2748 
2749             unum_setAttribute(unumFmt, UNUM_CURRENCY_USAGE, UCURR_USAGE_CASH);
2750         }
2751 
2752         if(unum_getAttribute(unumFmt, UNUM_CURRENCY_USAGE) != UCURR_USAGE_CASH) {
2753             log_err("FAIL: currency usage attribute is not UNUM_CURRENCY_CASH\n");
2754         }
2755 
2756         for (j=0; j<UPRV_LENGTHOF(DATA); ++j) {
2757             UChar expect[64];
2758             int32_t expectLen;
2759             UChar currencyCode[4];
2760             UChar result[64];
2761             int32_t resultLen;
2762             UFieldPosition pos = {0};
2763 
2764             u_charsToUChars(DATA[j][0], currencyCode, 3);
2765             expectLen = u_unescape(DATA[j][1], expect, UPRV_LENGTHOF(expect));
2766 
2767             unum_setTextAttribute(unumFmt, UNUM_CURRENCY_CODE, currencyCode, 3, &status);
2768             assertSuccess("num_setTextAttribute()", &status);
2769 
2770             resultLen = unum_formatDouble(unumFmt, numberToBeFormat, result, UPRV_LENGTHOF(result),
2771                                         &pos, &status);
2772             assertSuccess("num_formatDouble()", &status);
2773 
2774             if(resultLen != expectLen || u_strcmp(result, expect) != 0) {
2775                 log_err("Fail: Error in Number Format Currency Purpose using unum_setAttribute() expected: %s, got %s\n",
2776                 aescstrdup(expect, expectLen), aescstrdup(result, resultLen));
2777             }
2778 
2779         }
2780 
2781         unum_close(unumFmt);
2782     }
2783 }
2784 
2785 static UChar currFmtNegSameAsPos[] = /* "\u00A4#,##0.00;\u00A4#,##0.00" */
2786     {0xA4,0x23,0x2C,0x23,0x23,0x30,0x2E,0x30,0x30,0x3B,0xA4,0x23,0x2C,0x23,0x23,0x30,0x2E,0x30,0x30,0};
2787 
2788 // NOTE: As of ICU 62, identical positive and negative subpatterns means no minus sign!
2789 // See CLDR ticket https://unicode.org/cldr/trac/ticket/10703
2790 //static UChar currFmtToPatExpected[] = /* "\u00A4#,##0.00" */
2791 //    {0xA4,0x23,0x2C,0x23,0x23,0x30,0x2E,0x30,0x30,0};
2792 static const UChar* currFmtToPatExpected = currFmtNegSameAsPos;
2793 
2794 static UChar currFmtResultExpected[] = /* "$100.00" */
2795     {0x24,0x31,0x30,0x30,0x2E,0x30,0x30,0};
2796 
2797 static UChar emptyString[] = {0};
2798 
2799 enum { kUBufSize = 64, kBBufSize = 128 };
2800 
TestCurrFmtNegSameAsPositive(void)2801 static void TestCurrFmtNegSameAsPositive(void) {
2802     UErrorCode status = U_ZERO_ERROR;
2803     UNumberFormat* unumfmt = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
2804     if ( U_SUCCESS(status) ) {
2805         unum_applyPattern(unumfmt, FALSE, currFmtNegSameAsPos, -1, NULL, &status);
2806         if (U_SUCCESS(status)) {
2807             UChar ubuf[kUBufSize];
2808             int32_t ulen = unum_toPattern(unumfmt, FALSE, ubuf, kUBufSize, &status);
2809             if (U_FAILURE(status)) {
2810                 log_err("unum_toPattern fails with status %s\n", myErrorName(status));
2811             } else if (u_strcmp(ubuf, currFmtToPatExpected) != 0) {
2812                 log_err("unum_toPattern result wrong, expected %s, got %s\n", aescstrdup(currFmtToPatExpected,-1), aescstrdup(ubuf,ulen));
2813             }
2814             unum_setSymbol(unumfmt, UNUM_MINUS_SIGN_SYMBOL, emptyString, 0, &status);
2815             if (U_SUCCESS(status)) {
2816                 ulen = unum_formatDouble(unumfmt, -100.0, ubuf, kUBufSize, NULL, &status);
2817                 if (U_FAILURE(status)) {
2818                     log_err("unum_formatDouble fails with status %s\n", myErrorName(status));
2819                 } else if (u_strcmp(ubuf, currFmtResultExpected) != 0) {
2820                     log_err("unum_formatDouble result wrong, expected %s, got %s\n", aescstrdup(currFmtResultExpected,-1), aescstrdup(ubuf,ulen));
2821                 }
2822             } else {
2823                 log_err("unum_setSymbol fails with status %s\n", myErrorName(status));
2824             }
2825         } else {
2826             log_err("unum_applyPattern fails with status %s\n", myErrorName(status));
2827         }
2828         unum_close(unumfmt);
2829     } else {
2830         log_data_err("unum_open UNUM_CURRENCY for en_US fails with status %s\n", myErrorName(status));
2831     }
2832 }
2833 
2834 
2835 typedef struct {
2836   double value;
2837   const char *expected;
2838 } ValueAndExpectedString;
2839 
2840 static const ValueAndExpectedString enShort[] = {
2841   {0.0, "0"},
2842   {0.17, "0.17"},
2843   {1.0, "1"},
2844   {1234.0, "1.2K"},
2845   {12345.0, "12K"},
2846   {123456.0, "123K"},
2847   {1234567.0, "1.2M"},
2848   {12345678.0, "12M"},
2849   {123456789.0, "123M"},
2850   {1.23456789E9, "1.2B"},
2851   {1.23456789E10, "12B"},
2852   {1.23456789E11, "123B"},
2853   {1.23456789E12, "1.2T"},
2854   {1.23456789E13, "12T"},
2855   {1.23456789E14, "123T"},
2856   {1.23456789E15, "1235T"},
2857   {0.0, NULL}
2858 };
2859 
2860 static const ValueAndExpectedString enShortMax2[] = {
2861   {0.0, "0"},
2862   {0.17, "0.17"},
2863   {1.0, "1"},
2864   {1234.0, "1.2K"},
2865   {12345.0, "12K"},
2866   {123456.0, "120K"},
2867   {1234567.0, "1.2M"},
2868   {12345678.0, "12M"},
2869   {123456789.0, "120M"},
2870   {1.23456789E9, "1.2B"},
2871   {1.23456789E10, "12B"},
2872   {1.23456789E11, "120B"},
2873   {1.23456789E12, "1.2T"},
2874   {1.23456789E13, "12T"},
2875   {1.23456789E14, "120T"},
2876   {1.23456789E15, "1200T"},
2877   {0.0, NULL}
2878 };
2879 
2880 static const ValueAndExpectedString enShortMax5[] = {
2881   {0.0, "0"},
2882   {0.17, "0.17"},
2883   {1.0, "1"},
2884   {1234.0, "1.234K"},
2885   {12345.0, "12.345K"},
2886   {123456.0, "123.46K"},
2887   {1234567.0, "1.2346M"},
2888   {12345678.0, "12.346M"},
2889   {123456789.0, "123.46M"},
2890   {1.23456789E9, "1.2346B"},
2891   {1.23456789E10, "12.346B"},
2892   {1.23456789E11, "123.46B"},
2893   {1.23456789E12, "1.2346T"},
2894   {1.23456789E13, "12.346T"},
2895   {1.23456789E14, "123.46T"},
2896   {1.23456789E15, "1234.6T"},
2897   {0.0, NULL}
2898 };
2899 
2900 static const ValueAndExpectedString enShortMin3[] = {
2901   {0.0, "0.00"},
2902   {0.17, "0.170"},
2903   {1.0, "1.00"},
2904   {1234.0, "1.23K"},
2905   {12345.0, "12.3K"},
2906   {123456.0, "123K"},
2907   {1234567.0, "1.23M"},
2908   {12345678.0, "12.3M"},
2909   {123456789.0, "123M"},
2910   {1.23456789E9, "1.23B"},
2911   {1.23456789E10, "12.3B"},
2912   {1.23456789E11, "123B"},
2913   {1.23456789E12, "1.23T"},
2914   {1.23456789E13, "12.3T"},
2915   {1.23456789E14, "123T"},
2916   {1.23456789E15, "1230T"},
2917   {0.0, NULL}
2918 };
2919 
2920 static const ValueAndExpectedString jaShortMax2[] = {
2921   {1234.0, "1200"},
2922   {12345.0, "1.2\\u4E07"},
2923   {123456.0, "12\\u4E07"},
2924   {1234567.0, "120\\u4E07"},
2925   {12345678.0, "1200\\u4E07"},
2926   {123456789.0, "1.2\\u5104"},
2927   {1.23456789E9, "12\\u5104"},
2928   {1.23456789E10, "120\\u5104"},
2929   {1.23456789E11, "1200\\u5104"},
2930   {1.23456789E12, "1.2\\u5146"},
2931   {1.23456789E13, "12\\u5146"},
2932   {1.23456789E14, "120\\u5146"},
2933   {0.0, NULL}
2934 };
2935 
2936 static const ValueAndExpectedString srLongMax2[] = {
2937   {1234.0, "1,2 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"}, // 10^3 few
2938   {12345.0, "12 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"}, // 10^3 other
2939   {21789.0, "22 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"}, // 10^3 few
2940   {123456.0, "120 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"}, // 10^3 other
2941   {999999.0, "1 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D"}, // 10^6 one
2942   {1234567.0, "1,2 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"}, // 10^6 few
2943   {12345678.0, "12 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"}, // 10^6 other
2944   {123456789.0, "120 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"}, // 10^6 other
2945   {1.23456789E9, "1,2 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"}, // 10^9 few
2946   {1.23456789E10, "12 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"}, // 10^9 other
2947   {2.08901234E10, "21 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0430"}, // 10^9 one
2948   {2.18901234E10, "22 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"}, // 10^9 few
2949   {1.23456789E11, "120 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"}, // 10^9 other
2950   {-1234.0, "-1,2 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"},
2951   {-12345.0, "-12 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
2952   {-21789.0, "-22 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0435"},
2953   {-123456.0, "-120 \\u0445\\u0438\\u0459\\u0430\\u0434\\u0430"},
2954   {-999999.0, "-1 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D"},
2955   {-1234567.0, "-1,2 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
2956   {-12345678.0, "-12 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
2957   {-123456789.0, "-120 \\u043C\\u0438\\u043B\\u0438\\u043E\\u043D\\u0430"},
2958   {-1.23456789E9, "-1,2 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"},
2959   {-1.23456789E10, "-12 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
2960   {-2.08901234E10, "-21 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0430"},
2961   {-2.18901234E10, "-22 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0435"},
2962   {-1.23456789E11, "-120 \\u043C\\u0438\\u043B\\u0438\\u0458\\u0430\\u0440\\u0434\\u0438"},
2963   {0.0, NULL}
2964 };
2965 
2966 typedef struct {
2967     const char *       locale;
2968     UNumberFormatStyle style;
2969     int32_t            attribute; // UNumberFormatAttribute, or -1 for none
2970     int32_t            attrValue; //
2971     const ValueAndExpectedString * veItems;
2972 } LocStyleAttributeTest;
2973 
2974 static const LocStyleAttributeTest lsaTests[] = {
2975   { "en",   UNUM_DECIMAL_COMPACT_SHORT,     -1,                             0,  enShort },
2976   { "en",   UNUM_DECIMAL_COMPACT_SHORT,     UNUM_MAX_SIGNIFICANT_DIGITS,    2,  enShortMax2 },
2977   { "en",   UNUM_DECIMAL_COMPACT_SHORT,     UNUM_MAX_SIGNIFICANT_DIGITS,    5,  enShortMax5 },
2978   { "en",   UNUM_DECIMAL_COMPACT_SHORT,     UNUM_MIN_SIGNIFICANT_DIGITS,    3,  enShortMin3 },
2979   { "ja",   UNUM_DECIMAL_COMPACT_SHORT,     UNUM_MAX_SIGNIFICANT_DIGITS,    2,  jaShortMax2 },
2980   { "sr",   UNUM_DECIMAL_COMPACT_LONG,      UNUM_MAX_SIGNIFICANT_DIGITS,    2,  srLongMax2  },
2981   { NULL,   (UNumberFormatStyle)0,          -1,                             0,  NULL }
2982 };
2983 
TestVariousStylesAndAttributes(void)2984 static void TestVariousStylesAndAttributes(void) {
2985     const LocStyleAttributeTest * lsaTestPtr;
2986     for (lsaTestPtr = lsaTests; lsaTestPtr->locale != NULL; lsaTestPtr++) {
2987         UErrorCode status = U_ZERO_ERROR;
2988         UNumberFormat * unum = unum_open(lsaTestPtr->style, NULL, 0, lsaTestPtr->locale, NULL, &status);
2989         if ( U_FAILURE(status) ) {
2990             log_data_err("FAIL: unum_open style %d, locale %s: error %s\n", (int)lsaTestPtr->style, lsaTestPtr->locale, u_errorName(status));
2991         } else {
2992             const ValueAndExpectedString * veItemPtr;
2993             if (lsaTestPtr->attribute >= 0) {
2994                 unum_setAttribute(unum, (UNumberFormatAttribute)lsaTestPtr->attribute, lsaTestPtr->attrValue);
2995             }
2996             // ICU 62: should call minSignificantDigits in tandem with maxSignificantDigits.
2997             if (lsaTestPtr->attribute == UNUM_MIN_SIGNIFICANT_DIGITS) {
2998                 unum_setAttribute(unum, UNUM_MAX_SIGNIFICANT_DIGITS, lsaTestPtr->attrValue);
2999             }
3000             for (veItemPtr = lsaTestPtr->veItems; veItemPtr->expected != NULL; veItemPtr++) {
3001                 UChar uexp[kUBufSize];
3002                 UChar uget[kUBufSize];
3003                 int32_t uexplen, ugetlen;
3004 
3005                 status = U_ZERO_ERROR;
3006                 uexplen = u_unescape(veItemPtr->expected, uexp, kUBufSize);
3007                 ugetlen = unum_formatDouble(unum, veItemPtr->value, uget, kUBufSize, NULL, &status);
3008                 if ( U_FAILURE(status) ) {
3009                     log_err("FAIL: unum_formatDouble style %d, locale %s, attr %d, value %.2f: error %s\n",
3010                             (int)lsaTestPtr->style, lsaTestPtr->locale, lsaTestPtr->attribute, veItemPtr->value, u_errorName(status));
3011                 } else if (ugetlen != uexplen || u_strncmp(uget, uexp, uexplen) != 0) {
3012                     char bexp[kBBufSize];
3013                     char bget[kBBufSize];
3014                     u_strToUTF8(bexp, kBBufSize, NULL, uexp, uexplen, &status);
3015                     u_strToUTF8(bget, kBBufSize, NULL, uget, ugetlen, &status);
3016                     log_err("FAIL: unum_formatDouble style %d, locale %s, attr %d, value %.2f: expect \"%s\", get \"%s\"\n",
3017                             (int)lsaTestPtr->style, lsaTestPtr->locale, lsaTestPtr->attribute, veItemPtr->value, bexp, bget);
3018                 }
3019             }
3020             unum_close(unum);
3021         }
3022     }
3023 }
3024 
3025 static const UChar currpat[]  = { 0xA4,0x23,0x2C,0x23,0x23,0x30,0x2E,0x30,0x30,0};
3026 static const UChar parsetxt[] = { 0x78,0x30,0x79,0x24,0 }; /* x0y$ */
3027 
TestParseCurrPatternWithDecStyle()3028 static void TestParseCurrPatternWithDecStyle() {
3029     UErrorCode status = U_ZERO_ERROR;
3030     UNumberFormat *unumfmt = unum_open(UNUM_DECIMAL, NULL, 0, "en_US", NULL, &status);
3031     if (U_FAILURE(status)) {
3032         log_data_err("unum_open DECIMAL failed for en_US: %s (Are you missing data?)\n", u_errorName(status));
3033     } else {
3034         unum_applyPattern(unumfmt, FALSE, currpat, -1, NULL, &status);
3035         if (U_FAILURE(status)) {
3036             log_err_status(status, "unum_applyPattern failed: %s\n", u_errorName(status));
3037         } else {
3038             int32_t pos = 0;
3039             double value = unum_parseDouble(unumfmt, parsetxt, -1, &pos, &status);
3040             if (U_SUCCESS(status)) {
3041                 log_err_status(status, "unum_parseDouble expected to fail but got status %s, value %f\n", u_errorName(status), value);
3042             }
3043         }
3044         unum_close(unumfmt);
3045     }
3046 }
3047 
3048 /*
3049  * Ticket #12684
3050  * Test unum_formatDoubleForFields (and UFieldPositionIterator)
3051  */
3052 
3053 typedef struct {
3054     int32_t field;
3055     int32_t beginPos;
3056     int32_t endPos;
3057 } FieldsData;
3058 
3059 typedef struct {
3060     const char *       locale;
3061     UNumberFormatStyle style;
3062     double             value;
3063     const FieldsData * expectedFields;
3064 } FormatForFieldsItem;
3065 
3066 static const UChar patNoFields[] = { 0x0027, 0x0078, 0x0027, 0 }; /* "'x'", for UNUM_PATTERN_DECIMAL */
3067 
3068 
3069 /* "en_US", UNUM_CURRENCY, 123456.0 : "¤#,##0.00" => "$123,456.00" */
3070 static const FieldsData fields_en_CURR[] = {
3071     { UNUM_CURRENCY_FIELD /*7*/,            0, 1 },
3072     { UNUM_GROUPING_SEPARATOR_FIELD /*6*/,  4, 5 },
3073     { UNUM_INTEGER_FIELD /*0*/,             1, 8 },
3074     { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/,   8, 9 },
3075     { UNUM_FRACTION_FIELD /*1*/,            9, 11 },
3076     { -1, -1, -1 },
3077 };
3078 /* "en_US", UNUM_PERCENT, -34 : "#,##0%" => "-34%" */
3079 static const FieldsData fields_en_PRCT[] = {
3080     { UNUM_SIGN_FIELD /*10*/,               0, 1 },
3081     { UNUM_INTEGER_FIELD /*0*/,             1, 3 },
3082     { UNUM_PERCENT_FIELD /*8*/,             3, 4 },
3083     { -1, -1, -1 },
3084 };
3085 /* "fr_FR", UNUM_CURRENCY, 123456.0 : "#,##0.00 ¤" => "123,456.00 €" */
3086 static const FieldsData fields_fr_CURR[] = {
3087     { UNUM_GROUPING_SEPARATOR_FIELD /*6*/,  3, 4 },
3088     { UNUM_INTEGER_FIELD /*0*/,             0, 7 },
3089     { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/,   7, 8 },
3090     { UNUM_FRACTION_FIELD /*1*/,            8, 10 },
3091     { UNUM_CURRENCY_FIELD /*7*/,           11, 12 },
3092     { -1, -1, -1 },
3093 };
3094 /* "en_US", UNUM_PATTERN_DECIMAL, 12.0 : "'x'" => "x12" */
3095 static const FieldsData fields_en_PATN[] = {
3096     { UNUM_INTEGER_FIELD /*0*/,             1, 3 },
3097     { -1, -1, -1 },
3098 };
3099 
3100 static const FormatForFieldsItem fffItems[] = {
3101     { "en_US", UNUM_CURRENCY_STANDARD, 123456.0, fields_en_CURR },
3102     { "en_US", UNUM_PERCENT,              -0.34, fields_en_PRCT },
3103     { "fr_FR", UNUM_CURRENCY_STANDARD, 123456.0, fields_fr_CURR },
3104     { "en_US", UNUM_PATTERN_DECIMAL,       12.0, fields_en_PATN },
3105     { NULL, (UNumberFormatStyle)0, 0, NULL },
3106 };
3107 
TestFormatForFields(void)3108 static void TestFormatForFields(void) {
3109     UErrorCode status = U_ZERO_ERROR;
3110     UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
3111     if ( U_FAILURE(status) ) {
3112         log_err("ufieldpositer_open fails, status %s\n", u_errorName(status));
3113     } else {
3114         const FormatForFieldsItem * itemPtr;
3115         for (itemPtr = fffItems; itemPtr->locale != NULL; itemPtr++) {
3116             UNumberFormat* unum;
3117             status = U_ZERO_ERROR;
3118             unum = (itemPtr->style == UNUM_PATTERN_DECIMAL)?
3119                 unum_open(itemPtr->style, patNoFields, -1, itemPtr->locale, NULL, &status):
3120                 unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status);
3121             if ( U_FAILURE(status) ) {
3122                 log_data_err("unum_open fails for locale %s, style %d: status %s (Are you missing data?)\n", itemPtr->locale, itemPtr->style, u_errorName(status));
3123             } else {
3124                 UChar ubuf[kUBufSize];
3125                 int32_t ulen = unum_formatDoubleForFields(unum, itemPtr->value, ubuf, kUBufSize, fpositer, &status);
3126                 if ( U_FAILURE(status) ) {
3127                     log_err("unum_formatDoubleForFields fails for locale %s, style %d: status %s\n", itemPtr->locale, itemPtr->style, u_errorName(status));
3128                 } else {
3129                     const FieldsData * fptr;
3130                     int32_t field, beginPos, endPos;
3131                     for (fptr = itemPtr->expectedFields; TRUE; fptr++) {
3132                         field = ufieldpositer_next(fpositer, &beginPos, &endPos);
3133                         if (field != fptr->field || (field >= 0 && (beginPos != fptr->beginPos || endPos != fptr->endPos))) {
3134                             if (fptr->field >= 0) {
3135                                 log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field %d range %d-%d, get field %d range %d-%d\n",
3136                                     itemPtr->locale, aescstrdup(ubuf, ulen), fptr->field, fptr->beginPos, fptr->endPos, field, beginPos, endPos);
3137                             } else {
3138                                 log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field < 0, get field %d range %d-%d\n",
3139                                     itemPtr->locale, aescstrdup(ubuf, ulen), field, beginPos, endPos);
3140                             }
3141                             break;
3142                         }
3143                         if (field < 0) {
3144                             break;
3145                         }
3146                     }
3147                 }
3148                 unum_close(unum);
3149             }
3150         }
3151         ufieldpositer_close(fpositer);
3152     }
3153 }
3154 
Test12052_NullPointer()3155 static void Test12052_NullPointer() {
3156     UErrorCode status = U_ZERO_ERROR;
3157     static const UChar input[] = u"199a";
3158     UChar currency[200] = {0};
3159     UNumberFormat *theFormatter = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
3160     if (!assertSuccessCheck("unum_open() failed", &status, TRUE)) { return; }
3161     status = U_ZERO_ERROR;
3162     unum_setAttribute(theFormatter, UNUM_LENIENT_PARSE, 1);
3163     int32_t pos = 1;
3164     unum_parseDoubleCurrency(theFormatter, input, -1, &pos, currency, &status);
3165     assertEquals("should fail gracefully", "U_PARSE_ERROR", u_errorName(status));
3166     unum_close(theFormatter);
3167 }
3168 
3169 typedef struct {
3170     const char*  locale;
3171     const UChar* text;       // text to parse
3172     UBool        lenient;    // leniency to use
3173     UBool        intOnly;    // whether to set PARSE_INT_ONLY
3174     UErrorCode   intStatus;  // expected status from parse
3175     int32_t      intPos;     // expected final pos from parse
3176     int32_t      intValue;   // expected value from parse
3177     UErrorCode   doubStatus; // expected status from parseDouble
3178     int32_t      doubPos;    // expected final pos from parseDouble
3179     double       doubValue;  // expected value from parseDouble
3180     UErrorCode   decStatus;  // expected status from parseDecimal
3181     int32_t      decPos;     // expected final pos from parseDecimal
3182     const char*  decString;  // expected output string from parseDecimal
3183 
3184 } ParseCaseItem;
3185 
3186 static const ParseCaseItem parseCaseItems[] = {
3187     { "en", u"0,000",            FALSE, FALSE, U_ZERO_ERROR,            5,          0, U_ZERO_ERROR,  5,                0.0, U_ZERO_ERROR,  5, "0" },
3188     { "en", u"0,000",            TRUE,  FALSE, U_ZERO_ERROR,            5,          0, U_ZERO_ERROR,  5,                0.0, U_ZERO_ERROR,  5, "0" },
3189     { "en", u"1000,000",         FALSE, FALSE, U_PARSE_ERROR,           0,          0, U_PARSE_ERROR, 0,                0.0, U_PARSE_ERROR, 0, "" },
3190     { "en", u"1000,000",         TRUE,  FALSE, U_ZERO_ERROR,            8,    1000000, U_ZERO_ERROR,  8,          1000000.0, U_ZERO_ERROR,  8, "1000000" },
3191     { "en", u"",                 FALSE, FALSE, U_PARSE_ERROR,           0,          0, U_PARSE_ERROR, 0,                0.0, U_PARSE_ERROR, 0, "" },
3192     { "en", u"",                 TRUE,  FALSE, U_PARSE_ERROR,           0,          0, U_PARSE_ERROR, 0,                0.0, U_PARSE_ERROR, 0, "" },
3193     { "en", u"9999990000503021", FALSE, FALSE, U_INVALID_FORMAT_ERROR, 16, 2147483647, U_ZERO_ERROR, 16, 9999990000503020.0, U_ZERO_ERROR, 16, "9999990000503021" },
3194     { "en", u"9999990000503021", FALSE, TRUE,  U_INVALID_FORMAT_ERROR, 16, 2147483647, U_ZERO_ERROR, 16, 9999990000503020.0, U_ZERO_ERROR, 16, "9999990000503021" },
3195     { "en", u"1000000.5",        FALSE, FALSE, U_ZERO_ERROR,            9,    1000000, U_ZERO_ERROR,  9,          1000000.5, U_ZERO_ERROR,  9, "1.0000005E+6"},
3196     { "en", u"1000000.5",        FALSE, TRUE,  U_ZERO_ERROR,            7,    1000000, U_ZERO_ERROR,  7,          1000000.0, U_ZERO_ERROR,  7, "1000000" },
3197     { "en", u"123.5",            FALSE, FALSE, U_ZERO_ERROR,            5,        123, U_ZERO_ERROR,  5,              123.5, U_ZERO_ERROR,  5, "123.5" },
3198     { "en", u"123.5",            FALSE, TRUE,  U_ZERO_ERROR,            3,        123, U_ZERO_ERROR,  3,              123.0, U_ZERO_ERROR,  3, "123" },
3199     { NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0, NULL }
3200 };
3201 
TestParseCases(void)3202 static void TestParseCases(void) {
3203     const ParseCaseItem* itemPtr;
3204     for (itemPtr = parseCaseItems; itemPtr->locale != NULL; itemPtr++) {
3205         UErrorCode status = U_ZERO_ERROR;
3206         UNumberFormat* unumDec = unum_open(UNUM_DECIMAL, NULL, 0, itemPtr->locale, NULL, &status);
3207         if (U_FAILURE(status)) {
3208             log_data_err("unum_open UNUM_DECIMAL fails for locale %s: %s\n", itemPtr->locale, u_errorName(status));
3209             continue;
3210         }
3211         int32_t intValue, parsePos, dclen;
3212         double doubValue;
3213         char decstr[32];
3214         unum_setAttribute(unumDec, UNUM_LENIENT_PARSE, itemPtr->lenient);
3215         unum_setAttribute(unumDec, UNUM_PARSE_INT_ONLY, itemPtr->intOnly);
3216 
3217         parsePos = 0;
3218         status = U_ZERO_ERROR;
3219         intValue = unum_parse(unumDec, itemPtr->text, -1, &parsePos, &status);
3220         if (status != itemPtr->intStatus || parsePos != itemPtr->intPos || intValue != itemPtr->intValue) {
3221             char btext[32];
3222             u_austrcpy(btext, itemPtr->text);
3223             log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n      parse        expected status %s, pos %d, value %d;\n                   got   %s, %d, %d\n",
3224                     itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
3225                     u_errorName(itemPtr->intStatus), itemPtr->intPos, itemPtr->intValue,
3226                     u_errorName(status), parsePos, intValue);
3227         }
3228 
3229         parsePos = 0;
3230         status = U_ZERO_ERROR;
3231         doubValue = unum_parseDouble(unumDec, itemPtr->text, -1, &parsePos, &status);
3232         if (status != itemPtr->doubStatus || parsePos != itemPtr->doubPos || doubValue != itemPtr->doubValue) {
3233             char btext[32];
3234             u_austrcpy(btext, itemPtr->text);
3235             log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n      parseDouble  expected status %s, pos %d, value %.1f;\n                   got   %s, %d, %.1f\n",
3236                     itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
3237                     u_errorName(itemPtr->doubStatus), itemPtr->doubPos, itemPtr->doubValue,
3238                     u_errorName(status), parsePos, doubValue);
3239         }
3240 
3241         parsePos = 0;
3242         status = U_ZERO_ERROR;
3243         decstr[0] = 0;
3244         dclen = unum_parseDecimal(unumDec, itemPtr->text, -1, &parsePos, decstr, 32, &status);
3245         (void)dclen;
3246         if (status != itemPtr->decStatus || parsePos != itemPtr->decPos || uprv_strcmp(decstr,itemPtr->decString) != 0) {
3247             char btext[32];
3248             u_austrcpy(btext, itemPtr->text);
3249             log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n      parseDecimal expected status %s, pos %d, str \"%s\";\n                   got   %s, %d, \"%s\"\n",
3250                     itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
3251                     u_errorName(itemPtr->decStatus), itemPtr->decPos, itemPtr->decString,
3252                     u_errorName(status), parsePos, decstr);
3253         }
3254 
3255         unum_close(unumDec);
3256     }
3257 }
3258 
3259 typedef struct {
3260     const char*        descrip;
3261     const char*        locale;
3262     UNumberFormatStyle style;
3263     int32_t            minInt;
3264     int32_t            minFrac;
3265     int32_t            maxFrac;
3266     double             roundIncr;
3267     const UChar*       expPattern;
3268     double             valueToFmt;
3269     const UChar*       expFormat;
3270 } SetMaxFracAndRoundIncrItem;
3271 
3272 static const SetMaxFracAndRoundIncrItem maxFracAndRoundIncrItems[] = {
3273     // descrip                     locale   style         mnI mnF mxF rdInc   expPat         value  expFmt
3274     { "01 en_US DEC 1/0/3/0.0",    "en_US", UNUM_DECIMAL,  1,  0,  3, 0.0,    u"#,##0.###",  0.128, u"0.128" },
3275     { "02 en_US DEC 1/0/1/0.0",    "en_US", UNUM_DECIMAL,  1,  0,  1, 0.0,    u"#,##0.#",    0.128, u"0.1"   },
3276     { "03 en_US DEC 1/0/1/0.01",   "en_US", UNUM_DECIMAL,  1,  0,  1, 0.01,   u"#,##0.#",    0.128, u"0.1"   },
3277     { "04 en_US DEC 1/1/1/0.01",   "en_US", UNUM_DECIMAL,  1,  1,  1, 0.01,   u"#,##0.0",    0.128, u"0.1"   },
3278     { "05 en_US DEC 1/0/1/0.1",    "en_US", UNUM_DECIMAL,  1,  0,  1, 0.1,    u"#,##0.1",    0.128, u"0.1"   }, // use incr
3279     { "06 en_US DEC 1/1/1/0.1",    "en_US", UNUM_DECIMAL,  1,  1,  1, 0.1,    u"#,##0.1",    0.128, u"0.1"   }, // use incr
3280 
3281     { "10 en_US DEC 1/0/1/0.02",   "en_US", UNUM_DECIMAL,  1,  0,  1, 0.02,   u"#,##0.#",    0.128, u"0.1"   },
3282     { "11 en_US DEC 1/0/2/0.02",   "en_US", UNUM_DECIMAL,  1,  0,  2, 0.02,   u"#,##0.02",   0.128, u"0.12"  }, // use incr
3283     { "12 en_US DEC 1/0/3/0.02",   "en_US", UNUM_DECIMAL,  1,  0,  3, 0.02,   u"#,##0.02#",  0.128, u"0.12"  }, // use incr
3284     { "13 en_US DEC 1/1/1/0.02",   "en_US", UNUM_DECIMAL,  1,  1,  1, 0.02,   u"#,##0.0",    0.128, u"0.1"   },
3285     { "14 en_US DEC 1/1/2/0.02",   "en_US", UNUM_DECIMAL,  1,  1,  2, 0.02,   u"#,##0.02",   0.128, u"0.12"  }, // use incr
3286     { "15 en_US DEC 1/1/3/0.02",   "en_US", UNUM_DECIMAL,  1,  1,  3, 0.02,   u"#,##0.02#",  0.128, u"0.12"  }, // use incr
3287     { "16 en_US DEC 1/2/2/0.02",   "en_US", UNUM_DECIMAL,  1,  2,  2, 0.02,   u"#,##0.02",   0.128, u"0.12"  }, // use incr
3288     { "17 en_US DEC 1/2/3/0.02",   "en_US", UNUM_DECIMAL,  1,  2,  3, 0.02,   u"#,##0.02#",  0.128, u"0.12"  }, // use incr
3289     { "18 en_US DEC 1/3/3/0.02",   "en_US", UNUM_DECIMAL,  1,  3,  3, 0.02,   u"#,##0.020",  0.128, u"0.120" }, // use incr
3290 
3291     { "20 en_US DEC 1/1/1/0.0075", "en_US", UNUM_DECIMAL,  1,  1,  1, 0.0075, u"#,##0.0",    0.019, u"0.0"    },
3292     { "21 en_US DEC 1/1/2/0.0075", "en_US", UNUM_DECIMAL,  1,  1,  2, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
3293     { "22 en_US DEC 1/1/2/0.0075", "en_US", UNUM_DECIMAL,  1,  1,  2, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
3294     { "23 en_US DEC 1/1/3/0.0075", "en_US", UNUM_DECIMAL,  1,  1,  3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
3295     { "24 en_US DEC 1/1/3/0.0075", "en_US", UNUM_DECIMAL,  1,  1,  3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
3296     { "25 en_US DEC 1/2/2/0.0075", "en_US", UNUM_DECIMAL,  1,  2,  2, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
3297     { "26 en_US DEC 1/2/2/0.0075", "en_US", UNUM_DECIMAL,  1,  2,  2, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
3298     { "27 en_US DEC 1/2/3/0.0075", "en_US", UNUM_DECIMAL,  1,  2,  3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
3299     { "28 en_US DEC 1/2/3/0.0075", "en_US", UNUM_DECIMAL,  1,  2,  3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
3300     { "29 en_US DEC 1/3/3/0.0075", "en_US", UNUM_DECIMAL,  1,  3,  3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
3301     { "2A en_US DEC 1/3/3/0.0075", "en_US", UNUM_DECIMAL,  1,  3,  3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
3302 
3303     { NULL, NULL, UNUM_IGNORE, 0, 0, 0, 0.0, NULL, 0.0, NULL }
3304 };
3305 
3306 // The following is copied from C++ number_patternstring.cpp for this C test.
3307 //
3308 // Determine whether a given roundingIncrement should be ignored for formatting
3309 // based on the current maxFrac value (maximum fraction digits). For example a
3310 // roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac
3311 // is 2 or more. Note that roundingIncrements are rounded in significance, so
3312 // a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e.
3313 // it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of
3314 // 0.005 is treated like 0.001 for significance). This is the reason for the
3315 // initial doubling below.
3316 // roundIncr must be non-zero
ignoreRoundingIncrement(double roundIncr,int32_t maxFrac)3317 static UBool ignoreRoundingIncrement(double roundIncr, int32_t maxFrac) {
3318     if (maxFrac < 0) {
3319         return FALSE;
3320     }
3321     int32_t frac = 0;
3322     roundIncr *= 2.0;
3323     for (frac = 0; frac <= maxFrac && roundIncr <= 1.0; frac++, roundIncr *= 10.0);
3324     return (frac > maxFrac);
3325 }
3326 
3327 enum { kBBufMax = 128 };
TestSetMaxFracAndRoundIncr(void)3328 static void TestSetMaxFracAndRoundIncr(void) {
3329     const SetMaxFracAndRoundIncrItem* itemPtr;
3330     for (itemPtr = maxFracAndRoundIncrItems; itemPtr->descrip != NULL; itemPtr++) {
3331         UChar ubuf[kUBufMax];
3332         char  bbufe[kBBufMax];
3333         char  bbufg[kBBufMax];
3334         int32_t ulen;
3335         UErrorCode status = U_ZERO_ERROR;
3336         UNumberFormat* unf = unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status);
3337         if (U_FAILURE(status)) {
3338             log_data_err("locale %s: unum_open style %d fails with %s\n", itemPtr->locale, itemPtr->style, u_errorName(status));
3339             continue;
3340         }
3341 
3342         unum_setAttribute(unf, UNUM_MIN_INTEGER_DIGITS, itemPtr->minInt);
3343         unum_setAttribute(unf, UNUM_MIN_FRACTION_DIGITS, itemPtr->minFrac);
3344         unum_setAttribute(unf, UNUM_MAX_FRACTION_DIGITS, itemPtr->maxFrac);
3345         unum_setDoubleAttribute(unf, UNUM_ROUNDING_INCREMENT, itemPtr->roundIncr);
3346 
3347         UBool roundIncrUsed = (itemPtr->roundIncr != 0.0 && !ignoreRoundingIncrement(itemPtr->roundIncr, itemPtr->maxFrac));
3348 
3349         int32_t minInt = unum_getAttribute(unf, UNUM_MIN_INTEGER_DIGITS);
3350         if (minInt != itemPtr->minInt) {
3351             log_err("test %s: unum_getAttribute UNUM_MIN_INTEGER_DIGITS, expected %d, got %d\n",
3352                     itemPtr->descrip, itemPtr->minInt, minInt);
3353         }
3354         int32_t minFrac = unum_getAttribute(unf, UNUM_MIN_FRACTION_DIGITS);
3355         if (minFrac != itemPtr->minFrac) {
3356             log_err("test %s: unum_getAttribute UNUM_MIN_FRACTION_DIGITS, expected %d, got %d\n",
3357                     itemPtr->descrip, itemPtr->minFrac, minFrac);
3358         }
3359         // If incrementRounding is used, maxFrac is set equal to minFrac
3360         int32_t maxFrac = unum_getAttribute(unf, UNUM_MAX_FRACTION_DIGITS);
3361         // If incrementRounding is used, maxFrac is set equal to minFrac
3362         int32_t expMaxFrac = (roundIncrUsed)? itemPtr->minFrac: itemPtr->maxFrac;
3363         if (maxFrac != expMaxFrac) {
3364             log_err("test %s: unum_getAttribute UNUM_MAX_FRACTION_DIGITS, expected %d, got %d\n",
3365                     itemPtr->descrip, expMaxFrac, maxFrac);
3366         }
3367         double roundIncr = unum_getDoubleAttribute(unf, UNUM_ROUNDING_INCREMENT);
3368         // If incrementRounding is not used, roundIncr is set to 0.0
3369         double expRoundIncr = (roundIncrUsed)? itemPtr->roundIncr: 0.0;
3370         if (roundIncr != expRoundIncr) {
3371             log_err("test %s: unum_getDoubleAttribute UNUM_ROUNDING_INCREMENT, expected %f, got %f\n",
3372                     itemPtr->descrip, expRoundIncr, roundIncr);
3373         }
3374 
3375         status = U_ZERO_ERROR;
3376         ulen = unum_toPattern(unf, FALSE, ubuf, kUBufMax, &status);
3377         (void)ulen;
3378         if ( U_FAILURE(status) ) {
3379             log_err("test %s: unum_toPattern fails with %s\n", itemPtr->descrip, u_errorName(status));
3380         } else if (u_strcmp(ubuf,itemPtr->expPattern)!=0) {
3381             u_austrcpy(bbufe, itemPtr->expPattern);
3382             u_austrcpy(bbufg, ubuf);
3383             log_err("test %s: unum_toPattern expect \"%s\", get \"%s\"\n", itemPtr->descrip, bbufe, bbufg);
3384         }
3385 
3386         status = U_ZERO_ERROR;
3387         ulen = unum_formatDouble(unf, itemPtr->valueToFmt, ubuf, kUBufMax, NULL, &status);
3388         if ( U_FAILURE(status) ) {
3389             log_err("test %s: unum_formatDouble fails with %s\n", itemPtr->descrip, u_errorName(status));
3390         } else if (u_strcmp(ubuf,itemPtr->expFormat)!=0) {
3391             u_austrcpy(bbufe, itemPtr->expFormat);
3392             u_austrcpy(bbufg, ubuf);
3393             log_err("test %s: unum_formatDouble expect \"%s\", get \"%s\"\n", itemPtr->descrip, bbufe, bbufg);
3394         }
3395 
3396         unum_close(unf);
3397     }
3398 }
3399 
TestIgnorePadding(void)3400 static void TestIgnorePadding(void) {
3401     UErrorCode status = U_ZERO_ERROR;
3402     UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, NULL, 0, "en_US", NULL, &status);
3403     if (U_FAILURE(status)) {
3404         log_data_err("unum_open UNUM_PATTERN_DECIMAL for en_US and NULL pattern fails:%s\n", u_errorName(status));
3405     } else {
3406         unum_setAttribute(unum, UNUM_GROUPING_USED, 0);
3407         unum_setAttribute(unum, UNUM_FORMAT_WIDTH, 0);
3408         unum_setTextAttribute(unum, UNUM_PADDING_CHARACTER, u"*", 1, &status);
3409         if (U_FAILURE(status)) {
3410             log_err("unum_setTextAttribute UNUM_PADDING_CHARACTER to '*' fails: %s\n", u_errorName(status));
3411         } else {
3412             unum_setAttribute(unum, UNUM_PADDING_POSITION, 0);
3413             unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
3414             unum_setAttribute(unum, UNUM_MAX_INTEGER_DIGITS, 8);
3415             unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
3416             unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 0);
3417 
3418             UChar ubuf[kUBufMax];
3419             int32_t ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
3420             if (U_FAILURE(status)) {
3421                 log_err("unum_toPattern fails: %s\n", u_errorName(status));
3422             } else {
3423                 char bbuf[kBBufMax];
3424                 if (ulen > 0 && ubuf[0]==u'*') {
3425                     ubuf[kUBufMax-1] = 0; // ensure zero termination
3426                     u_austrncpy(bbuf, ubuf, kBBufMax);
3427                     log_err("unum_toPattern result should ignore padding but get %s\n", bbuf);
3428                 }
3429                 unum_applyPattern(unum, FALSE, ubuf, ulen, NULL, &status);
3430                 if (U_FAILURE(status)) {
3431                     log_err("unum_applyPattern fails: %s\n", u_errorName(status));
3432                 } else {
3433                     ulen = unum_formatDecimal(unum, "24", -1, ubuf, kUBufMax, NULL, &status);
3434                     if (U_FAILURE(status)) {
3435                         log_err("unum_formatDecimal fails: %s\n", u_errorName(status));
3436                     } else if (u_strcmp(ubuf, u"24") != 0) {
3437                         ubuf[kUBufMax-1] = 0; // ensure zero termination
3438                         u_austrncpy(bbuf, ubuf, kBBufMax);
3439                         log_err("unum_formatDecimal result expect 24 but get %s\n", bbuf);
3440                     }
3441                 }
3442             }
3443         }
3444         unum_close(unum);
3445     }
3446 }
3447 
TestSciNotationMaxFracCap(void)3448 static void TestSciNotationMaxFracCap(void) {
3449     static const UChar* pat1 = u"#.##E+00;-#.##E+00";
3450     UErrorCode status = U_ZERO_ERROR;
3451     UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, pat1, -1, "en_US", NULL, &status);
3452     if ( U_FAILURE(status) ) {
3453         log_data_err("unum_open UNUM_PATTERN_DECIMAL with scientific pattern for \"en_US\" fails with %s\n", u_errorName(status));
3454     } else {
3455         double value;
3456         UChar ubuf[kUBufMax];
3457         char bbuf[kBBufMax];
3458         int32_t ulen;
3459 
3460         unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
3461         unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 2147483647);
3462         ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
3463         if ( U_SUCCESS(status) ) {
3464             u_austrncpy(bbuf, ubuf, kUBufMax);
3465             log_info("unum_toPattern (%d): %s\n", ulen, bbuf);
3466         }
3467 
3468         for (value = 10.0; value < 1000000000.0; value *= 10.0) {
3469             status = U_ZERO_ERROR;
3470             ulen = unum_formatDouble(unum, value, ubuf, kUBufMax, NULL, &status);
3471             if ( U_FAILURE(status) ) {
3472                 log_err("unum_formatDouble value %.1f status %s\n", value, u_errorName(status));
3473             } else if (u_strncmp(ubuf,u"1E+0",4) != 0) {
3474                 u_austrncpy(bbuf, ubuf, kUBufMax);
3475                 log_err("unum_formatDouble value %.1f expected result to begin with 1E+0, got %s\n", value, bbuf);
3476             }
3477         }
3478         unum_close(unum);
3479     }
3480 }
3481 
TestMinIntMinFracZero(void)3482 static void TestMinIntMinFracZero(void) {
3483     UErrorCode status = U_ZERO_ERROR;
3484     UNumberFormat* unum = unum_open(UNUM_DECIMAL, NULL, 0, "en_US", NULL, &status);
3485     if ( U_FAILURE(status) ) {
3486         log_data_err("unum_open UNUM_DECIMAL for en_US fails with %s\n", u_errorName(status));
3487     } else {
3488         UChar ubuf[kUBufMax];
3489         char  bbuf[kBBufMax];
3490         int minInt, minFrac, ulen;
3491 
3492         unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
3493         unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
3494         minInt = unum_getAttribute(unum, UNUM_MIN_INTEGER_DIGITS);
3495         minFrac = unum_getAttribute(unum, UNUM_MIN_FRACTION_DIGITS);
3496         if (minInt != 0 || minFrac != 0) {
3497             log_err("after setting minInt=minFrac=0, get minInt %d, minFrac %d\n", minInt, minFrac);
3498         }
3499 
3500         ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
3501         if ( U_FAILURE(status) ) {
3502             log_err("unum_toPattern fails with %s\n", u_errorName(status));
3503         } else if (ulen < 3 || u_strstr(ubuf, u"#.#")==NULL) {
3504             u_austrncpy(bbuf, ubuf, kUBufMax);
3505             log_info("after setting minInt=minFrac=0, expect pattern to contain \"#.#\", but get (%d): \"%s\"\n", ulen, bbuf);
3506         }
3507 
3508         status = U_ZERO_ERROR;
3509         ulen = unum_formatDouble(unum, 10.0, ubuf, kUBufMax, NULL, &status);
3510         if ( U_FAILURE(status) ) {
3511             log_err("unum_formatDouble 10.0 ulen %d fails with %s\n", ulen, u_errorName(status));
3512         } else if (u_strcmp(ubuf, u"10") != 0) {
3513             u_austrncpy(bbuf, ubuf, kUBufMax);
3514             log_err("unum_formatDouble 10.0 expected \"10\", got \"%s\"\n", bbuf);
3515         }
3516 
3517         status = U_ZERO_ERROR;
3518         ulen = unum_formatDouble(unum, 0.9, ubuf, kUBufMax, NULL, &status);
3519         if ( U_FAILURE(status) ) {
3520             log_err("unum_formatDouble 0.9 ulen %d fails with %s\n", ulen, u_errorName(status));
3521         } else if (u_strcmp(ubuf, u".9") != 0) {
3522             u_austrncpy(bbuf, ubuf, kUBufMax);
3523             log_err("unum_formatDouble 0.9 expected \".9\", got \"%s\"\n", bbuf);
3524         }
3525 
3526         status = U_ZERO_ERROR;
3527         ulen = unum_formatDouble(unum, 0.0, ubuf, kUBufMax, NULL, &status);
3528         if ( U_FAILURE(status) ) {
3529             log_err("unum_formatDouble 0.0 ulen %d fails with %s\n", ulen, u_errorName(status));
3530         } else if (u_strcmp(ubuf, u"0") != 0) {
3531             u_austrncpy(bbuf, ubuf, kUBufMax);
3532             log_err("unum_formatDouble 0.0 expected \"0\", got \"%s\"\n", bbuf);
3533         }
3534 
3535         unum_close(unum);
3536         status = U_ZERO_ERROR;
3537         unum = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
3538         if ( U_FAILURE(status) ) {
3539             log_data_err("unum_open UNUM_CURRENCY for en_US fails with %s\n", u_errorName(status));
3540         } else {
3541             unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
3542             unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
3543             minInt = unum_getAttribute(unum, UNUM_MIN_INTEGER_DIGITS);
3544             minFrac = unum_getAttribute(unum, UNUM_MIN_FRACTION_DIGITS);
3545             if (minInt != 0 || minFrac != 0) {
3546                 log_err("after setting CURRENCY minInt=minFrac=0, get minInt %d, minFrac %d\n", minInt, minFrac);
3547             }
3548 
3549             status = U_ZERO_ERROR;
3550             ulen = unum_formatDouble(unum, 10.0, ubuf, kUBufMax, NULL, &status);
3551             if ( U_FAILURE(status) ) {
3552                 log_err("unum_formatDouble (CURRRENCY) 10.0 ulen %d fails with %s\n", ulen, u_errorName(status));
3553             } else if (u_strcmp(ubuf, u"$10") != 0) {
3554                 u_austrncpy(bbuf, ubuf, kUBufMax);
3555                 log_err("unum_formatDouble (CURRRENCY) 10.0 expected \"$10\", got \"%s\"\n", bbuf);
3556             }
3557 
3558             status = U_ZERO_ERROR;
3559             ulen = unum_formatDouble(unum, 0.9, ubuf, kUBufMax, NULL, &status);
3560             if ( U_FAILURE(status) ) {
3561                 log_err("unum_formatDouble (CURRRENCY) 0.9 ulen %d fails with %s\n", ulen, u_errorName(status));
3562             } else if (u_strcmp(ubuf, u"$.9") != 0) {
3563                 u_austrncpy(bbuf, ubuf, kUBufMax);
3564                 log_err("unum_formatDouble (CURRRENCY) 0.9 expected \"$.9\", got \"%s\"\n", bbuf);
3565             }
3566 
3567             status = U_ZERO_ERROR;
3568             ulen = unum_formatDouble(unum, 0.0, ubuf, kUBufMax, NULL, &status);
3569             if ( U_FAILURE(status) ) {
3570                 log_err("unum_formatDouble (CURRRENCY) 0.0 ulen %d fails with %s\n", ulen, u_errorName(status));
3571             } else if (u_strcmp(ubuf, u"$0") != 0) {
3572                 u_austrncpy(bbuf, ubuf, kUBufMax);
3573                 log_err("unum_formatDouble (CURRRENCY) 0.0 expected \"$0\", got \"%s\"\n", bbuf);
3574             }
3575 
3576             unum_close(unum);
3577         }
3578     }
3579 }
3580 
Test21479_ExactCurrency(void)3581 static void Test21479_ExactCurrency(void) {
3582     UErrorCode status = U_ZERO_ERROR;
3583     UNumberFormat* nf = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
3584     if ( U_FAILURE(status) ) {
3585         log_data_err("unum_open UNUM_CURRENCY for en_US fails with %s\n", u_errorName(status));
3586         goto cleanup;
3587     }
3588     unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, u"EUR", -1, &status);
3589     UChar result[40];
3590     unum_formatDecimal(nf, "987654321000000000000001", -1, result, 40, NULL, &status);
3591     if (!assertSuccess("Formatting currency decimal", &status)) {
3592         goto cleanup;
3593     }
3594     assertUEquals("", u"€987,654,321,000,000,000,000,001.00", result);
3595 
3596     cleanup:
3597     unum_close(nf);
3598 }
3599 
3600 #endif /* #if !UCONFIG_NO_FORMATTING */
3601