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