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