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