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 #include "unicode/utypes.h" 10 11 #include "cmemory.h" 12 #include "cstring.h" 13 #include "unicode/unistr.h" 14 #include "unicode/uniset.h" 15 #include "unicode/resbund.h" 16 #include "restest.h" 17 18 #include <stdlib.h> 19 #include <time.h> 20 #include <string.h> 21 #include <limits.h> 22 23 //*************************************************************************************** 24 25 static const UChar kErrorUChars[] = { 0x45, 0x52, 0x52, 0x4f, 0x52, 0 }; 26 static const int32_t kErrorLength = 5; 27 28 //*************************************************************************************** 29 30 enum E_Where 31 { 32 e_Root, 33 e_te, 34 e_te_IN, 35 e_Where_count 36 }; 37 38 //*************************************************************************************** 39 40 #define CONFIRM_EQ(actual, expected, myAction) if ((expected)==(actual)) { record_pass(myAction); } else { record_fail(myAction + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of " + (expected) + "\n");} 41 #define CONFIRM_GE(actual, expected, myAction) if ((actual)>=(expected)) { record_pass(myAction); } else { record_fail(myAction + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of x >= " + (expected) + "\n");} 42 #define CONFIRM_NE(actual, expected, myAction) if ((expected)!=(actual)) { record_pass(myAction); } else { record_fail(myAction + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of x != " + (expected) + "\n");} 43 44 #define CONFIRM_UErrorCode(actual, expected, myAction) if ((expected)==(actual)) { record_pass(myAction); } else { record_fail(myAction + (UnicodeString)" returned " + u_errorName(actual) + " instead of " + u_errorName(expected) + "\n"); } 45 46 //*************************************************************************************** 47 48 /** 49 * Convert an integer, positive or negative, to a character string radix 10. 50 */ 51 char* 52 itoa(int32_t i, char* buf) 53 { 54 char* result = buf; 55 56 // Handle negative 57 if (i < 0) 58 { 59 *buf++ = '-'; 60 i = -i; 61 } 62 63 // Output digits in reverse order 64 char* p = buf; 65 do 66 { 67 *p++ = (char)('0' + (i % 10)); 68 i /= 10; 69 } 70 while (i); 71 *p-- = 0; 72 73 // Reverse the string 74 while (buf < p) 75 { 76 char c = *buf; 77 *buf++ = *p; 78 *p-- = c; 79 } 80 81 return result; 82 } 83 84 85 86 //*************************************************************************************** 87 88 // Array of our test objects 89 90 static struct 91 { 92 const char* name; 93 Locale *locale; 94 UErrorCode expected_constructor_status; 95 E_Where where; 96 UBool like[e_Where_count]; 97 UBool inherits[e_Where_count]; 98 } 99 param[] = 100 { 101 // "te" means test 102 // "IN" means inherits 103 // "NE" or "ne" means "does not exist" 104 105 { "root", NULL, U_ZERO_ERROR, e_Root, { TRUE, FALSE, FALSE }, { TRUE, FALSE, FALSE } }, 106 { "te", NULL, U_ZERO_ERROR, e_te, { FALSE, TRUE, FALSE }, { TRUE, TRUE, FALSE } }, 107 { "te_IN", NULL, U_ZERO_ERROR, e_te_IN, { FALSE, FALSE, TRUE }, { TRUE, TRUE, TRUE } }, 108 { "te_NE", NULL, U_USING_FALLBACK_WARNING, e_te, { FALSE, TRUE, FALSE }, { TRUE, TRUE, FALSE } }, 109 { "te_IN_NE", NULL, U_USING_FALLBACK_WARNING, e_te_IN, { FALSE, FALSE, TRUE }, { TRUE, TRUE, TRUE } }, 110 { "ne", NULL, U_USING_DEFAULT_WARNING, e_Root, { TRUE, FALSE, FALSE }, { TRUE, FALSE, FALSE } } 111 }; 112 113 static const int32_t bundles_count = UPRV_LENGTHOF(param); 114 115 //*************************************************************************************** 116 117 /** 118 * Return a random unsigned long l where 0N <= l <= ULONG_MAX. 119 */ 120 121 uint32_t 122 randul() 123 { 124 static UBool initialized = FALSE; 125 if (!initialized) 126 { 127 srand((unsigned)time(NULL)); 128 initialized = TRUE; 129 } 130 // Assume rand has at least 12 bits of precision 131 uint32_t l = 0; 132 for (uint32_t i=0; i<sizeof(l); ++i) 133 ((char*)&l)[i] = (char)((rand() & 0x0FF0) >> 4); 134 return l; 135 } 136 137 /** 138 * Return a random double x where 0.0 <= x < 1.0. 139 */ 140 double 141 randd() 142 { 143 return (double)(randul() / ULONG_MAX); 144 } 145 146 /** 147 * Return a random integer i where 0 <= i < n. 148 */ 149 int32_t randi(int32_t n) 150 { 151 return (int32_t)(randd() * n); 152 } 153 154 //*************************************************************************************** 155 156 /* 157 Don't use more than one of these at a time because of the Locale names 158 */ 159 ResourceBundleTest::ResourceBundleTest() 160 : pass(0), 161 fail(0) 162 { 163 if (param[5].locale == NULL) { 164 param[0].locale = new Locale("root"); 165 param[1].locale = new Locale("te"); 166 param[2].locale = new Locale("te", "IN"); 167 param[3].locale = new Locale("te", "NE"); 168 param[4].locale = new Locale("te", "IN", "NE"); 169 param[5].locale = new Locale("ne"); 170 } 171 } 172 173 ResourceBundleTest::~ResourceBundleTest() 174 { 175 if (param[5].locale) { 176 int idx; 177 for (idx = 0; idx < UPRV_LENGTHOF(param); idx++) { 178 delete param[idx].locale; 179 param[idx].locale = NULL; 180 } 181 } 182 } 183 184 void ResourceBundleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) 185 { 186 if (exec) logln("TestSuite ResourceBundleTest: "); 187 switch (index) { 188 #if !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION 189 case 0: name = "TestResourceBundles"; if (exec) TestResourceBundles(); break; 190 case 1: name = "TestConstruction"; if (exec) TestConstruction(); break; 191 case 2: name = "TestGetSize"; if (exec) TestGetSize(); break; 192 case 3: name = "TestGetLocaleByType"; if (exec) TestGetLocaleByType(); break; 193 #else 194 case 0: case 1: case 2: case 3: name = "skip"; break; 195 #endif 196 197 case 4: name = "TestExemplar"; if (exec) TestExemplar(); break; 198 default: name = ""; break; //needed to end loop 199 } 200 } 201 202 //*************************************************************************************** 203 204 void 205 ResourceBundleTest::TestResourceBundles() 206 { 207 UErrorCode status = U_ZERO_ERROR; 208 209 loadTestData(status); 210 if(U_FAILURE(status)) 211 { 212 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(status))); 213 return; 214 } 215 216 /* Make sure that users using te_IN for the default locale don't get test failures. */ 217 Locale originalDefault; 218 if (Locale::getDefault() == Locale("te_IN")) { 219 Locale::setDefault(Locale("en_US"), status); 220 } 221 222 testTag("only_in_Root", TRUE, FALSE, FALSE); 223 testTag("only_in_te", FALSE, TRUE, FALSE); 224 testTag("only_in_te_IN", FALSE, FALSE, TRUE); 225 testTag("in_Root_te", TRUE, TRUE, FALSE); 226 testTag("in_Root_te_te_IN", TRUE, TRUE, TRUE); 227 testTag("in_Root_te_IN", TRUE, FALSE, TRUE); 228 testTag("in_te_te_IN", FALSE, TRUE, TRUE); 229 testTag("nonexistent", FALSE, FALSE, FALSE); 230 logln("Passed: %d\nFailed: %d", pass, fail); 231 232 /* Restore the default locale for the other tests. */ 233 Locale::setDefault(originalDefault, status); 234 } 235 236 void 237 ResourceBundleTest::TestConstruction() 238 { 239 UErrorCode err = U_ZERO_ERROR; 240 Locale locale("te", "IN"); 241 242 const char* testdatapath=loadTestData(err); 243 if(U_FAILURE(err)) 244 { 245 dataerrln("Could not load testdata.dat " + UnicodeString(testdatapath) + ", " + UnicodeString(u_errorName(err))); 246 return; 247 } 248 249 /* Make sure that users using te_IN for the default locale don't get test failures. */ 250 Locale originalDefault; 251 if (Locale::getDefault() == Locale("te_IN")) { 252 Locale::setDefault(Locale("en_US"), err); 253 } 254 255 ResourceBundle test1((UnicodeString)testdatapath, err); 256 ResourceBundle test2(testdatapath, locale, err); 257 //ResourceBundle test1("c:\\icu\\icu\\source\\test\\testdata\\testdata", err); 258 //ResourceBundle test2("c:\\icu\\icu\\source\\test\\testdata\\testdata", locale, err); 259 260 UnicodeString result1(test1.getStringEx("string_in_Root_te_te_IN", err)); 261 UnicodeString result2(test2.getStringEx("string_in_Root_te_te_IN", err)); 262 263 if (U_FAILURE(err)) { 264 errln("Something threw an error in TestConstruction()"); 265 return; 266 } 267 268 logln("for string_in_Root_te_te_IN, default.txt had " + result1); 269 logln("for string_in_Root_te_te_IN, te_IN.txt had " + result2); 270 271 if (result1 != "ROOT" || result2 != "TE_IN") 272 errln("Construction test failed; run verbose for more information"); 273 274 const char* version1; 275 const char* version2; 276 277 version1 = test1.getVersionNumber(); 278 version2 = test2.getVersionNumber(); 279 280 char *versionID1 = new char[1+strlen(version1)]; // + 1 for zero byte 281 char *versionID2 = new char[1+ strlen(version2)]; // + 1 for zero byte 282 283 strcpy(versionID1, "44.0"); // hardcoded, please change if the default.txt file or ResourceBundle::kVersionSeparater is changed. 284 285 strcpy(versionID2, "55.0"); // hardcoded, please change if the te_IN.txt file or ResourceBundle::kVersionSeparater is changed. 286 287 logln(UnicodeString("getVersionNumber on default.txt returned ") + version1); 288 logln(UnicodeString("getVersionNumber on te_IN.txt returned ") + version2); 289 290 if (strcmp(version1, versionID1) != 0 || strcmp(version2, versionID2) != 0) 291 errln("getVersionNumber() failed"); 292 293 delete[] versionID1; 294 delete[] versionID2; 295 296 /* Restore the default locale for the other tests. */ 297 Locale::setDefault(originalDefault, err); 298 } 299 300 //*************************************************************************************** 301 302 UBool 303 ResourceBundleTest::testTag(const char* frag, 304 UBool in_Root, 305 UBool in_te, 306 UBool in_te_IN) 307 { 308 int32_t failOrig = fail; 309 310 // Make array from input params 311 312 UBool is_in[] = { in_Root, in_te, in_te_IN }; 313 314 const char* NAME[] = { "ROOT", "TE", "TE_IN" }; 315 316 // Now try to load the desired items 317 318 char tag[100]; 319 UnicodeString action; 320 321 int32_t i,j,actual_bundle; 322 // int32_t row,col; 323 int32_t index; 324 UErrorCode status = U_ZERO_ERROR; 325 const char* testdatapath; 326 testdatapath=loadTestData(status); 327 if(U_FAILURE(status)) 328 { 329 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(status))); 330 return FALSE; 331 } 332 333 for (i=0; i<bundles_count; ++i) 334 { 335 action = "Constructor for "; 336 action += param[i].name; 337 338 status = U_ZERO_ERROR; 339 ResourceBundle theBundle( testdatapath, *param[i].locale, status); 340 //ResourceBundle theBundle( "c:\\icu\\icu\\source\\test\\testdata\\testdata", *param[i].locale, status); 341 CONFIRM_UErrorCode(status, param[i].expected_constructor_status, action); 342 343 if(i == 5) 344 actual_bundle = 0; /* ne -> default */ 345 else if(i == 3) 346 actual_bundle = 1; /* te_NE -> te */ 347 else if(i == 4) 348 actual_bundle = 2; /* te_IN_NE -> te_IN */ 349 else 350 actual_bundle = i; 351 352 353 UErrorCode expected_resource_status = U_MISSING_RESOURCE_ERROR; 354 for (j=e_te_IN; j>=e_Root; --j) 355 { 356 if (is_in[j] && param[i].inherits[j]) 357 { 358 if(j == actual_bundle) /* it's in the same bundle OR it's a nonexistent=default bundle (5) */ 359 expected_resource_status = U_ZERO_ERROR; 360 else if(j == 0) 361 expected_resource_status = U_USING_DEFAULT_WARNING; 362 else 363 expected_resource_status = U_USING_FALLBACK_WARNING; 364 365 break; 366 } 367 } 368 369 UErrorCode expected_status; 370 371 UnicodeString base; 372 for (j=param[i].where; j>=0; --j) 373 { 374 if (is_in[j]) 375 { 376 base = NAME[j]; 377 break; 378 } 379 } 380 381 //-------------------------------------------------------------------------- 382 // string 383 384 uprv_strcpy(tag, "string_"); 385 uprv_strcat(tag, frag); 386 387 action = param[i].name; 388 action += ".getString("; 389 action += tag; 390 action += ")"; 391 392 393 status = U_ZERO_ERROR; 394 395 UnicodeString string(theBundle.getStringEx(tag, status)); 396 397 if(U_FAILURE(status)) { 398 string.setTo(TRUE, kErrorUChars, kErrorLength); 399 } 400 401 CONFIRM_UErrorCode(status, expected_resource_status, action); 402 403 UnicodeString expected_string(kErrorUChars); 404 if (U_SUCCESS(status)) { 405 expected_string = base; 406 } 407 408 CONFIRM_EQ(string, expected_string, action); 409 410 //-------------------------------------------------------------------------- 411 // array 412 413 uprv_strcpy(tag, "array_"); 414 uprv_strcat(tag, frag); 415 416 action = param[i].name; 417 action += ".get("; 418 action += tag; 419 action += ")"; 420 421 status = U_ZERO_ERROR; 422 ResourceBundle arrayBundle(theBundle.get(tag, status)); 423 CONFIRM_UErrorCode(status, expected_resource_status, action); 424 int32_t count = arrayBundle.getSize(); 425 426 if (U_SUCCESS(status)) 427 { 428 CONFIRM_GE(count, 1, action); 429 430 for (j=0; j < count; ++j) 431 { 432 char buf[32]; 433 UnicodeString value(arrayBundle.getStringEx(j, status)); 434 expected_string = base; 435 expected_string += itoa(j,buf); 436 CONFIRM_EQ(value, expected_string, action); 437 } 438 439 action = param[i].name; 440 action += ".getStringEx("; 441 action += tag; 442 action += ")"; 443 444 for (j=0; j<100; ++j) 445 { 446 index = count ? (randi(count * 3) - count) : (randi(200) - 100); 447 status = U_ZERO_ERROR; 448 string = kErrorUChars; 449 UnicodeString t(arrayBundle.getStringEx(index, status)); 450 expected_status = (index >= 0 && index < count) ? expected_resource_status : U_MISSING_RESOURCE_ERROR; 451 CONFIRM_UErrorCode(status, expected_status, action); 452 453 if (U_SUCCESS(status)) 454 { 455 char buf[32]; 456 expected_string = base; 457 expected_string += itoa(index,buf); 458 } 459 else 460 { 461 expected_string = kErrorUChars; 462 } 463 CONFIRM_EQ(string, expected_string, action); 464 } 465 } 466 else if (status != expected_resource_status) 467 { 468 record_fail("Error getting " + (UnicodeString)tag); 469 return (UBool)(failOrig != fail); 470 } 471 472 } 473 474 return (UBool)(failOrig != fail); 475 } 476 477 void 478 ResourceBundleTest::record_pass(UnicodeString passMessage) 479 { 480 logln(passMessage); 481 ++pass; 482 } 483 void 484 ResourceBundleTest::record_fail(UnicodeString errMessage) 485 { 486 err(errMessage); 487 ++fail; 488 } 489 490 void 491 ResourceBundleTest::TestExemplar(){ 492 493 int32_t locCount = uloc_countAvailable(); 494 int32_t locIndex=0; 495 int num=0; 496 UErrorCode status = U_ZERO_ERROR; 497 for(;locIndex<locCount;locIndex++){ 498 const char* locale = uloc_getAvailable(locIndex); 499 UResourceBundle *resb =ures_open(NULL,locale,&status); 500 if(U_SUCCESS(status) && status!=U_USING_FALLBACK_WARNING && status!=U_USING_DEFAULT_WARNING){ 501 int32_t len=0; 502 const UChar* strSet = ures_getStringByKey(resb,"ExemplarCharacters",&len,&status); 503 UnicodeSet set(strSet,status); 504 if(U_FAILURE(status)){ 505 errln("Could not construct UnicodeSet from pattern for ExemplarCharacters in locale : %s. Error: %s",locale,u_errorName(status)); 506 status=U_ZERO_ERROR; 507 } 508 num++; 509 } 510 ures_close(resb); 511 } 512 logln("Number of installed locales with exemplar characters that could be tested: %d",num); 513 514 } 515 516 void 517 ResourceBundleTest::TestGetSize(void) 518 { 519 const struct { 520 const char* key; 521 int32_t size; 522 } test[] = { 523 { "zerotest", 1}, 524 { "one", 1}, 525 { "importtest", 1}, 526 { "integerarray", 1}, 527 { "emptyarray", 0}, 528 { "emptytable", 0}, 529 { "emptystring", 1}, /* empty string is still a string */ 530 { "emptyint", 1}, 531 { "emptybin", 1}, 532 { "testinclude", 1}, 533 { "collations", 1}, /* not 2 - there is hidden %%CollationBin */ 534 }; 535 536 UErrorCode status = U_ZERO_ERROR; 537 538 const char* testdatapath = loadTestData(status); 539 int32_t i = 0, j = 0; 540 int32_t size = 0; 541 542 if(U_FAILURE(status)) 543 { 544 dataerrln("Could not load testdata.dat %s\n", u_errorName(status)); 545 return; 546 } 547 548 ResourceBundle rb(testdatapath, "testtypes", status); 549 if(U_FAILURE(status)) 550 { 551 err("Could not testtypes resource bundle %s\n", u_errorName(status)); 552 return; 553 } 554 555 for(i = 0; i < UPRV_LENGTHOF(test); i++) { 556 ResourceBundle res = rb.get(test[i].key, status); 557 if(U_FAILURE(status)) 558 { 559 err("Couldn't find the key %s. Error: %s\n", u_errorName(status)); 560 return; 561 } 562 size = res.getSize(); 563 if(size != test[i].size) { 564 err("Expected size %i, got size %i for key %s\n", test[i].size, size, test[i].key); 565 for(j = 0; j < size; j++) { 566 ResourceBundle helper = res.get(j, status); 567 err("%s\n", helper.getKey()); 568 } 569 } 570 } 571 } 572 573 void 574 ResourceBundleTest::TestGetLocaleByType(void) 575 { 576 const struct { 577 const char *requestedLocale; 578 const char *resourceKey; 579 const char *validLocale; 580 const char *actualLocale; 581 } test[] = { 582 { "te_IN_BLAH", "string_only_in_te_IN", "te_IN", "te_IN" }, 583 { "te_IN_BLAH", "string_only_in_te", "te_IN", "te" }, 584 { "te_IN_BLAH", "string_only_in_Root", "te_IN", "root" }, 585 { "te_IN_BLAH_01234567890_01234567890_01234567890_01234567890_01234567890_01234567890", "array_2d_only_in_Root", "te_IN", "root" }, 586 { "te_IN_BLAH@currency=euro", "array_2d_only_in_te_IN", "te_IN", "te_IN" }, 587 { "te_IN_BLAH@calendar=thai;collation=phonebook", "array_2d_only_in_te", "te_IN", "te" } 588 }; 589 590 UErrorCode status = U_ZERO_ERROR; 591 592 const char* testdatapath = loadTestData(status); 593 int32_t i = 0; 594 Locale locale; 595 596 if(U_FAILURE(status)) 597 { 598 dataerrln("Could not load testdata.dat %s\n", u_errorName(status)); 599 return; 600 } 601 602 for(i = 0; i < UPRV_LENGTHOF(test); i++) { 603 ResourceBundle rb(testdatapath, test[i].requestedLocale, status); 604 if(U_FAILURE(status)) 605 { 606 err("Could not open resource bundle %s (error %s)\n", test[i].requestedLocale, u_errorName(status)); 607 status = U_ZERO_ERROR; 608 continue; 609 } 610 611 ResourceBundle res = rb.get(test[i].resourceKey, status); 612 if(U_FAILURE(status)) 613 { 614 err("Couldn't find the key %s. Error: %s\n", test[i].resourceKey, u_errorName(status)); 615 status = U_ZERO_ERROR; 616 continue; 617 } 618 619 locale = res.getLocale(ULOC_REQUESTED_LOCALE, status); 620 if(U_SUCCESS(status) && locale != Locale::getDefault()) { 621 err("Expected requested locale to be %s. Got %s\n", test[i].requestedLocale, locale.getName()); 622 } 623 status = U_ZERO_ERROR; 624 locale = res.getLocale(ULOC_VALID_LOCALE, status); 625 if(strcmp(locale.getName(), test[i].validLocale) != 0) { 626 err("Expected valid locale to be %s. Got %s\n", test[i].requestedLocale, locale.getName()); 627 } 628 locale = res.getLocale(ULOC_ACTUAL_LOCALE, status); 629 if(strcmp(locale.getName(), test[i].actualLocale) != 0) { 630 err("Expected actual locale to be %s. Got %s\n", test[i].requestedLocale, locale.getName()); 631 } 632 } 633 } 634