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