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