1/*
2 *******************************************************************************
3 * Copyright (C) 2003-2013, 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
21void 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
32void 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    LocalUCollatorPointer 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        /* The locale display pattern for the locale ja, ko, and zh are different. */
156        const UChar zh_fuFU_Array[] = { 0x0066, 0x0075, 0xff08, 0x0046, 0x0055, 0xff09, 0 };
157        const UnicodeString zh_fuFU(zh_fuFU_Array);
158        const Locale& defaultLocale = Locale::getDefault();
159        if (displayName != "fu (FU)" &&
160           ((defaultLocale == Locale::getKorean() && defaultLocale == Locale::getJapanese()) && displayName == "fu(FU)") &&
161           ((defaultLocale == Locale::getChinese()) && displayName != zh_fuFU)) {
162            errln(UnicodeString("found ") + displayName + " for fu_FU");
163        }
164
165        Collator::getDisplayName(fu_FU, fu_FU, displayName);
166        if (displayName != "fu (FU)" &&
167           ((defaultLocale == Locale::getKorean() && defaultLocale == Locale::getJapanese()) && displayName == "fu(FU)") &&
168           ((defaultLocale == Locale::getChinese()) && displayName != zh_fuFU)) {
169            errln(UnicodeString("found ") + displayName + " for fu_FU");
170        }
171
172        // test ucol_open
173        LocalUCollatorPointer fufu(ucol_open("fu_FU_FOO", &status));
174        if (fufu.isNull()) {
175            errln("could not open fu_FU_FOO with ucol_open");
176        } else {
177            if (!ucol_equals(fufu.getAlias(), frFR.getAlias())) {
178                errln("collator fufu != collator frFR");
179            }
180        }
181
182        if (!Collator::unregister(key, status)) {
183            errln("failed to unregister french collator");
184        }
185        // !!! note frcoll invalid again, but we're no longer using it
186
187        // other collators should still work ok
188        Locale nloc = ncol->getLocale(ULOC_VALID_LOCALE, status);
189        if (nloc != fu_FU) {
190            errln(UnicodeString("asked for nloc valid locale after close and got") + nloc.getName());
191        }
192        delete ncol; ncol = NULL;
193
194        if (fufu.isValid()) {
195            const char* nlocstr = ucol_getLocaleByType(fufu.getAlias(), ULOC_VALID_LOCALE, &status);
196            if (uprv_strcmp(nlocstr, "fu_FU") != 0) {
197                errln(UnicodeString("asked for uloc valid locale after close and got ") + nlocstr);
198            }
199        }
200
201        ncol = Collator::createInstance(fu_FU, status);
202        if (*fucol != *ncol) {
203            errln("collator after unregister does not match original fu_FU");
204        }
205        delete uscol; uscol = NULL;
206        delete ncol; ncol = NULL;
207        delete fucol; fucol = NULL;
208    }
209#endif
210}
211
212// ------------------
213
214#if !UCONFIG_NO_SERVICE
215struct CollatorInfo {
216  Locale locale;
217  Collator* collator;
218  Hashtable* displayNames; // locale name -> string
219
220  CollatorInfo(const Locale& locale, Collator* collatorToAdopt, Hashtable* displayNamesToAdopt);
221  ~CollatorInfo();
222  UnicodeString& getDisplayName(const Locale& displayLocale, UnicodeString& name) const;
223};
224
225CollatorInfo::CollatorInfo(const Locale& _locale, Collator* _collator, Hashtable* _displayNames)
226  : locale(_locale)
227  , collator(_collator)
228  , displayNames(_displayNames)
229{
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        Locale loc = ncol->getLocale(ULOC_REQUESTED_LOCALE, status);
414        if (loc != fu_FU_FOO) {
415            errln(UnicodeString("requested locale for fu_FU_FOO is not fu_FU_FOO but ") + loc.getName());
416        }
417        loc = ncol->getLocale(ULOC_VALID_LOCALE, status);
418        if (loc != fu_FU) {
419            errln(UnicodeString("valid locale for fu_FU_FOO is not fu_FU but ") + loc.getName());
420        }
421        delete ncol; ncol = NULL;
422
423        UnicodeString locName = fu_FU.getName();
424        StringEnumeration* localeEnum = Collator::getAvailableLocales();
425        UBool found = FALSE;
426        const UnicodeString* locStr;
427        for (locStr = localeEnum->snext(status);
428            !found && locStr != NULL;
429            locStr = localeEnum->snext(status))
430        {
431            if (locName == *locStr) {
432                found = TRUE;
433            }
434        }
435        delete localeEnum;
436
437        if (!found) {
438            errln("new locale fu_FU not reported as supported locale");
439        }
440
441        UnicodeString name;
442        Collator::getDisplayName(fu_FU, name);
443        if (name != "little bunny Foo Foo") {
444            errln(UnicodeString("found ") + name + " for fu_FU");
445        }
446
447        Collator::getDisplayName(fu_FU, fu_FU_FOO, name);
448        if (name != "zee leetel bunny Foo-Foo") {
449            errln(UnicodeString("found ") + name + " for fu_FU in fu_FU_FOO");
450        }
451
452        if (!Collator::unregister(key, status)) {
453            errln("failed to unregister factory");
454        }
455        // ja, fr, ge collators no longer valid
456
457        ncol = Collator::createInstance(fu_FU, status);
458        if (*fucol != *ncol) {
459            errln("collator after unregister does not match original fu_FU");
460        }
461        delete ncol;
462
463        n3 = checkAvailable("after unregister");
464        assertTrue("count after unregister == count before register", n3 == n1);
465    }
466
467    delete fucol;
468    delete uscol;
469#endif
470}
471
472/**
473 * Iterate through the given iterator, checking to see that all the strings
474 * in the expected array are present.
475 * @param expected array of strings we expect to see, or NULL
476 * @param expectedCount number of elements of expected, or 0
477 */
478int32_t CollationServiceTest::checkStringEnumeration(const char* msg,
479                                                     StringEnumeration& iter,
480                                                     const char** expected,
481                                                     int32_t expectedCount) {
482    UErrorCode ec = U_ZERO_ERROR;
483    U_ASSERT(expectedCount >= 0 && expectedCount < 31); // [sic] 31 not 32
484    int32_t i = 0, idxAfterReset = 0, n = iter.count(ec);
485    assertSuccess("count", ec);
486    UnicodeString buf, buffAfterReset;
487    int32_t seenMask = 0;
488    for (;; ++i) {
489        const UnicodeString* s = iter.snext(ec);
490        if (!assertSuccess("snext", ec) || s == NULL)
491            break;
492        if (i != 0)
493            buf.append(UNICODE_STRING_SIMPLE(", "));
494        buf.append(*s);
495        // check expected list
496        for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) {
497            if ((seenMask&bit)==0) {
498                UnicodeString exp(expected[j], (char*)NULL);
499                if (*s == exp) {
500                    seenMask |= bit;
501                    logln((UnicodeString)"Ok: \"" + exp + "\" seen");
502                }
503            }
504        }
505    }
506    // can't get pesky operator+(const US&, foo) to cooperate; use toString
507#if !UCONFIG_NO_FORMATTING
508    logln(UnicodeString() + msg + " = [" + buf + "] (" + toString(i) + ")");
509#else
510    logln(UnicodeString() + msg + " = [" + buf + "] (??? NO_FORMATTING)");
511#endif
512    assertTrue("count verified", i==n);
513    iter.reset(ec);
514    for (;; ++idxAfterReset) {
515        const UChar *s = iter.unext(NULL, ec);
516        if (!assertSuccess("unext", ec) || s == NULL)
517            break;
518        if (idxAfterReset != 0)
519            buffAfterReset.append(UNICODE_STRING_SIMPLE(", "));
520        buffAfterReset.append(s);
521    }
522    assertTrue("idxAfterReset verified", idxAfterReset==n);
523    assertTrue("buffAfterReset verified", buffAfterReset==buf);
524    // did we see all expected strings?
525    if (((1<<expectedCount)-1) != seenMask) {
526        for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) {
527            if ((seenMask&bit)==0) {
528                errln((UnicodeString)"FAIL: \"" + expected[j] + "\" not seen");
529            }
530        }
531    }
532    return n;
533}
534
535/**
536 * Check the integrity of the results of Collator::getAvailableLocales().
537 * Return the number of items returned.
538 */
539#if !UCONFIG_NO_SERVICE
540int32_t CollationServiceTest::checkAvailable(const char* msg) {
541    StringEnumeration *iter = Collator::getAvailableLocales();
542    if (!assertTrue("getAvailableLocales != NULL", iter!=NULL)) return -1;
543    int32_t n = checkStringEnumeration(msg, *iter, NULL, 0);
544    delete iter;
545    return n;
546}
547#endif
548
549static const char* KW[] = {
550    "collation"
551};
552static const int32_t KW_COUNT = sizeof(KW)/sizeof(KW[0]);
553
554static const char* KWVAL[] = {
555    "phonebook",
556    "stroke"
557};
558static const int32_t KWVAL_COUNT = sizeof(KWVAL)/sizeof(KWVAL[0]);
559
560void CollationServiceTest::TestSeparateTree() {
561    UErrorCode ec = U_ZERO_ERROR;
562    StringEnumeration *iter = Collator::getKeywords(ec);
563    if (!assertTrue("getKeywords != NULL", iter!=NULL)) return;
564    if (!assertSuccess("getKeywords", ec)) return;
565    checkStringEnumeration("getKeywords", *iter, KW, KW_COUNT);
566    delete iter;
567
568    iter = Collator::getKeywordValues(KW[0], ec);
569    if (!assertTrue("getKeywordValues != NULL", iter!=NULL, FALSE, TRUE)) return;
570    if (!assertSuccess("getKeywordValues", ec)) return;
571    checkStringEnumeration("getKeywordValues", *iter, KWVAL, KWVAL_COUNT);
572    delete iter;
573
574    UBool isAvailable;
575    Locale equiv = Collator::getFunctionalEquivalent("collation",
576                                                     Locale::createFromName("de"),
577                                                     isAvailable, ec);
578    assertSuccess("getFunctionalEquivalent", ec);
579    assertEquals("getFunctionalEquivalent(de)", "root", equiv.getName());
580    assertTrue("getFunctionalEquivalent(de).isAvailable==TRUE",
581               isAvailable == TRUE);
582
583    equiv = Collator::getFunctionalEquivalent("collation",
584                                              Locale::createFromName("de_DE"),
585                                              isAvailable, ec);
586    assertSuccess("getFunctionalEquivalent", ec);
587    assertEquals("getFunctionalEquivalent(de_DE)", "root", equiv.getName());
588    assertTrue("getFunctionalEquivalent(de_DE).isAvailable==TRUE",
589               isAvailable == TRUE);
590
591    equiv = Collator::getFunctionalEquivalent("collation",
592                                                     Locale::createFromName("sv"),
593                                                     isAvailable, ec);
594    assertSuccess("getFunctionalEquivalent", ec);
595    assertEquals("getFunctionalEquivalent(sv)", "sv", equiv.getName());
596    assertTrue("getFunctionalEquivalent(sv).isAvailable==TRUE",
597               isAvailable == TRUE);
598
599    equiv = Collator::getFunctionalEquivalent("collation",
600                                              Locale::createFromName("sv_SE"),
601                                              isAvailable, ec);
602    assertSuccess("getFunctionalEquivalent", ec);
603    assertEquals("getFunctionalEquivalent(sv_SE)", "sv", equiv.getName());
604    assertTrue("getFunctionalEquivalent(sv_SE).isAvailable==TRUE",
605               isAvailable == TRUE);
606}
607
608#endif
609