1 /* 2 ******************************************************************************* 3 * Copyright (C) 2003-2009, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_COLLATION 11 12 #include "svccoll.h" 13 #include "unicode/coll.h" 14 #include "unicode/strenum.h" 15 #include "hash.h" 16 #include "uassert.h" 17 18 #include "ucol_imp.h" // internal api needed to test ucollator equality 19 #include "cstring.h" // internal api used to compare locale strings 20 21 void CollationServiceTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par */) 22 { 23 if (exec) logln("TestSuite CollationServiceTest: "); 24 switch (index) { 25 TESTCASE(0, TestRegister); 26 TESTCASE(1, TestRegisterFactory); 27 TESTCASE(2, TestSeparateTree); 28 default: name = ""; break; 29 } 30 } 31 32 void CollationServiceTest::TestRegister() 33 { 34 #if !UCONFIG_NO_SERVICE 35 // register a singleton 36 const Locale& FR = Locale::getFrance(); 37 const Locale& US = Locale::getUS(); 38 const Locale US_FOO("en", "US", "FOO"); 39 40 UErrorCode status = U_ZERO_ERROR; 41 42 Collator* frcol = Collator::createInstance(FR, status); 43 Collator* uscol = Collator::createInstance(US, status); 44 if(U_FAILURE(status)) { 45 errcheckln(status, "Failed to create collators with %s", u_errorName(status)); 46 delete frcol; 47 delete uscol; 48 return; 49 } 50 51 { // try override en_US collator 52 URegistryKey key = Collator::registerInstance(frcol, US, status); 53 54 Collator* ncol = Collator::createInstance(US_FOO, status); 55 if (*frcol != *ncol) { 56 errln("register of french collator for en_US failed on request for en_US_FOO"); 57 } 58 // ensure original collator's params not touched 59 Locale loc = frcol->getLocale(ULOC_REQUESTED_LOCALE, status); 60 if (loc != FR) { 61 errln(UnicodeString("fr collator's requested locale changed to ") + loc.getName()); 62 } 63 loc = frcol->getLocale(ULOC_VALID_LOCALE, status); 64 if (loc != FR) { 65 errln(UnicodeString("fr collator's valid locale changed to ") + loc.getName()); 66 } 67 68 loc = ncol->getLocale(ULOC_REQUESTED_LOCALE, status); 69 if (loc != US_FOO) { 70 errln(UnicodeString("requested locale for en_US_FOO is not en_US_FOO but ") + loc.getName()); 71 } 72 loc = ncol->getLocale(ULOC_VALID_LOCALE, status); 73 if (loc != US) { 74 errln(UnicodeString("valid locale for en_US_FOO is not en_US but ") + loc.getName()); 75 } 76 loc = ncol->getLocale(ULOC_ACTUAL_LOCALE, status); 77 if (loc != US) { 78 errln(UnicodeString("actual locale for en_US_FOO is not en_US but ") + loc.getName()); 79 } 80 delete ncol; ncol = NULL; 81 82 if (!Collator::unregister(key, status)) { 83 errln("failed to unregister french collator"); 84 } 85 // !!! frcol pointer is now invalid !!! 86 87 ncol = Collator::createInstance(US, status); 88 if (*uscol != *ncol) { 89 errln("collator after unregister does not match original"); 90 } 91 delete ncol; ncol = NULL; 92 } 93 94 // recreate frcol 95 frcol = Collator::createInstance(FR, status); 96 97 UCollator* frFR = ucol_open("fr_FR", &status); 98 99 { // try create collator for new locale 100 Locale fu_FU_FOO("fu", "FU", "FOO"); 101 Locale fu_FU("fu", "FU", ""); 102 103 Collator* fucol = Collator::createInstance(fu_FU, status); 104 URegistryKey key = Collator::registerInstance(frcol, fu_FU, status); 105 Collator* ncol = Collator::createInstance(fu_FU_FOO, status); 106 if (*frcol != *ncol) { 107 errln("register of fr collator for fu_FU failed"); 108 } 109 110 UnicodeString locName = fu_FU.getName(); 111 StringEnumeration* localeEnum = Collator::getAvailableLocales(); 112 UBool found = FALSE; 113 const UnicodeString* locStr, *ls2; 114 for (locStr = localeEnum->snext(status); 115 !found && locStr != NULL; 116 locStr = localeEnum->snext(status)) { 117 // 118 if (locName == *locStr) { 119 found = TRUE; 120 } 121 } 122 123 StringEnumeration *le2 = NULL; 124 localeEnum->reset(status); 125 int32_t i, count; 126 count = localeEnum->count(status); 127 for(i = 0; i < count; ++i) { 128 if(i == count / 2) { 129 le2 = localeEnum->clone(); 130 if(le2 == NULL || count != le2->count(status)) { 131 errln("ServiceEnumeration.clone() failed"); 132 break; 133 } 134 } 135 if(i >= count / 2) { 136 locStr = localeEnum->snext(status); 137 ls2 = le2->snext(status); 138 if(*locStr != *ls2) { 139 errln("ServiceEnumeration.clone() failed for item %d", i); 140 } 141 } else { 142 localeEnum->snext(status); 143 } 144 } 145 146 delete localeEnum; 147 delete le2; 148 149 if (!found) { 150 errln("new locale fu_FU not reported as supported locale"); 151 } 152 153 UnicodeString displayName; 154 Collator::getDisplayName(fu_FU, displayName); 155 if (displayName != "fu (FU)") { 156 errln(UnicodeString("found ") + displayName + " for fu_FU"); 157 } 158 159 Collator::getDisplayName(fu_FU, fu_FU, displayName); 160 if (displayName != "fu (FU)") { 161 errln(UnicodeString("found ") + displayName + " for fu_FU"); 162 } 163 164 // test ucol_open 165 UCollator* fufu = ucol_open("fu_FU_FOO", &status); 166 if (!fufu) { 167 errln("could not open fu_FU_FOO with ucol_open"); 168 } else { 169 if (!ucol_equals(fufu, frFR)) { 170 errln("collator fufu != collator frFR"); 171 } 172 } 173 174 if (!Collator::unregister(key, status)) { 175 errln("failed to unregister french collator"); 176 } 177 // !!! note frcoll invalid again, but we're no longer using it 178 179 // other collators should still work ok 180 Locale nloc = ncol->getLocale(ULOC_VALID_LOCALE, status); 181 if (nloc != fu_FU) { 182 errln(UnicodeString("asked for nloc valid locale after close and got") + nloc.getName()); 183 } 184 delete ncol; ncol = NULL; 185 186 if (fufu) { 187 const char* nlocstr = ucol_getLocaleByType(fufu, ULOC_VALID_LOCALE, &status); 188 if (uprv_strcmp(nlocstr, "fu_FU") != 0) { 189 errln(UnicodeString("asked for uloc valid locale after close and got ") + nlocstr); 190 } 191 ucol_close(fufu); 192 } 193 ucol_close(frFR); 194 195 ncol = Collator::createInstance(fu_FU, status); 196 if (*fucol != *ncol) { 197 errln("collator after unregister does not match original fu_FU"); 198 } 199 delete uscol; uscol = NULL; 200 delete ncol; ncol = NULL; 201 delete fucol; fucol = NULL; 202 } 203 #endif 204 } 205 206 // ------------------ 207 208 #if !UCONFIG_NO_SERVICE 209 struct CollatorInfo { 210 Locale locale; 211 Collator* collator; 212 Hashtable* displayNames; // locale name -> string 213 214 CollatorInfo(const Locale& locale, Collator* collatorToAdopt, Hashtable* displayNamesToAdopt); 215 ~CollatorInfo(); 216 UnicodeString& getDisplayName(const Locale& displayLocale, UnicodeString& name) const; 217 }; 218 219 CollatorInfo::CollatorInfo(const Locale& _locale, Collator* _collator, Hashtable* _displayNames) 220 : locale(_locale) 221 , collator(_collator) 222 , displayNames(_displayNames) 223 { 224 } 225 226 CollatorInfo::~CollatorInfo() { 227 delete collator; 228 delete displayNames; 229 } 230 231 UnicodeString& 232 CollatorInfo::getDisplayName(const Locale& displayLocale, UnicodeString& name) const { 233 if (displayNames) { 234 UnicodeString* val = (UnicodeString*)displayNames->get(displayLocale.getName()); 235 if (val) { 236 name = *val; 237 return name; 238 } 239 } 240 241 return locale.getDisplayName(displayLocale, name); 242 } 243 244 // --------------- 245 246 class TestFactory : public CollatorFactory { 247 CollatorInfo** info; 248 int32_t count; 249 UnicodeString* ids; 250 251 const CollatorInfo* getInfo(const Locale& loc) const { 252 for (CollatorInfo** p = info; *p; ++p) { 253 if (loc == (**p).locale) { 254 return *p; 255 } 256 } 257 return NULL; 258 } 259 260 public: 261 TestFactory(CollatorInfo** _info) 262 : info(_info) 263 , count(0) 264 , ids(NULL) 265 { 266 CollatorInfo** p; 267 for (p = info; *p; ++p) {} 268 count = (int32_t)(p - info); 269 } 270 271 ~TestFactory() { 272 for (CollatorInfo** p = info; *p; ++p) { 273 delete *p; 274 } 275 delete[] info; 276 delete[] ids; 277 } 278 279 virtual Collator* createCollator(const Locale& loc) { 280 const CollatorInfo* ci = getInfo(loc); 281 if (ci) { 282 return ci->collator->clone(); 283 } 284 return NULL; 285 } 286 287 virtual UnicodeString& getDisplayName(const Locale& objectLocale, 288 const Locale& displayLocale, 289 UnicodeString& result) 290 { 291 const CollatorInfo* ci = getInfo(objectLocale); 292 if (ci) { 293 ci->getDisplayName(displayLocale, result); 294 } else { 295 result.setToBogus(); 296 } 297 return result; 298 } 299 300 const UnicodeString* getSupportedIDs(int32_t& _count, UErrorCode& status) { 301 if (U_SUCCESS(status)) { 302 if (!ids) { 303 ids = new UnicodeString[count]; 304 if (!ids) { 305 status = U_MEMORY_ALLOCATION_ERROR; 306 _count = 0; 307 return NULL; 308 } 309 310 for (int i = 0; i < count; ++i) { 311 ids[i] = info[i]->locale.getName(); 312 } 313 } 314 315 _count = count; 316 return ids; 317 } 318 return NULL; 319 } 320 321 virtual inline UClassID getDynamicClassID() const { 322 return (UClassID)&gClassID; 323 } 324 325 static UClassID getStaticClassID() { 326 return (UClassID)&gClassID; 327 } 328 329 private: 330 static char gClassID; 331 }; 332 333 char TestFactory::gClassID = 0; 334 #endif 335 336 void CollationServiceTest::TestRegisterFactory(void) 337 { 338 #if !UCONFIG_NO_SERVICE 339 int32_t n1, n2, n3; 340 Locale fu_FU("fu", "FU", ""); 341 Locale fu_FU_FOO("fu", "FU", "FOO"); 342 343 UErrorCode status = U_ZERO_ERROR; 344 345 Hashtable* fuFUNames = new Hashtable(FALSE, status); 346 if (!fuFUNames) { 347 errln("memory allocation error"); 348 return; 349 } 350 fuFUNames->setValueDeleter(uhash_deleteUnicodeString); 351 352 fuFUNames->put(fu_FU.getName(), new UnicodeString("ze leetle bunny Fu-Fu"), status); 353 fuFUNames->put(fu_FU_FOO.getName(), new UnicodeString("zee leetel bunny Foo-Foo"), status); 354 fuFUNames->put(Locale::getDefault().getName(), new UnicodeString("little bunny Foo Foo"), status); 355 356 Collator* frcol = Collator::createInstance(Locale::getFrance(), status); 357 Collator* gecol = Collator::createInstance(Locale::getGermany(), status); 358 Collator* jpcol = Collator::createInstance(Locale::getJapan(), status); 359 if(U_FAILURE(status)) { 360 errcheckln(status, "Failed to create collators with %s", u_errorName(status)); 361 delete frcol; 362 delete gecol; 363 delete jpcol; 364 delete fuFUNames; 365 return; 366 } 367 368 CollatorInfo** info = new CollatorInfo*[4]; 369 if (!info) { 370 errln("memory allocation error"); 371 return; 372 } 373 374 info[0] = new CollatorInfo(Locale::getUS(), frcol, NULL); 375 info[1] = new CollatorInfo(Locale::getFrance(), gecol, NULL); 376 info[2] = new CollatorInfo(fu_FU, jpcol, fuFUNames); 377 info[3] = NULL; 378 379 TestFactory* factory = new TestFactory(info); 380 if (!factory) { 381 errln("memory allocation error"); 382 return; 383 } 384 385 Collator* uscol = Collator::createInstance(Locale::getUS(), status); 386 Collator* fucol = Collator::createInstance(fu_FU, status); 387 388 { 389 n1 = checkAvailable("before registerFactory"); 390 391 URegistryKey key = Collator::registerFactory(factory, status); 392 393 n2 = checkAvailable("after registerFactory"); 394 assertTrue("count after > count before", n2 > n1); 395 396 Collator* ncol = Collator::createInstance(Locale::getUS(), status); 397 if (*frcol != *ncol) { 398 errln("frcoll for en_US failed"); 399 } 400 delete ncol; ncol = NULL; 401 402 ncol = Collator::createInstance(fu_FU_FOO, status); 403 if (*jpcol != *ncol) { 404 errln("jpcol for fu_FU_FOO failed"); 405 } 406 407 Locale loc = ncol->getLocale(ULOC_REQUESTED_LOCALE, status); 408 if (loc != fu_FU_FOO) { 409 errln(UnicodeString("requested locale for fu_FU_FOO is not fu_FU_FOO but ") + loc.getName()); 410 } 411 loc = ncol->getLocale(ULOC_VALID_LOCALE, status); 412 if (loc != fu_FU) { 413 errln(UnicodeString("valid locale for fu_FU_FOO is not fu_FU but ") + loc.getName()); 414 } 415 delete ncol; ncol = NULL; 416 417 UnicodeString locName = fu_FU.getName(); 418 StringEnumeration* localeEnum = Collator::getAvailableLocales(); 419 UBool found = FALSE; 420 const UnicodeString* locStr; 421 for (locStr = localeEnum->snext(status); 422 !found && locStr != NULL; 423 locStr = localeEnum->snext(status)) 424 { 425 if (locName == *locStr) { 426 found = TRUE; 427 } 428 } 429 delete localeEnum; 430 431 if (!found) { 432 errln("new locale fu_FU not reported as supported locale"); 433 } 434 435 UnicodeString name; 436 Collator::getDisplayName(fu_FU, name); 437 if (name != "little bunny Foo Foo") { 438 errln(UnicodeString("found ") + name + " for fu_FU"); 439 } 440 441 Collator::getDisplayName(fu_FU, fu_FU_FOO, name); 442 if (name != "zee leetel bunny Foo-Foo") { 443 errln(UnicodeString("found ") + name + " for fu_FU in fu_FU_FOO"); 444 } 445 446 if (!Collator::unregister(key, status)) { 447 errln("failed to unregister factory"); 448 } 449 // ja, fr, ge collators no longer valid 450 451 ncol = Collator::createInstance(fu_FU, status); 452 if (*fucol != *ncol) { 453 errln("collator after unregister does not match original fu_FU"); 454 } 455 delete ncol; 456 457 n3 = checkAvailable("after unregister"); 458 assertTrue("count after unregister == count before register", n3 == n1); 459 } 460 461 delete fucol; 462 delete uscol; 463 #endif 464 } 465 466 /** 467 * Iterate through the given iterator, checking to see that all the strings 468 * in the expected array are present. 469 * @param expected array of strings we expect to see, or NULL 470 * @param expectedCount number of elements of expected, or 0 471 */ 472 int32_t CollationServiceTest::checkStringEnumeration(const char* msg, 473 StringEnumeration& iter, 474 const char** expected, 475 int32_t expectedCount) { 476 UErrorCode ec = U_ZERO_ERROR; 477 U_ASSERT(expectedCount >= 0 && expectedCount < 31); // [sic] 31 not 32 478 int32_t i = 0, idxAfterReset = 0, n = iter.count(ec); 479 assertSuccess("count", ec); 480 UnicodeString buf, buffAfterReset; 481 int32_t seenMask = 0; 482 for (;; ++i) { 483 const UnicodeString* s = iter.snext(ec); 484 if (!assertSuccess("snext", ec) || s == NULL) 485 break; 486 if (i != 0) 487 buf.append(UNICODE_STRING_SIMPLE(", ")); 488 buf.append(*s); 489 // check expected list 490 for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) { 491 if ((seenMask&bit)==0) { 492 UnicodeString exp(expected[j], (char*)NULL); 493 if (*s == exp) { 494 seenMask |= bit; 495 logln((UnicodeString)"Ok: \"" + exp + "\" seen"); 496 } 497 } 498 } 499 } 500 // can't get pesky operator+(const US&, foo) to cooperate; use toString 501 #if !UCONFIG_NO_FORMATTING 502 logln(UnicodeString() + msg + " = [" + buf + "] (" + toString(i) + ")"); 503 #else 504 logln(UnicodeString() + msg + " = [" + buf + "] (??? NO_FORMATTING)"); 505 #endif 506 assertTrue("count verified", i==n); 507 iter.reset(ec); 508 for (;; ++idxAfterReset) { 509 const UChar *s = iter.unext(NULL, ec); 510 if (!assertSuccess("unext", ec) || s == NULL) 511 break; 512 if (idxAfterReset != 0) 513 buffAfterReset.append(UNICODE_STRING_SIMPLE(", ")); 514 buffAfterReset.append(s); 515 } 516 assertTrue("idxAfterReset verified", idxAfterReset==n); 517 assertTrue("buffAfterReset verified", buffAfterReset==buf); 518 // did we see all expected strings? 519 if (((1<<expectedCount)-1) != seenMask) { 520 for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) { 521 if ((seenMask&bit)==0) { 522 errln((UnicodeString)"FAIL: \"" + expected[j] + "\" not seen"); 523 } 524 } 525 } 526 return n; 527 } 528 529 /** 530 * Check the integrity of the results of Collator::getAvailableLocales(). 531 * Return the number of items returned. 532 */ 533 #if !UCONFIG_NO_SERVICE 534 int32_t CollationServiceTest::checkAvailable(const char* msg) { 535 StringEnumeration *iter = Collator::getAvailableLocales(); 536 if (!assertTrue("getAvailableLocales != NULL", iter!=NULL)) return -1; 537 int32_t n = checkStringEnumeration(msg, *iter, NULL, 0); 538 delete iter; 539 return n; 540 } 541 #endif 542 543 static const char* KW[] = { 544 "collation" 545 }; 546 static const int32_t KW_COUNT = sizeof(KW)/sizeof(KW[0]); 547 548 static const char* KWVAL[] = { 549 "phonebook", 550 "stroke" 551 }; 552 static const int32_t KWVAL_COUNT = sizeof(KWVAL)/sizeof(KWVAL[0]); 553 554 void CollationServiceTest::TestSeparateTree() { 555 UErrorCode ec = U_ZERO_ERROR; 556 StringEnumeration *iter = Collator::getKeywords(ec); 557 if (!assertTrue("getKeywords != NULL", iter!=NULL)) return; 558 if (!assertSuccess("getKeywords", ec)) return; 559 checkStringEnumeration("getKeywords", *iter, KW, KW_COUNT); 560 delete iter; 561 562 iter = Collator::getKeywordValues(KW[0], ec); 563 if (!assertTrue("getKeywordValues != NULL", iter!=NULL, FALSE, TRUE)) return; 564 if (!assertSuccess("getKeywordValues", ec)) return; 565 checkStringEnumeration("getKeywordValues", *iter, KWVAL, KWVAL_COUNT); 566 delete iter; 567 568 UBool isAvailable; 569 Locale equiv = Collator::getFunctionalEquivalent("collation", 570 Locale::createFromName("fr"), 571 isAvailable, ec); 572 assertSuccess("getFunctionalEquivalent", ec); 573 assertEquals("getFunctionalEquivalent(fr)", "fr", equiv.getName()); 574 assertTrue("getFunctionalEquivalent(fr).isAvailable==TRUE", 575 isAvailable == TRUE); 576 577 equiv = Collator::getFunctionalEquivalent("collation", 578 Locale::createFromName("fr_FR"), 579 isAvailable, ec); 580 assertSuccess("getFunctionalEquivalent", ec); 581 assertEquals("getFunctionalEquivalent(fr_FR)", "fr", equiv.getName()); 582 assertTrue("getFunctionalEquivalent(fr_FR).isAvailable==TRUE", 583 isAvailable == TRUE); 584 } 585 586 #endif 587