1 /******************************************************************** 2 * COPYRIGHT: 3 * Copyright (c) 1997-2010, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ********************************************************************/ 6 /******************************************************************************** 7 * 8 * File CNUMTST.C 9 * 10 * Madhu Katragadda Creation 11 * 12 * Modification History: 13 * 14 * Date Name Description 15 * 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes 16 * 07/15/99 helena Ported to HPUX 10/11 CC. 17 ********************************************************************************* 18 */ 19 20 /* C API TEST FOR NUMBER FORMAT */ 21 22 #include "unicode/utypes.h" 23 24 #if !UCONFIG_NO_FORMATTING 25 26 #include "unicode/uloc.h" 27 #include "unicode/unum.h" 28 #include "unicode/ustring.h" 29 #include "cintltst.h" 30 #include "cnumtst.h" 31 #include "cmemory.h" 32 #include "putilimp.h" 33 34 #define LENGTH(arr) (sizeof(arr)/sizeof(arr[0])) 35 36 void addNumForTest(TestNode** root); 37 static void TestTextAttributeCrash(void); 38 static void TestNBSPInPattern(void); 39 40 #define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x) 41 42 void addNumForTest(TestNode** root) 43 { 44 TESTCASE(TestNumberFormat); 45 TESTCASE(TestSpelloutNumberParse); 46 TESTCASE(TestSignificantDigits); 47 TESTCASE(TestNumberFormatPadding); 48 TESTCASE(TestInt64Format); 49 TESTCASE(TestNonExistentCurrency); 50 TESTCASE(TestCurrencyRegression); 51 TESTCASE(TestTextAttributeCrash); 52 TESTCASE(TestRBNFFormat); 53 TESTCASE(TestNBSPInPattern); 54 } 55 56 /** copy src to dst with unicode-escapes for values < 0x20 and > 0x7e, null terminate if possible */ 57 static int32_t ustrToAstr(const UChar* src, int32_t srcLength, char* dst, int32_t dstLength) { 58 static const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 59 60 char *p = dst; 61 const char *e = p + dstLength; 62 if (srcLength < 0) { 63 const UChar* s = src; 64 while (*s) { 65 ++s; 66 } 67 srcLength = (int32_t)(s - src); 68 } 69 while (p < e && --srcLength >= 0) { 70 UChar c = *src++; 71 if (c == 0xd || c == 0xa || c == 0x9 || (c>= 0x20 && c <= 0x7e)) { 72 *p++ = (char) c & 0x7f; 73 } else if (e - p >= 6) { 74 *p++ = '\\'; 75 *p++ = 'u'; 76 *p++ = hex[(c >> 12) & 0xf]; 77 *p++ = hex[(c >> 8) & 0xf]; 78 *p++ = hex[(c >> 4) & 0xf]; 79 *p++ = hex[c & 0xf]; 80 } else { 81 break; 82 } 83 } 84 if (p < e) { 85 *p = 0; 86 } 87 return (int32_t)(p - dst); 88 } 89 90 /* test Number Format API */ 91 static void TestNumberFormat() 92 { 93 UChar *result=NULL; 94 UChar temp1[512]; 95 UChar temp2[512]; 96 97 UChar temp[5]; 98 99 UChar prefix[5]; 100 UChar suffix[5]; 101 UChar symbol[20]; 102 int32_t resultlength; 103 int32_t resultlengthneeded; 104 int32_t parsepos; 105 double d1 = -1.0; 106 int32_t l1; 107 double d = -10456.37; 108 double a = 1234.56, a1 = 1235.0; 109 int32_t l = 100000000; 110 UFieldPosition pos1; 111 UFieldPosition pos2; 112 int32_t numlocales; 113 int32_t i; 114 115 UNumberFormatAttribute attr; 116 UNumberFormatSymbol symType = UNUM_DECIMAL_SEPARATOR_SYMBOL; 117 int32_t newvalue; 118 UErrorCode status=U_ZERO_ERROR; 119 UNumberFormatStyle style= UNUM_DEFAULT; 120 UNumberFormat *pattern; 121 UNumberFormat *def, *fr, *cur_def, *cur_fr, *per_def, *per_fr, 122 *cur_frpattern, *myclone, *spellout_def; 123 124 /* Testing unum_open() with various Numberformat styles and locales*/ 125 status = U_ZERO_ERROR; 126 log_verbose("Testing unum_open() with default style and locale\n"); 127 def=unum_open(style, NULL,0,NULL, NULL,&status); 128 129 /* Might as well pack it in now if we can't even get a default NumberFormat... */ 130 if(U_FAILURE(status)) 131 { 132 log_data_err("Error in creating default NumberFormat using unum_open(): %s (Are you missing data?)\n", myErrorName(status)); 133 return; 134 } 135 136 log_verbose("\nTesting unum_open() with french locale and default style(decimal)\n"); 137 fr=unum_open(style,NULL,0, "fr_FR",NULL, &status); 138 if(U_FAILURE(status)) 139 log_err("Error: could not create NumberFormat (french): %s\n", myErrorName(status)); 140 141 log_verbose("\nTesting unum_open(currency,NULL,status)\n"); 142 style=UNUM_CURRENCY; 143 /* Can't hardcode the result to assume the default locale is "en_US". */ 144 cur_def=unum_open(style, NULL,0,"en_US", NULL, &status); 145 if(U_FAILURE(status)) 146 log_err("Error: could not create NumberFormat using \n unum_open(currency, NULL, &status) %s\n", 147 myErrorName(status) ); 148 149 log_verbose("\nTesting unum_open(currency, frenchlocale, status)\n"); 150 cur_fr=unum_open(style,NULL,0, "fr_FR", NULL, &status); 151 if(U_FAILURE(status)) 152 log_err("Error: could not create NumberFormat using unum_open(currency, french, &status): %s\n", 153 myErrorName(status)); 154 155 log_verbose("\nTesting unum_open(percent, NULL, status)\n"); 156 style=UNUM_PERCENT; 157 per_def=unum_open(style,NULL,0, NULL,NULL, &status); 158 if(U_FAILURE(status)) 159 log_err("Error: could not create NumberFormat using unum_open(percent, NULL, &status): %s\n", myErrorName(status)); 160 161 log_verbose("\nTesting unum_open(percent,frenchlocale, status)\n"); 162 per_fr=unum_open(style, NULL,0,"fr_FR", NULL,&status); 163 if(U_FAILURE(status)) 164 log_err("Error: could not create NumberFormat using unum_open(percent, french, &status): %s\n", myErrorName(status)); 165 166 log_verbose("\nTesting unum_open(spellout, NULL, status)"); 167 style=UNUM_SPELLOUT; 168 spellout_def=unum_open(style, NULL, 0, "en_US", NULL, &status); 169 if(U_FAILURE(status)) 170 log_err("Error: could not create NumberFormat using unum_open(spellout, NULL, &status): %s\n", myErrorName(status)); 171 172 /* Testing unum_clone(..) */ 173 log_verbose("\nTesting unum_clone(fmt, status)"); 174 status = U_ZERO_ERROR; 175 myclone = unum_clone(def,&status); 176 if(U_FAILURE(status)) 177 log_err("Error: could not clone unum_clone(def, &status): %s\n", myErrorName(status)); 178 else 179 { 180 log_verbose("unum_clone() successful\n"); 181 } 182 183 /*Testing unum_getAvailable() and unum_countAvailable()*/ 184 log_verbose("\nTesting getAvailableLocales and countAvailable()\n"); 185 numlocales=unum_countAvailable(); 186 if(numlocales < 0) 187 log_err("error in countAvailable"); 188 else{ 189 log_verbose("unum_countAvialable() successful\n"); 190 log_verbose("The no: of locales where number formattting is applicable is %d\n", numlocales); 191 } 192 for(i=0;i<numlocales;i++) 193 { 194 log_verbose("%s\n", unum_getAvailable(i)); 195 if (unum_getAvailable(i) == 0) 196 log_err("No locale for which number formatting patterns are applicable\n"); 197 else 198 log_verbose("A locale %s for which number formatting patterns are applicable\n",unum_getAvailable(i)); 199 } 200 201 202 /*Testing unum_format() and unum_formatdouble()*/ 203 u_uastrcpy(temp1, "$100,000,000.00"); 204 205 log_verbose("\nTesting unum_format() \n"); 206 resultlength=0; 207 pos1.field = 0; /* Integer Section */ 208 resultlengthneeded=unum_format(cur_def, l, NULL, resultlength, &pos1, &status); 209 if(status==U_BUFFER_OVERFLOW_ERROR) 210 { 211 status=U_ZERO_ERROR; 212 resultlength=resultlengthneeded+1; 213 result=(UChar*)malloc(sizeof(UChar) * resultlength); 214 /* for (i = 0; i < 100000; i++) */ 215 { 216 unum_format(cur_def, l, result, resultlength, &pos1, &status); 217 } 218 } 219 220 if(U_FAILURE(status)) 221 { 222 log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status) ); 223 } 224 if(u_strcmp(result, temp1)==0) 225 log_verbose("Pass: Number formatting using unum_format() successful\n"); 226 else 227 log_err("Fail: Error in number Formatting using unum_format()\n"); 228 if(pos1.beginIndex == 1 && pos1.endIndex == 12) 229 log_verbose("Pass: Complete number formatting using unum_format() successful\n"); 230 else 231 log_err("Fail: Error in complete number Formatting using unum_format()\nGot: b=%d end=%d\nExpected: b=1 end=12\n", 232 pos1.beginIndex, pos1.endIndex); 233 234 free(result); 235 result = 0; 236 237 log_verbose("\nTesting unum_formatDouble()\n"); 238 u_uastrcpy(temp1, "($10,456.37)"); 239 resultlength=0; 240 pos2.field = 1; /* Fractional Section */ 241 resultlengthneeded=unum_formatDouble(cur_def, d, NULL, resultlength, &pos2, &status); 242 if(status==U_BUFFER_OVERFLOW_ERROR) 243 { 244 status=U_ZERO_ERROR; 245 resultlength=resultlengthneeded+1; 246 result=(UChar*)malloc(sizeof(UChar) * resultlength); 247 /* for (i = 0; i < 100000; i++) */ 248 { 249 unum_formatDouble(cur_def, d, result, resultlength, &pos2, &status); 250 } 251 } 252 if(U_FAILURE(status)) 253 { 254 log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status)); 255 } 256 if(result && u_strcmp(result, temp1)==0) 257 log_verbose("Pass: Number Formatting using unum_formatDouble() Successful\n"); 258 else 259 log_err("FAIL: Error in number formatting using unum_formatDouble()\n"); 260 if(pos2.beginIndex == 9 && pos2.endIndex == 11) 261 log_verbose("Pass: Complete number formatting using unum_format() successful\n"); 262 else 263 log_err("Fail: Error in complete number Formatting using unum_formatDouble()\nGot: b=%d end=%d\nExpected: b=9 end=11", 264 pos1.beginIndex, pos1.endIndex); 265 266 267 /* Testing unum_parse() and unum_parseDouble() */ 268 log_verbose("\nTesting unum_parseDouble()\n"); 269 /* for (i = 0; i < 100000; i++)*/ 270 if (result != NULL) 271 { 272 parsepos=0; 273 d1=unum_parseDouble(cur_def, result, u_strlen(result), &parsepos, &status); 274 } 275 else { 276 log_err("result is NULL\n"); 277 } 278 if(U_FAILURE(status)) 279 { 280 log_err("parse failed. The error is : %s\n", myErrorName(status)); 281 } 282 283 if(d1!=d) 284 log_err("Fail: Error in parsing\n"); 285 else 286 log_verbose("Pass: parsing successful\n"); 287 if (result) 288 free(result); 289 result = 0; 290 291 292 /* Testing unum_formatDoubleCurrency / unum_parseDoubleCurrency */ 293 log_verbose("\nTesting unum_formatDoubleCurrency\n"); 294 u_uastrcpy(temp1, "Y1,235"); 295 temp1[0] = 0xA5; /* Yen sign */ 296 u_uastrcpy(temp, "JPY"); 297 resultlength=0; 298 pos2.field = 0; /* integer part */ 299 resultlengthneeded=unum_formatDoubleCurrency(cur_def, a, temp, NULL, resultlength, &pos2, &status); 300 if (status==U_BUFFER_OVERFLOW_ERROR) { 301 status=U_ZERO_ERROR; 302 resultlength=resultlengthneeded+1; 303 result=(UChar*)malloc(sizeof(UChar) * resultlength); 304 unum_formatDoubleCurrency(cur_def, a, temp, result, resultlength, &pos2, &status); 305 } 306 if (U_FAILURE(status)) { 307 log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status)); 308 } 309 if (result && u_strcmp(result, temp1)==0) { 310 log_verbose("Pass: Number Formatting using unum_formatDouble() Successful\n"); 311 } else { 312 log_err("FAIL: Error in number formatting using unum_formatDouble()\n"); 313 } 314 if (pos2.beginIndex == 1 && pos2.endIndex == 6) { 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_formatDouble()\nGot: b=%d end=%d\nExpected: b=1 end=6\n", 318 pos1.beginIndex, pos1.endIndex); 319 } 320 321 log_verbose("\nTesting unum_parseDoubleCurrency\n"); 322 parsepos=0; 323 if (result == NULL) { 324 log_err("result is NULL\n"); 325 } 326 else { 327 d1=unum_parseDoubleCurrency(cur_def, result, u_strlen(result), &parsepos, temp2, &status); 328 if (U_FAILURE(status)) { 329 log_err("parse failed. The error is : %s\n", myErrorName(status)); 330 } 331 /* Note: a==1234.56, but on parse expect a1=1235.0 */ 332 if (d1!=a1) { 333 log_err("Fail: Error in parsing currency, got %f, expected %f\n", d1, a1); 334 } else { 335 log_verbose("Pass: parsed currency ammount successfully\n"); 336 } 337 if (u_strcmp(temp2, temp)==0) { 338 log_verbose("Pass: parsed correct currency\n"); 339 } else { 340 log_err("Fail: parsed incorrect currency\n"); 341 } 342 } 343 344 free(result); 345 result = 0; 346 347 348 /* performance testing */ 349 u_uastrcpy(temp1, "$462.12345"); 350 resultlength=u_strlen(temp1); 351 /* for (i = 0; i < 100000; i++) */ 352 { 353 parsepos=0; 354 d1=unum_parseDouble(cur_def, temp1, resultlength, &parsepos, &status); 355 } 356 if(U_FAILURE(status)) 357 { 358 log_err("parse failed. The error is : %s\n", myErrorName(status)); 359 } 360 361 if(d1!=462.12345) 362 log_err("Fail: Error in parsing\n"); 363 else 364 log_verbose("Pass: parsing successful\n"); 365 366 free(result); 367 368 u_uastrcpy(temp1, "($10,456.3E1])"); 369 parsepos=0; 370 d1=unum_parseDouble(cur_def, temp1, u_strlen(temp1), &parsepos, &status); 371 if(U_SUCCESS(status)) 372 { 373 log_err("Error in unum_parseDouble(..., %s, ...): %s\n", temp1, myErrorName(status)); 374 } 375 376 377 log_verbose("\nTesting unum_format()\n"); 378 status=U_ZERO_ERROR; 379 resultlength=0; 380 parsepos=0; 381 resultlengthneeded=unum_format(per_fr, l, NULL, resultlength, &pos1, &status); 382 if(status==U_BUFFER_OVERFLOW_ERROR) 383 { 384 status=U_ZERO_ERROR; 385 resultlength=resultlengthneeded+1; 386 result=(UChar*)malloc(sizeof(UChar) * resultlength); 387 /* for (i = 0; i < 100000; i++)*/ 388 { 389 unum_format(per_fr, l, result, resultlength, &pos1, &status); 390 } 391 } 392 if(U_FAILURE(status)) 393 { 394 log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status)); 395 } 396 397 398 log_verbose("\nTesting unum_parse()\n"); 399 /* for (i = 0; i < 100000; i++) */ 400 { 401 parsepos=0; 402 l1=unum_parse(per_fr, result, u_strlen(result), &parsepos, &status); 403 } 404 if(U_FAILURE(status)) 405 { 406 log_err("parse failed. The error is : %s\n", myErrorName(status)); 407 } 408 409 if(l1!=l) 410 log_err("Fail: Error in parsing\n"); 411 else 412 log_verbose("Pass: parsing successful\n"); 413 414 free(result); 415 416 /* create a number format using unum_openPattern(....)*/ 417 log_verbose("\nTesting unum_openPattern()\n"); 418 u_uastrcpy(temp1, "#,##0.0#;(#,##0.0#)"); 419 pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), NULL, NULL,&status); 420 if(U_FAILURE(status)) 421 { 422 log_err("error in unum_openPattern(): %s\n", myErrorName(status) );; 423 } 424 else 425 log_verbose("Pass: unum_openPattern() works fine\n"); 426 427 /*test for unum_toPattern()*/ 428 log_verbose("\nTesting unum_toPattern()\n"); 429 resultlength=0; 430 resultlengthneeded=unum_toPattern(pattern, FALSE, NULL, resultlength, &status); 431 if(status==U_BUFFER_OVERFLOW_ERROR) 432 { 433 status=U_ZERO_ERROR; 434 resultlength=resultlengthneeded+1; 435 result=(UChar*)malloc(sizeof(UChar) * resultlength); 436 unum_toPattern(pattern, FALSE, result, resultlength, &status); 437 } 438 if(U_FAILURE(status)) 439 { 440 log_err("error in extracting the pattern from UNumberFormat: %s\n", myErrorName(status)); 441 } 442 else 443 { 444 if(u_strcmp(result, temp1)!=0) 445 log_err("FAIL: Error in extracting the pattern using unum_toPattern()\n"); 446 else 447 log_verbose("Pass: extracted the pattern correctly using unum_toPattern()\n"); 448 free(result); 449 } 450 451 /*Testing unum_getSymbols() and unum_setSymbols()*/ 452 log_verbose("\nTesting unum_getSymbols and unum_setSymbols()\n"); 453 /*when we try to change the symbols of french to default we need to apply the pattern as well to fetch correct results */ 454 resultlength=0; 455 resultlengthneeded=unum_toPattern(cur_def, FALSE, NULL, resultlength, &status); 456 if(status==U_BUFFER_OVERFLOW_ERROR) 457 { 458 status=U_ZERO_ERROR; 459 resultlength=resultlengthneeded+1; 460 result=(UChar*)malloc(sizeof(UChar) * resultlength); 461 unum_toPattern(cur_def, FALSE, result, resultlength, &status); 462 } 463 if(U_FAILURE(status)) 464 { 465 log_err("error in extracting the pattern from UNumberFormat: %s\n", myErrorName(status)); 466 } 467 468 status=U_ZERO_ERROR; 469 cur_frpattern=unum_open(UNUM_IGNORE,result, u_strlen(result), "fr_FR",NULL, &status); 470 if(U_FAILURE(status)) 471 { 472 log_err("error in unum_openPattern(): %s\n", myErrorName(status)); 473 } 474 475 free(result); 476 477 /*getting the symbols of cur_def */ 478 /*set the symbols of cur_frpattern to cur_def */ 479 for (symType = UNUM_DECIMAL_SEPARATOR_SYMBOL; symType < UNUM_FORMAT_SYMBOL_COUNT; symType++) { 480 status=U_ZERO_ERROR; 481 unum_getSymbol(cur_def, symType, temp1, sizeof(temp1), &status); 482 unum_setSymbol(cur_frpattern, symType, temp1, -1, &status); 483 if(U_FAILURE(status)) 484 { 485 log_err("Error in get/set symbols: %s\n", myErrorName(status)); 486 } 487 } 488 489 /*format to check the result */ 490 resultlength=0; 491 resultlengthneeded=unum_format(cur_def, l, NULL, resultlength, &pos1, &status); 492 if(status==U_BUFFER_OVERFLOW_ERROR) 493 { 494 status=U_ZERO_ERROR; 495 resultlength=resultlengthneeded+1; 496 result=(UChar*)malloc(sizeof(UChar) * resultlength); 497 unum_format(cur_def, l, result, resultlength, &pos1, &status); 498 } 499 if(U_FAILURE(status)) 500 { 501 log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status)); 502 } 503 504 if(U_FAILURE(status)){ 505 log_err("Fail: error in unum_setSymbols: %s\n", myErrorName(status)); 506 } 507 unum_applyPattern(cur_frpattern, FALSE, result, u_strlen(result),NULL,NULL); 508 509 for (symType = UNUM_DECIMAL_SEPARATOR_SYMBOL; symType < UNUM_FORMAT_SYMBOL_COUNT; symType++) { 510 status=U_ZERO_ERROR; 511 unum_getSymbol(cur_def, symType, temp1, sizeof(temp1), &status); 512 unum_getSymbol(cur_frpattern, symType, temp2, sizeof(temp2), &status); 513 if(U_FAILURE(status) || u_strcmp(temp1, temp2) != 0) 514 { 515 log_err("Fail: error in getting symbols\n"); 516 } 517 else 518 log_verbose("Pass: get and set symbols successful\n"); 519 } 520 521 /*format and check with the previous result */ 522 523 resultlength=0; 524 resultlengthneeded=unum_format(cur_frpattern, l, NULL, resultlength, &pos1, &status); 525 if(status==U_BUFFER_OVERFLOW_ERROR) 526 { 527 status=U_ZERO_ERROR; 528 resultlength=resultlengthneeded+1; 529 unum_format(cur_frpattern, l, temp1, resultlength, &pos1, &status); 530 } 531 if(U_FAILURE(status)) 532 { 533 log_err("Error in formatting using unum_format(.....): %s\n", myErrorName(status)); 534 } 535 /* TODO: 536 * This test fails because we have not called unum_applyPattern(). 537 * Currently, such an applyPattern() does not exist on the C API, and 538 * we have jitterbug 411 for it. 539 * Since it is close to the 1.5 release, I (markus) am disabling this test just 540 * for this release (I added the test itself only last week). 541 * For the next release, we need to fix this. 542 * Then, remove the uprv_strcmp("1.5", ...) and this comment, and the include "cstring.h" at the beginning of this file. 543 */ 544 if(u_strcmp(result, temp1) != 0) { 545 log_err("Formatting failed after setting symbols. result=%s temp1=%s\n", result, temp1); 546 } 547 548 549 /*----------- */ 550 551 free(result); 552 553 /* Testing unum_get/setSymbol() */ 554 for(i = 0; i < UNUM_FORMAT_SYMBOL_COUNT; ++i) { 555 symbol[0] = (UChar)(0x41 + i); 556 symbol[1] = (UChar)(0x61 + i); 557 unum_setSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, 2, &status); 558 if(U_FAILURE(status)) { 559 log_err("Error from unum_setSymbol(%d): %s\n", i, myErrorName(status)); 560 return; 561 } 562 } 563 for(i = 0; i < UNUM_FORMAT_SYMBOL_COUNT; ++i) { 564 resultlength = unum_getSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, sizeof(symbol)/U_SIZEOF_UCHAR, &status); 565 if(U_FAILURE(status)) { 566 log_err("Error from unum_getSymbol(%d): %s\n", i, myErrorName(status)); 567 return; 568 } 569 if(resultlength != 2 || symbol[0] != 0x41 + i || symbol[1] != 0x61 + i) { 570 log_err("Failure in unum_getSymbol(%d): got unexpected symbol\n", i); 571 } 572 } 573 /*try getting from a bogus symbol*/ 574 unum_getSymbol(cur_frpattern, (UNumberFormatSymbol)i, symbol, sizeof(symbol)/U_SIZEOF_UCHAR, &status); 575 if(U_SUCCESS(status)){ 576 log_err("Error : Expected U_ILLEGAL_ARGUMENT_ERROR for bogus symbol"); 577 } 578 if(U_FAILURE(status)){ 579 if(status != U_ILLEGAL_ARGUMENT_ERROR){ 580 log_err("Error: Expected U_ILLEGAL_ARGUMENT_ERROR for bogus symbol, Got %s\n", myErrorName(status)); 581 } 582 } 583 status=U_ZERO_ERROR; 584 585 /* Testing unum_getTextAttribute() and unum_setTextAttribute()*/ 586 log_verbose("\nTesting getting and setting text attributes\n"); 587 resultlength=5; 588 unum_getTextAttribute(cur_fr, UNUM_NEGATIVE_SUFFIX, temp, resultlength, &status); 589 if(U_FAILURE(status)) 590 { 591 log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status)); 592 } 593 unum_setTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, temp, u_strlen(temp), &status); 594 if(U_FAILURE(status)) 595 { 596 log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status)); 597 } 598 unum_getTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, suffix, resultlength, &status); 599 if(U_FAILURE(status)) 600 { 601 log_err("Failure in gettting the Text attributes of number format: %s\n", myErrorName(status)); 602 } 603 if(u_strcmp(suffix,temp)!=0) 604 log_err("Fail:Error in setTextAttribute or getTextAttribute in setting and getting suffix\n"); 605 else 606 log_verbose("Pass: setting and getting suffix works fine\n"); 607 /*set it back to normal */ 608 u_uastrcpy(temp,"$"); 609 unum_setTextAttribute(cur_def, UNUM_NEGATIVE_SUFFIX, temp, u_strlen(temp), &status); 610 611 /*checking some more text setter conditions */ 612 u_uastrcpy(prefix, "+"); 613 unum_setTextAttribute(def, UNUM_POSITIVE_PREFIX, prefix, u_strlen(prefix) , &status); 614 if(U_FAILURE(status)) 615 { 616 log_err("error in setting the text attributes : %s\n", myErrorName(status)); 617 } 618 unum_getTextAttribute(def, UNUM_POSITIVE_PREFIX, temp, resultlength, &status); 619 if(U_FAILURE(status)) 620 { 621 log_err("error in getting the text attributes : %s\n", myErrorName(status)); 622 } 623 624 if(u_strcmp(prefix, temp)!=0) 625 log_err("ERROR: get and setTextAttributes with positive prefix failed\n"); 626 else 627 log_verbose("Pass: get and setTextAttributes with positive prefix works fine\n"); 628 629 u_uastrcpy(prefix, "+"); 630 unum_setTextAttribute(def, UNUM_NEGATIVE_PREFIX, prefix, u_strlen(prefix), &status); 631 if(U_FAILURE(status)) 632 { 633 log_err("error in setting the text attributes : %s\n", myErrorName(status)); 634 } 635 unum_getTextAttribute(def, UNUM_NEGATIVE_PREFIX, temp, resultlength, &status); 636 if(U_FAILURE(status)) 637 { 638 log_err("error in getting the text attributes : %s\n", myErrorName(status)); 639 } 640 if(u_strcmp(prefix, temp)!=0) 641 log_err("ERROR: get and setTextAttributes with negative prefix failed\n"); 642 else 643 log_verbose("Pass: get and setTextAttributes with negative prefix works fine\n"); 644 645 u_uastrcpy(suffix, "+"); 646 unum_setTextAttribute(def, UNUM_NEGATIVE_SUFFIX, suffix, u_strlen(suffix) , &status); 647 if(U_FAILURE(status)) 648 { 649 log_err("error in setting the text attributes: %s\n", myErrorName(status)); 650 } 651 652 unum_getTextAttribute(def, UNUM_NEGATIVE_SUFFIX, temp, resultlength, &status); 653 if(U_FAILURE(status)) 654 { 655 log_err("error in getting the text attributes : %s\n", myErrorName(status)); 656 } 657 if(u_strcmp(suffix, temp)!=0) 658 log_err("ERROR: get and setTextAttributes with negative suffix failed\n"); 659 else 660 log_verbose("Pass: get and settextAttributes with negative suffix works fine\n"); 661 662 u_uastrcpy(suffix, "++"); 663 unum_setTextAttribute(def, UNUM_POSITIVE_SUFFIX, suffix, u_strlen(suffix) , &status); 664 if(U_FAILURE(status)) 665 { 666 log_err("error in setting the text attributes: %s\n", myErrorName(status)); 667 } 668 669 unum_getTextAttribute(def, UNUM_POSITIVE_SUFFIX, temp, resultlength, &status); 670 if(U_FAILURE(status)) 671 { 672 log_err("error in getting the text attributes : %s\n", myErrorName(status)); 673 } 674 if(u_strcmp(suffix, temp)!=0) 675 log_err("ERROR: get and setTextAttributes with negative suffix failed\n"); 676 else 677 log_verbose("Pass: get and settextAttributes with negative suffix works fine\n"); 678 679 /*Testing unum_getAttribute and unum_setAttribute() */ 680 log_verbose("\nTesting get and set Attributes\n"); 681 attr=UNUM_GROUPING_SIZE; 682 newvalue=unum_getAttribute(def, attr); 683 newvalue=2; 684 unum_setAttribute(def, attr, newvalue); 685 if(unum_getAttribute(def,attr)!=2) 686 log_err("Fail: error in setting and getting attributes for UNUM_GROUPING_SIZE\n"); 687 else 688 log_verbose("Pass: setting and getting attributes for UNUM_GROUPING_SIZE works fine\n"); 689 690 attr=UNUM_MULTIPLIER; 691 newvalue=unum_getAttribute(def, attr); 692 newvalue=8; 693 unum_setAttribute(def, attr, newvalue); 694 if(unum_getAttribute(def,attr) != 8) 695 log_err("error in setting and getting attributes for UNUM_MULTIPLIER\n"); 696 else 697 log_verbose("Pass:setting and getting attributes for UNUM_MULTIPLIER works fine\n"); 698 699 attr=UNUM_SECONDARY_GROUPING_SIZE; 700 newvalue=unum_getAttribute(def, attr); 701 newvalue=2; 702 unum_setAttribute(def, attr, newvalue); 703 if(unum_getAttribute(def,attr) != 2) 704 log_err("error in setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE\n"); 705 else 706 log_verbose("Pass:setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE works fine\n"); 707 708 /*testing set and get Attributes extensively */ 709 log_verbose("\nTesting get and set attributes extensively\n"); 710 for(attr=UNUM_PARSE_INT_ONLY; attr<= UNUM_PADDING_POSITION; attr=(UNumberFormatAttribute)((int32_t)attr + 1) ) 711 { 712 newvalue=unum_getAttribute(fr, attr); 713 unum_setAttribute(def, attr, newvalue); 714 if(unum_getAttribute(def,attr)!=unum_getAttribute(fr, attr)) 715 log_err("error in setting and getting attributes\n"); 716 else 717 log_verbose("Pass: attributes set and retrieved successfully\n"); 718 } 719 720 /*testing spellout format to make sure we can use it successfully.*/ 721 log_verbose("\nTesting spellout format\n"); 722 if (spellout_def) 723 { 724 static const int32_t values[] = { 0, -5, 105, 1005, 105050 }; 725 for (i = 0; i < LENGTH(values); ++i) { 726 UChar buffer[128]; 727 int32_t len; 728 int32_t value = values[i]; 729 status = U_ZERO_ERROR; 730 len = unum_format(spellout_def, value, buffer, LENGTH(buffer), NULL, &status); 731 if(U_FAILURE(status)) { 732 log_err("Error in formatting using unum_format(spellout_fmt, ...): %s\n", myErrorName(status)); 733 } else { 734 int32_t pp = 0; 735 int32_t parseResult; 736 char logbuf[256]; 737 ustrToAstr(buffer, len, logbuf, LENGTH(logbuf)); 738 log_verbose("formatted %d as '%s', length: %d\n", value, logbuf, len); 739 740 parseResult = unum_parse(spellout_def, buffer, len, &pp, &status); 741 if (U_FAILURE(status)) { 742 log_err("Error in parsing using unum_format(spellout_fmt, ...): %s\n", myErrorName(status)); 743 } else if (parseResult != value) { 744 log_err("unum_format result %d != value %d\n", parseResult, value); 745 } 746 } 747 } 748 } 749 else { 750 log_err("Spellout format is unavailable\n"); 751 } 752 753 { /* Test for ticket #7079 */ 754 UNumberFormat* dec_en; 755 UChar groupingSep[] = { 0 }; 756 UChar numPercent[] = { 0x0031, 0x0032, 0x0025, 0 }; /* "12%" */ 757 double parseResult = 0.0; 758 759 status=U_ZERO_ERROR; 760 dec_en = unum_open(UNUM_DECIMAL, NULL, 0, "en_US", NULL, &status); 761 unum_setAttribute(dec_en, UNUM_LENIENT_PARSE, 0); 762 unum_setSymbol(dec_en, UNUM_GROUPING_SEPARATOR_SYMBOL, groupingSep, 0, &status); 763 parseResult = unum_parseDouble(dec_en, numPercent, -1, NULL, &status); 764 /* Without the fix in #7079, the above call will hang */ 765 if ( U_FAILURE(status) || parseResult != 12.0 ) { 766 log_err("unum_parseDouble with empty groupingSep: status %s, parseResult %f not 12.0\n", 767 myErrorName(status), parseResult); 768 } else { 769 log_verbose("unum_parseDouble with empty groupingSep: no hang, OK\n"); 770 } 771 unum_close(dec_en); 772 } 773 774 /*closing the NumberFormat() using unum_close(UNumberFormat*)")*/ 775 unum_close(def); 776 unum_close(fr); 777 unum_close(cur_def); 778 unum_close(cur_fr); 779 unum_close(per_def); 780 unum_close(per_fr); 781 unum_close(spellout_def); 782 unum_close(pattern); 783 unum_close(cur_frpattern); 784 unum_close(myclone); 785 786 } 787 788 typedef struct { 789 const char * testname; 790 const char * locale; 791 const UChar * source; 792 int32_t startPos; 793 int32_t value; 794 int32_t endPos; 795 UErrorCode status; 796 } SpelloutParseTest; 797 798 static const UChar ustr_en0[] = {0x7A, 0x65, 0x72, 0x6F, 0}; /* zero */ 799 static const UChar ustr_123[] = {0x31, 0x32, 0x33, 0}; /* 123 */ 800 static const UChar ustr_en123[] = {0x6f, 0x6e, 0x65, 0x20, 0x68, 0x75, 0x6e, 0x64, 0x72, 0x65, 0x64, 801 0x20, 0x74, 0x77, 0x65, 0x6e, 0x74, 0x79, 802 0x2d, 0x74, 0x68, 0x72, 0x65, 0x65, 0}; /* one hundred twenty-three */ 803 static const UChar ustr_fr123[] = {0x63, 0x65, 0x6e, 0x74, 0x2d, 0x76, 0x69, 0x6e, 0x67, 0x74, 0x2d, 804 0x74, 0x72, 0x6f, 0x69, 0x73, 0}; /* cent-vingt-trois */ 805 static const UChar ustr_ja123[] = {0x767e, 0x4e8c, 0x5341, 0x4e09, 0}; /* kanji 100(+)2(*)10(+)3 */ 806 807 static const SpelloutParseTest spelloutParseTests[] = { 808 /* name loc src start val end status */ 809 { "en0", "en", ustr_en0, 0, 0, 4, U_ZERO_ERROR }, 810 { "en0", "en", ustr_en0, 2, 0, 2, U_PARSE_ERROR }, 811 { "en0", "ja", ustr_en0, 0, 0, 0, U_PARSE_ERROR }, 812 { "123", "en", ustr_123, 0, 123, 3, U_ZERO_ERROR }, 813 { "en123", "en", ustr_en123, 0, 123, 24, U_ZERO_ERROR }, 814 { "en123", "en", ustr_en123, 12, 23, 24, U_ZERO_ERROR }, 815 { "en123", "fr", ustr_en123, 16, 0, 16, U_PARSE_ERROR }, 816 { "fr123", "fr", ustr_fr123, 0, 123, 16, U_ZERO_ERROR }, 817 { "fr123", "fr", ustr_fr123, 5, 23, 16, U_ZERO_ERROR }, 818 { "fr123", "en", ustr_fr123, 0, 0, 0, U_PARSE_ERROR }, 819 { "ja123", "ja", ustr_ja123, 0, 123, 4, U_ZERO_ERROR }, 820 { "ja123", "ja", ustr_ja123, 1, 23, 4, U_ZERO_ERROR }, 821 { "ja123", "fr", ustr_ja123, 0, 0, 0, U_PARSE_ERROR }, 822 { NULL, NULL, NULL, 0, 0, 0, 0 } /* terminator */ 823 }; 824 825 static void TestSpelloutNumberParse() 826 { 827 const SpelloutParseTest * testPtr; 828 for (testPtr = spelloutParseTests; testPtr->testname != NULL; ++testPtr) { 829 UErrorCode status = U_ZERO_ERROR; 830 int32_t value, position = testPtr->startPos; 831 UNumberFormat *nf = unum_open(UNUM_SPELLOUT, NULL, 0, testPtr->locale, NULL, &status); 832 if (U_FAILURE(status)) { 833 log_err_status(status, "unum_open fails for UNUM_SPELLOUT with locale %s, status %s\n", testPtr->locale, myErrorName(status)); 834 continue; 835 } 836 value = unum_parse(nf, testPtr->source, -1, &position, &status); 837 if ( value != testPtr->value || position != testPtr->endPos || status != testPtr->status ) { 838 log_err("unum_parse SPELLOUT, locale %s, testname %s, startPos %d: for value / endPos / status, expected %d / %d / %s, got %d / %d / %s\n", 839 testPtr->locale, testPtr->testname, testPtr->startPos, 840 testPtr->value, testPtr->endPos, myErrorName(testPtr->status), 841 value, position, myErrorName(status) ); 842 } 843 unum_close(nf); 844 } 845 } 846 847 static void TestSignificantDigits() 848 { 849 UChar temp[128]; 850 int32_t resultlengthneeded; 851 int32_t resultlength; 852 UErrorCode status = U_ZERO_ERROR; 853 UChar *result = NULL; 854 UNumberFormat* fmt; 855 double d = 123456.789; 856 857 u_uastrcpy(temp, "###0.0#"); 858 fmt=unum_open(UNUM_IGNORE, temp, -1, NULL, NULL,&status); 859 if (U_FAILURE(status)) { 860 log_err("got unexpected error for unum_open: '%s'\n", u_errorName(status)); 861 return; 862 } 863 unum_setAttribute(fmt, UNUM_SIGNIFICANT_DIGITS_USED, TRUE); 864 unum_setAttribute(fmt, UNUM_MAX_SIGNIFICANT_DIGITS, 6); 865 866 u_uastrcpy(temp, "123457"); 867 resultlength=0; 868 resultlengthneeded=unum_formatDouble(fmt, d, NULL, resultlength, NULL, &status); 869 if(status==U_BUFFER_OVERFLOW_ERROR) 870 { 871 status=U_ZERO_ERROR; 872 resultlength=resultlengthneeded+1; 873 result=(UChar*)malloc(sizeof(UChar) * resultlength); 874 unum_formatDouble(fmt, d, result, resultlength, NULL, &status); 875 } 876 if(U_FAILURE(status)) 877 { 878 log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status)); 879 return; 880 } 881 if(u_strcmp(result, temp)==0) 882 log_verbose("Pass: Number Formatting using unum_formatDouble() Successful\n"); 883 else 884 log_err("FAIL: Error in number formatting using unum_formatDouble()\n"); 885 free(result); 886 unum_close(fmt); 887 } 888 889 static void TestNumberFormatPadding() 890 { 891 UChar *result=NULL; 892 UChar temp1[512]; 893 894 UErrorCode status=U_ZERO_ERROR; 895 int32_t resultlength; 896 int32_t resultlengthneeded; 897 UNumberFormat *pattern; 898 double d1; 899 double d = -10456.37; 900 UFieldPosition pos1; 901 int32_t parsepos; 902 903 /* create a number format using unum_openPattern(....)*/ 904 log_verbose("\nTesting unum_openPattern() with padding\n"); 905 u_uastrcpy(temp1, "*#,##0.0#*;(#,##0.0#)"); 906 status=U_ZERO_ERROR; 907 pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), NULL, NULL,&status); 908 if(U_SUCCESS(status)) 909 { 910 log_err("error in unum_openPattern(%s): %s\n", temp1, myErrorName(status) ); 911 } 912 else 913 { 914 unum_close(pattern); 915 } 916 917 /* u_uastrcpy(temp1, "*x#,###,###,##0.0#;(*x#,###,###,##0.0#)"); */ 918 u_uastrcpy(temp1, "*x#,###,###,##0.0#;*x(###,###,##0.0#)"); 919 status=U_ZERO_ERROR; 920 pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), "en_US",NULL, &status); 921 if(U_FAILURE(status)) 922 { 923 log_err_status(status, "error in padding unum_openPattern(%s): %s\n", temp1, myErrorName(status) );; 924 } 925 else { 926 log_verbose("Pass: padding unum_openPattern() works fine\n"); 927 928 /*test for unum_toPattern()*/ 929 log_verbose("\nTesting padding unum_toPattern()\n"); 930 resultlength=0; 931 resultlengthneeded=unum_toPattern(pattern, FALSE, NULL, resultlength, &status); 932 if(status==U_BUFFER_OVERFLOW_ERROR) 933 { 934 status=U_ZERO_ERROR; 935 resultlength=resultlengthneeded+1; 936 result=(UChar*)malloc(sizeof(UChar) * resultlength); 937 unum_toPattern(pattern, FALSE, result, resultlength, &status); 938 } 939 if(U_FAILURE(status)) 940 { 941 log_err("error in extracting the padding pattern from UNumberFormat: %s\n", myErrorName(status)); 942 } 943 else 944 { 945 if(u_strcmp(result, temp1)!=0) 946 log_err("FAIL: Error in extracting the padding pattern using unum_toPattern()\n"); 947 else 948 log_verbose("Pass: extracted the padding pattern correctly using unum_toPattern()\n"); 949 free(result); 950 } 951 /* u_uastrcpy(temp1, "(xxxxxxx10,456.37)"); */ 952 u_uastrcpy(temp1, "xxxxx(10,456.37)"); 953 resultlength=0; 954 pos1.field = 1; /* Fraction field */ 955 resultlengthneeded=unum_formatDouble(pattern, d, NULL, resultlength, &pos1, &status); 956 if(status==U_BUFFER_OVERFLOW_ERROR) 957 { 958 status=U_ZERO_ERROR; 959 resultlength=resultlengthneeded+1; 960 result=(UChar*)malloc(sizeof(UChar) * resultlength); 961 unum_formatDouble(pattern, d, result, resultlength, NULL, &status); 962 } 963 if(U_FAILURE(status)) 964 { 965 log_err("Error in formatting using unum_formatDouble(.....) with padding : %s\n", myErrorName(status)); 966 } 967 else 968 { 969 if(u_strcmp(result, temp1)==0) 970 log_verbose("Pass: Number Formatting using unum_formatDouble() padding Successful\n"); 971 else 972 log_err("FAIL: Error in number formatting using unum_formatDouble() with padding\n"); 973 if(pos1.beginIndex == 13 && pos1.endIndex == 15) 974 log_verbose("Pass: Complete number formatting using unum_formatDouble() successful\n"); 975 else 976 log_err("Fail: Error in complete number Formatting using unum_formatDouble()\nGot: b=%d end=%d\nExpected: b=13 end=15\n", 977 pos1.beginIndex, pos1.endIndex); 978 979 980 /* Testing unum_parse() and unum_parseDouble() */ 981 log_verbose("\nTesting padding unum_parseDouble()\n"); 982 parsepos=0; 983 d1=unum_parseDouble(pattern, result, u_strlen(result), &parsepos, &status); 984 if(U_FAILURE(status)) 985 { 986 log_err("padding parse failed. The error is : %s\n", myErrorName(status)); 987 } 988 989 if(d1!=d) 990 log_err("Fail: Error in padding parsing\n"); 991 else 992 log_verbose("Pass: padding parsing successful\n"); 993 free(result); 994 } 995 } 996 997 unum_close(pattern); 998 } 999 1000 static UBool 1001 withinErr(double a, double b, double err) { 1002 return uprv_fabs(a - b) < uprv_fabs(a * err); 1003 } 1004 1005 static void TestInt64Format() { 1006 UChar temp1[512]; 1007 UChar result[512]; 1008 UNumberFormat *fmt; 1009 UErrorCode status = U_ZERO_ERROR; 1010 const double doubleInt64Max = (double)U_INT64_MAX; 1011 const double doubleInt64Min = (double)U_INT64_MIN; 1012 const double doubleBig = 10.0 * (double)U_INT64_MAX; 1013 int32_t val32; 1014 int64_t val64; 1015 double valDouble; 1016 int32_t parsepos; 1017 1018 /* create a number format using unum_openPattern(....) */ 1019 log_verbose("\nTesting Int64Format\n"); 1020 u_uastrcpy(temp1, "#.#E0"); 1021 fmt = unum_open(UNUM_IGNORE, temp1, u_strlen(temp1), NULL, NULL, &status); 1022 if(U_FAILURE(status)) { 1023 log_err("error in unum_openPattern(): %s\n", myErrorName(status)); 1024 } else { 1025 unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 20); 1026 unum_formatInt64(fmt, U_INT64_MAX, result, 512, NULL, &status); 1027 if (U_FAILURE(status)) { 1028 log_err("error in unum_format(): %s\n", myErrorName(status)); 1029 } else { 1030 log_verbose("format int64max: '%s'\n", result); 1031 parsepos = 0; 1032 val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status); 1033 if (status != U_INVALID_FORMAT_ERROR) { 1034 log_err("parse didn't report error: %s\n", myErrorName(status)); 1035 } else if (val32 != INT32_MAX) { 1036 log_err("parse didn't pin return value, got: %d\n", val32); 1037 } 1038 1039 status = U_ZERO_ERROR; 1040 parsepos = 0; 1041 val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status); 1042 if (U_FAILURE(status)) { 1043 log_err("parseInt64 returned error: %s\n", myErrorName(status)); 1044 } else if (val64 != U_INT64_MAX) { 1045 log_err("parseInt64 returned incorrect value, got: %ld\n", val64); 1046 } 1047 1048 status = U_ZERO_ERROR; 1049 parsepos = 0; 1050 valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status); 1051 if (U_FAILURE(status)) { 1052 log_err("parseDouble returned error: %s\n", myErrorName(status)); 1053 } else if (valDouble != doubleInt64Max) { 1054 log_err("parseDouble returned incorrect value, got: %g\n", valDouble); 1055 } 1056 } 1057 1058 unum_formatInt64(fmt, U_INT64_MIN, result, 512, NULL, &status); 1059 if (U_FAILURE(status)) { 1060 log_err("error in unum_format(): %s\n", myErrorName(status)); 1061 } else { 1062 log_verbose("format int64min: '%s'\n", result); 1063 parsepos = 0; 1064 val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status); 1065 if (status != U_INVALID_FORMAT_ERROR) { 1066 log_err("parse didn't report error: %s\n", myErrorName(status)); 1067 } else if (val32 != INT32_MIN) { 1068 log_err("parse didn't pin return value, got: %d\n", val32); 1069 } 1070 1071 status = U_ZERO_ERROR; 1072 parsepos = 0; 1073 val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status); 1074 if (U_FAILURE(status)) { 1075 log_err("parseInt64 returned error: %s\n", myErrorName(status)); 1076 } else if (val64 != U_INT64_MIN) { 1077 log_err("parseInt64 returned incorrect value, got: %ld\n", val64); 1078 } 1079 1080 status = U_ZERO_ERROR; 1081 parsepos = 0; 1082 valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status); 1083 if (U_FAILURE(status)) { 1084 log_err("parseDouble returned error: %s\n", myErrorName(status)); 1085 } else if (valDouble != doubleInt64Min) { 1086 log_err("parseDouble returned incorrect value, got: %g\n", valDouble); 1087 } 1088 } 1089 1090 unum_formatDouble(fmt, doubleBig, result, 512, NULL, &status); 1091 if (U_FAILURE(status)) { 1092 log_err("error in unum_format(): %s\n", myErrorName(status)); 1093 } else { 1094 log_verbose("format doubleBig: '%s'\n", result); 1095 parsepos = 0; 1096 val32 = unum_parse(fmt, result, u_strlen(result), &parsepos, &status); 1097 if (status != U_INVALID_FORMAT_ERROR) { 1098 log_err("parse didn't report error: %s\n", myErrorName(status)); 1099 } else if (val32 != INT32_MAX) { 1100 log_err("parse didn't pin return value, got: %d\n", val32); 1101 } 1102 1103 status = U_ZERO_ERROR; 1104 parsepos = 0; 1105 val64 = unum_parseInt64(fmt, result, u_strlen(result), &parsepos, &status); 1106 if (status != U_INVALID_FORMAT_ERROR) { 1107 log_err("parseInt64 didn't report error error: %s\n", myErrorName(status)); 1108 } else if (val64 != U_INT64_MAX) { 1109 log_err("parseInt64 returned incorrect value, got: %ld\n", val64); 1110 } 1111 1112 status = U_ZERO_ERROR; 1113 parsepos = 0; 1114 valDouble = unum_parseDouble(fmt, result, u_strlen(result), &parsepos, &status); 1115 if (U_FAILURE(status)) { 1116 log_err("parseDouble returned error: %s\n", myErrorName(status)); 1117 } else if (!withinErr(valDouble, doubleBig, 1e-15)) { 1118 log_err("parseDouble returned incorrect value, got: %g\n", valDouble); 1119 } 1120 } 1121 } 1122 unum_close(fmt); 1123 } 1124 1125 1126 static void test_fmt(UNumberFormat* fmt, UBool isDecimal) { 1127 char temp[512]; 1128 UChar buffer[512]; 1129 int32_t BUFSIZE = sizeof(buffer)/sizeof(buffer[0]); 1130 double vals[] = { 1131 -.2, 0, .2, 5.5, 15.2, 250, 123456789 1132 }; 1133 int i; 1134 1135 for (i = 0; i < sizeof(vals)/sizeof(vals[0]); ++i) { 1136 UErrorCode status = U_ZERO_ERROR; 1137 unum_formatDouble(fmt, vals[i], buffer, BUFSIZE, NULL, &status); 1138 if (U_FAILURE(status)) { 1139 log_err("failed to format: %g, returned %s\n", vals[i], u_errorName(status)); 1140 } else { 1141 u_austrcpy(temp, buffer); 1142 log_verbose("formatting %g returned '%s'\n", vals[i], temp); 1143 } 1144 } 1145 1146 /* check APIs now */ 1147 { 1148 UErrorCode status = U_ZERO_ERROR; 1149 UParseError perr; 1150 u_uastrcpy(buffer, "#,##0.0#"); 1151 unum_applyPattern(fmt, FALSE, buffer, -1, &perr, &status); 1152 if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) { 1153 log_err("got unexpected error for applyPattern: '%s'\n", u_errorName(status)); 1154 } 1155 } 1156 1157 { 1158 int isLenient = unum_getAttribute(fmt, UNUM_LENIENT_PARSE); 1159 log_verbose("lenient: 0x%x\n", isLenient); 1160 if (isDecimal ? (isLenient != -1) : (isLenient == TRUE)) { 1161 log_err("didn't expect lenient value: %d\n", isLenient); 1162 } 1163 1164 unum_setAttribute(fmt, UNUM_LENIENT_PARSE, TRUE); 1165 isLenient = unum_getAttribute(fmt, UNUM_LENIENT_PARSE); 1166 if (isDecimal ? (isLenient != -1) : (isLenient == FALSE)) { 1167 log_err("didn't expect lenient value after set: %d\n", isLenient); 1168 } 1169 } 1170 1171 { 1172 double val2; 1173 double val = unum_getDoubleAttribute(fmt, UNUM_LENIENT_PARSE); 1174 if (val != -1) { 1175 log_err("didn't expect double attribute\n"); 1176 } 1177 val = unum_getDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT); 1178 if ((val == -1) == isDecimal) { 1179 log_err("didn't expect -1 rounding increment\n"); 1180 } 1181 unum_setDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT, val+.5); 1182 val2 = unum_getDoubleAttribute(fmt, UNUM_ROUNDING_INCREMENT); 1183 if (isDecimal && (val2 - val != .5)) { 1184 log_err("set rounding increment had no effect on decimal format"); 1185 } 1186 } 1187 1188 { 1189 UErrorCode status = U_ZERO_ERROR; 1190 int len = unum_getTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, BUFSIZE, &status); 1191 if (isDecimal ? (status != U_UNSUPPORTED_ERROR) : U_FAILURE(status)) { 1192 log_err("got unexpected error for get default ruleset: '%s'\n", u_errorName(status)); 1193 } 1194 if (U_SUCCESS(status)) { 1195 u_austrcpy(temp, buffer); 1196 log_verbose("default ruleset: '%s'\n", temp); 1197 } 1198 1199 status = U_ZERO_ERROR; 1200 len = unum_getTextAttribute(fmt, UNUM_PUBLIC_RULESETS, buffer, BUFSIZE, &status); 1201 if (isDecimal ? (status != U_UNSUPPORTED_ERROR) : U_FAILURE(status)) { 1202 log_err("got unexpected error for get public rulesets: '%s'\n", u_errorName(status)); 1203 } 1204 if (U_SUCCESS(status)) { 1205 u_austrcpy(temp, buffer); 1206 log_verbose("public rulesets: '%s'\n", temp); 1207 1208 /* set the default ruleset to the first one found, and retry */ 1209 1210 if (len > 0) { 1211 for (i = 0; i < len && temp[i] != ';'; ++i){}; 1212 if (i < len) { 1213 buffer[i] = 0; 1214 unum_setTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, -1, &status); 1215 if (U_FAILURE(status)) { 1216 log_err("unexpected error setting default ruleset: '%s'\n", u_errorName(status)); 1217 } else { 1218 int len2 = unum_getTextAttribute(fmt, UNUM_DEFAULT_RULESET, buffer, BUFSIZE, &status); 1219 if (U_FAILURE(status)) { 1220 log_err("could not fetch default ruleset: '%s'\n", u_errorName(status)); 1221 } else if (len2 != i) { 1222 u_austrcpy(temp, buffer); 1223 log_err("unexpected ruleset len: %d ex: %d val: %s\n", len2, i, temp); 1224 } else { 1225 for (i = 0; i < sizeof(vals)/sizeof(vals[0]); ++i) { 1226 status = U_ZERO_ERROR; 1227 unum_formatDouble(fmt, vals[i], buffer, BUFSIZE, NULL, &status); 1228 if (U_FAILURE(status)) { 1229 log_err("failed to format: %g, returned %s\n", vals[i], u_errorName(status)); 1230 } else { 1231 u_austrcpy(temp, buffer); 1232 log_verbose("formatting %g returned '%s'\n", vals[i], temp); 1233 } 1234 } 1235 } 1236 } 1237 } 1238 } 1239 } 1240 } 1241 1242 { 1243 UErrorCode status = U_ZERO_ERROR; 1244 unum_toPattern(fmt, FALSE, buffer, BUFSIZE, &status); 1245 if (U_SUCCESS(status)) { 1246 u_austrcpy(temp, buffer); 1247 log_verbose("pattern: '%s'\n", temp); 1248 } else if (status != U_BUFFER_OVERFLOW_ERROR) { 1249 log_err("toPattern failed unexpectedly: %s\n", u_errorName(status)); 1250 } else { 1251 log_verbose("pattern too long to display\n"); 1252 } 1253 } 1254 1255 { 1256 UErrorCode status = U_ZERO_ERROR; 1257 int len = unum_getSymbol(fmt, UNUM_CURRENCY_SYMBOL, buffer, BUFSIZE, &status); 1258 if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) { 1259 log_err("unexpected error getting symbol: '%s'\n", u_errorName(status)); 1260 } 1261 1262 unum_setSymbol(fmt, UNUM_CURRENCY_SYMBOL, buffer, len, &status); 1263 if (isDecimal ? U_FAILURE(status) : (status != U_UNSUPPORTED_ERROR)) { 1264 log_err("unexpected error setting symbol: '%s'\n", u_errorName(status)); 1265 } 1266 } 1267 } 1268 1269 static void TestNonExistentCurrency() { 1270 UNumberFormat *format; 1271 UErrorCode status = U_ZERO_ERROR; 1272 UChar currencySymbol[8]; 1273 static const UChar QQQ[] = {0x51, 0x51, 0x51, 0}; 1274 1275 /* Get a non-existent currency and make sure it returns the correct currency code. */ 1276 format = unum_open(UNUM_CURRENCY, NULL, 0, "th_TH@currency=QQQ", NULL, &status); 1277 if (format == NULL || U_FAILURE(status)) { 1278 log_data_err("unum_open did not return expected result for non-existent requested currency: '%s' (Are you missing data?)\n", u_errorName(status)); 1279 } 1280 else { 1281 unum_getSymbol(format, 1282 UNUM_CURRENCY_SYMBOL, 1283 currencySymbol, 1284 sizeof(currencySymbol)/sizeof(currencySymbol[0]), 1285 &status); 1286 if (u_strcmp(currencySymbol, QQQ) != 0) { 1287 log_err("unum_open set the currency to QQQ\n"); 1288 } 1289 } 1290 unum_close(format); 1291 } 1292 1293 static void TestRBNFFormat() { 1294 UErrorCode status; 1295 UParseError perr; 1296 UChar pat[1024]; 1297 UChar tempUChars[512]; 1298 UNumberFormat *formats[5]; 1299 int COUNT = sizeof(formats)/sizeof(formats[0]); 1300 int i; 1301 1302 for (i = 0; i < COUNT; ++i) { 1303 formats[i] = 0; 1304 } 1305 1306 /* instantiation */ 1307 status = U_ZERO_ERROR; 1308 u_uastrcpy(pat, "#,##0.0#;(#,##0.0#)"); 1309 formats[0] = unum_open(UNUM_PATTERN_DECIMAL, pat, -1, "en_US", &perr, &status); 1310 if (U_FAILURE(status)) { 1311 log_err_status(status, "unable to open decimal pattern -> %s\n", u_errorName(status)); 1312 } 1313 1314 status = U_ZERO_ERROR; 1315 formats[1] = unum_open(UNUM_SPELLOUT, NULL, 0, "en_US", &perr, &status); 1316 if (U_FAILURE(status)) { 1317 log_err_status(status, "unable to open spellout -> %s\n", u_errorName(status)); 1318 } 1319 1320 status = U_ZERO_ERROR; 1321 formats[2] = unum_open(UNUM_ORDINAL, NULL, 0, "en_US", &perr, &status); 1322 if (U_FAILURE(status)) { 1323 log_err_status(status, "unable to open ordinal -> %s\n", u_errorName(status)); 1324 } 1325 1326 status = U_ZERO_ERROR; 1327 formats[3] = unum_open(UNUM_DURATION, NULL, 0, "en_US", &perr, &status); 1328 if (U_FAILURE(status)) { 1329 log_err_status(status, "unable to open duration %s\n", u_errorName(status)); 1330 } 1331 1332 status = U_ZERO_ERROR; 1333 u_uastrcpy(pat, 1334 "%standard:\n" 1335 "-x: minus >>;\n" 1336 "x.x: << point >>;\n" 1337 "zero; one; two; three; four; five; six; seven; eight; nine;\n" 1338 "ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n" 1339 "seventeen; eighteen; nineteen;\n" 1340 "20: twenty[->>];\n" 1341 "30: thirty[->>];\n" 1342 "40: forty[->>];\n" 1343 "50: fifty[->>];\n" 1344 "60: sixty[->>];\n" 1345 "70: seventy[->>];\n" 1346 "80: eighty[->>];\n" 1347 "90: ninety[->>];\n" 1348 "100: =#,##0=;\n"); 1349 u_uastrcpy(tempUChars, 1350 "%simple:\n" 1351 "=%standard=;\n" 1352 "20: twenty[ and change];\n" 1353 "30: thirty[ and change];\n" 1354 "40: forty[ and change];\n" 1355 "50: fifty[ and change];\n" 1356 "60: sixty[ and change];\n" 1357 "70: seventy[ and change];\n" 1358 "80: eighty[ and change];\n" 1359 "90: ninety[ and change];\n" 1360 "100: =#,##0=;\n" 1361 "%bogus:\n" 1362 "0.x: tiny;\n" 1363 "x.x: << point something;\n" 1364 "=%standard=;\n" 1365 "20: some reasonable number;\n" 1366 "100: some substantial number;\n" 1367 "100,000,000: some huge number;\n"); 1368 /* This is to get around some compiler warnings about char * string length. */ 1369 u_strcat(pat, tempUChars); 1370 formats[4] = unum_open(UNUM_PATTERN_RULEBASED, pat, -1, "en_US", &perr, &status); 1371 if (U_FAILURE(status)) { 1372 log_err_status(status, "unable to open rulebased pattern -> %s\n", u_errorName(status)); 1373 } 1374 if (U_FAILURE(status)) { 1375 log_err_status(status, "Something failed with %s\n", u_errorName(status)); 1376 return; 1377 } 1378 1379 for (i = 0; i < COUNT; ++i) { 1380 log_verbose("\n\ntesting format %d\n", i); 1381 test_fmt(formats[i], (UBool)(i == 0)); 1382 } 1383 1384 for (i = 0; i < COUNT; ++i) { 1385 unum_close(formats[i]); 1386 } 1387 } 1388 1389 static void TestCurrencyRegression(void) { 1390 /* 1391 I've found a case where unum_parseDoubleCurrency is not doing what I 1392 expect. The value I pass in is $1234567890q123460000.00 and this 1393 returns with a status of zero error & a parse pos of 22 (I would 1394 expect a parse error at position 11). 1395 1396 I stepped into DecimalFormat::subparse() and it looks like it parses 1397 the first 10 digits and then stops parsing at the q but doesn't set an 1398 error. Then later in DecimalFormat::parse() the value gets crammed 1399 into a long (which greatly truncates the value). 1400 1401 This is very problematic for me 'cause I try to remove chars that are 1402 invalid but this allows my users to enter bad chars and truncates 1403 their data! 1404 */ 1405 1406 UChar buf[1024]; 1407 UChar currency[8]; 1408 char acurrency[16]; 1409 double d; 1410 UNumberFormat *cur; 1411 int32_t pos; 1412 UErrorCode status = U_ZERO_ERROR; 1413 const int32_t expected = 11; 1414 1415 currency[0]=0; 1416 u_uastrcpy(buf, "$1234567890q643210000.00"); 1417 cur = unum_open(UNUM_CURRENCY, NULL,0,"en_US", NULL, &status); 1418 1419 if(U_FAILURE(status)) { 1420 log_data_err("unum_open failed: %s (Are you missing data?)\n", u_errorName(status)); 1421 return; 1422 } 1423 1424 status = U_ZERO_ERROR; /* so we can test it later. */ 1425 pos = 0; 1426 1427 d = unum_parseDoubleCurrency(cur, 1428 buf, 1429 -1, 1430 &pos, /* 0 = start */ 1431 currency, 1432 &status); 1433 1434 u_austrcpy(acurrency, currency); 1435 1436 if(U_FAILURE(status) || (pos != expected)) { 1437 log_err("unum_parseDoubleCurrency should have failed with pos %d, but gave: value %.9f, err %s, pos=%d, currency [%s]\n", 1438 expected, d, u_errorName(status), pos, acurrency); 1439 } else { 1440 log_verbose("unum_parseDoubleCurrency failed, value %.9f err %s, pos %d, currency [%s]\n", d, u_errorName(status), pos, acurrency); 1441 } 1442 1443 unum_close(cur); 1444 } 1445 1446 static void TestTextAttributeCrash(void) { 1447 UChar ubuffer[64] = {0x0049,0x004E,0x0052,0}; 1448 static const UChar expectedNeg[] = {0x0049,0x004E,0x0052,0x0031,0x0032,0x0033,0x0034,0x002E,0x0035,0}; 1449 static const UChar expectedPos[] = {0x0031,0x0032,0x0033,0x0034,0x002E,0x0035,0}; 1450 int32_t used; 1451 UErrorCode status = U_ZERO_ERROR; 1452 UNumberFormat *nf = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status); 1453 if (U_FAILURE(status)) { 1454 log_data_err("FAILED 1 -> %s (Are you missing data?)\n", u_errorName(status)); 1455 return; 1456 } 1457 unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, 3, &status); 1458 /* 1459 * the usual negative prefix and suffix seem to be '($' and ')' at this point 1460 * also crashes if UNUM_NEGATIVE_SUFFIX is substituted for UNUM_NEGATIVE_PREFIX here 1461 */ 1462 used = unum_getTextAttribute(nf, UNUM_NEGATIVE_PREFIX, ubuffer, 64, &status); 1463 unum_setTextAttribute(nf, UNUM_NEGATIVE_PREFIX, ubuffer, used, &status); 1464 if (U_FAILURE(status)) { 1465 log_err("FAILED 2\n"); exit(1); 1466 } 1467 log_verbose("attempting to format...\n"); 1468 used = unum_formatDouble(nf, -1234.5, ubuffer, 64, NULL, &status); 1469 if (U_FAILURE(status) || 64 < used) { 1470 log_err("Failed formatting %s\n", u_errorName(status)); 1471 return; 1472 } 1473 if (u_strcmp(expectedNeg, ubuffer) == 0) { 1474 log_err("Didn't get expected negative result\n"); 1475 } 1476 used = unum_formatDouble(nf, 1234.5, ubuffer, 64, NULL, &status); 1477 if (U_FAILURE(status) || 64 < used) { 1478 log_err("Failed formatting %s\n", u_errorName(status)); 1479 return; 1480 } 1481 if (u_strcmp(expectedPos, ubuffer) == 0) { 1482 log_err("Didn't get expected positive result\n"); 1483 } 1484 unum_close(nf); 1485 } 1486 1487 static void TestNBSPPatternRtNum(const char *testcase, UNumberFormat *nf, double myNumber) { 1488 UErrorCode status = U_ZERO_ERROR; 1489 UChar myString[20]; 1490 char tmpbuf[200]; 1491 double aNumber = -1.0; 1492 unum_formatDouble(nf, myNumber, myString, 20, NULL, &status); 1493 log_verbose("%s: formatted %.2f into %s\n", testcase, myNumber, u_austrcpy(tmpbuf, myString)); 1494 if(U_FAILURE(status)) { 1495 log_err("%s: failed format of %.2g with %s\n", testcase, myNumber, u_errorName(status)); 1496 return; 1497 } 1498 aNumber = unum_parse(nf, myString, -1, NULL, &status); 1499 if(U_FAILURE(status)) { 1500 log_err("%s: failed parse with %s\n", testcase, u_errorName(status)); 1501 return; 1502 } 1503 if(uprv_fabs(aNumber-myNumber)>.001) { 1504 log_err("FAIL: %s: formatted %.2f, parsed into %.2f\n", testcase, myNumber, aNumber); 1505 } else { 1506 log_verbose("PASS: %s: formatted %.2f, parsed into %.2f\n", testcase, myNumber, aNumber); 1507 } 1508 } 1509 1510 static void TestNBSPPatternRT(const char *testcase, UNumberFormat *nf) { 1511 TestNBSPPatternRtNum(testcase, nf, 12345.); 1512 TestNBSPPatternRtNum(testcase, nf, -12345.); 1513 } 1514 1515 static void TestNBSPInPattern(void) { 1516 UErrorCode status = U_ZERO_ERROR; 1517 UNumberFormat* nf = NULL; 1518 const char *testcase; 1519 1520 1521 testcase="ar_AE UNUM_CURRENCY"; 1522 nf = unum_open(UNUM_CURRENCY, NULL, -1, "ar_AE", NULL, &status); 1523 if(U_FAILURE(status) || nf == NULL) { 1524 log_data_err("%s: unum_open failed with %s (Are you missing data?)\n", testcase, u_errorName(status)); 1525 return; 1526 } 1527 TestNBSPPatternRT(testcase, nf); 1528 1529 /* if we don't have CLDR 1.6 data, bring out the problem anyways */ 1530 { 1531 #define SPECIAL_PATTERN "\\u00A4\\u00A4'\\u062f.\\u0625.\\u200f\\u00a0'###0.00" 1532 UChar pat[200]; 1533 testcase = "ar_AE special pattern: " SPECIAL_PATTERN; 1534 u_unescape(SPECIAL_PATTERN, pat, sizeof(pat)/sizeof(pat[0])); 1535 unum_applyPattern(nf, FALSE, pat, -1, NULL, &status); 1536 if(U_FAILURE(status)) { 1537 log_err("%s: unum_applyPattern failed with %s\n", testcase, u_errorName(status)); 1538 } else { 1539 TestNBSPPatternRT(testcase, nf); 1540 } 1541 #undef SPECIAL_PATTERN 1542 } 1543 unum_close(nf); status = U_ZERO_ERROR; 1544 1545 testcase="ar_AE UNUM_DECIMAL"; 1546 nf = unum_open(UNUM_DECIMAL, NULL, -1, "ar_AE", NULL, &status); 1547 if(U_FAILURE(status)) { 1548 log_err("%s: unum_open failed with %s\n", testcase, u_errorName(status)); 1549 } 1550 TestNBSPPatternRT(testcase, nf); 1551 unum_close(nf); status = U_ZERO_ERROR; 1552 1553 testcase="ar_AE UNUM_PERCENT"; 1554 nf = unum_open(UNUM_PERCENT, NULL, -1, "ar_AE", NULL, &status); 1555 if(U_FAILURE(status)) { 1556 log_err("%s: unum_open failed with %s\n", testcase, u_errorName(status)); 1557 } 1558 TestNBSPPatternRT(testcase, nf); 1559 unum_close(nf); status = U_ZERO_ERROR; 1560 1561 1562 1563 } 1564 1565 #endif /* #if !UCONFIG_NO_FORMATTING */ 1566