1/*
2 *******************************************************************************
3 * Copyright (C) 2003-2009, 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    UCollator* 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        if (displayName != "fu (FU)") {
156            errln(UnicodeString("found ") + displayName + " for fu_FU");
157        }
158
159        Collator::getDisplayName(fu_FU, fu_FU, displayName);
160        if (displayName != "fu (FU)") {
161            errln(UnicodeString("found ") + displayName + " for fu_FU");
162        }
163
164        // test ucol_open
165        UCollator* fufu = ucol_open("fu_FU_FOO", &status);
166        if (!fufu) {
167            errln("could not open fu_FU_FOO with ucol_open");
168        } else {
169            if (!ucol_equals(fufu, frFR)) {
170                errln("collator fufu != collator frFR");
171            }
172        }
173
174        if (!Collator::unregister(key, status)) {
175            errln("failed to unregister french collator");
176        }
177        // !!! note frcoll invalid again, but we're no longer using it
178
179        // other collators should still work ok
180        Locale nloc = ncol->getLocale(ULOC_VALID_LOCALE, status);
181        if (nloc != fu_FU) {
182            errln(UnicodeString("asked for nloc valid locale after close and got") + nloc.getName());
183        }
184        delete ncol; ncol = NULL;
185
186        if (fufu) {
187            const char* nlocstr = ucol_getLocaleByType(fufu, ULOC_VALID_LOCALE, &status);
188            if (uprv_strcmp(nlocstr, "fu_FU") != 0) {
189                errln(UnicodeString("asked for uloc valid locale after close and got ") + nlocstr);
190            }
191            ucol_close(fufu);
192        }
193        ucol_close(frFR);
194
195        ncol = Collator::createInstance(fu_FU, status);
196        if (*fucol != *ncol) {
197            errln("collator after unregister does not match original fu_FU");
198        }
199        delete uscol; uscol = NULL;
200        delete ncol; ncol = NULL;
201        delete fucol; fucol = NULL;
202    }
203#endif
204}
205
206// ------------------
207
208#if !UCONFIG_NO_SERVICE
209struct CollatorInfo {
210  Locale locale;
211  Collator* collator;
212  Hashtable* displayNames; // locale name -> string
213
214  CollatorInfo(const Locale& locale, Collator* collatorToAdopt, Hashtable* displayNamesToAdopt);
215  ~CollatorInfo();
216  UnicodeString& getDisplayName(const Locale& displayLocale, UnicodeString& name) const;
217};
218
219CollatorInfo::CollatorInfo(const Locale& _locale, Collator* _collator, Hashtable* _displayNames)
220  : locale(_locale)
221  , collator(_collator)
222  , displayNames(_displayNames)
223{
224}
225
226CollatorInfo::~CollatorInfo() {
227  delete collator;
228  delete displayNames;
229}
230
231UnicodeString&
232CollatorInfo::getDisplayName(const Locale& displayLocale, UnicodeString& name) const {
233  if (displayNames) {
234    UnicodeString* val = (UnicodeString*)displayNames->get(displayLocale.getName());
235    if (val) {
236      name = *val;
237      return name;
238    }
239  }
240
241  return locale.getDisplayName(displayLocale, name);
242}
243
244// ---------------
245
246class TestFactory : public CollatorFactory {
247  CollatorInfo** info;
248  int32_t count;
249  UnicodeString* ids;
250
251  const CollatorInfo* getInfo(const Locale& loc) const {
252    for (CollatorInfo** p = info; *p; ++p) {
253      if (loc == (**p).locale) {
254        return *p;
255      }
256    }
257    return NULL;
258  }
259
260public:
261  TestFactory(CollatorInfo** _info)
262    : info(_info)
263    , count(0)
264    , ids(NULL)
265  {
266    CollatorInfo** p;
267    for (p = info; *p; ++p) {}
268    count = (int32_t)(p - info);
269  }
270
271  ~TestFactory() {
272    for (CollatorInfo** p = info; *p; ++p) {
273      delete *p;
274    }
275    delete[] info;
276    delete[] ids;
277  }
278
279  virtual Collator* createCollator(const Locale& loc) {
280    const CollatorInfo* ci = getInfo(loc);
281    if (ci) {
282      return ci->collator->clone();
283    }
284    return NULL;
285  }
286
287  virtual UnicodeString& getDisplayName(const Locale& objectLocale,
288                                        const Locale& displayLocale,
289                                        UnicodeString& result)
290  {
291    const CollatorInfo* ci = getInfo(objectLocale);
292    if (ci) {
293      ci->getDisplayName(displayLocale, result);
294    } else {
295      result.setToBogus();
296    }
297    return result;
298  }
299
300  const UnicodeString* getSupportedIDs(int32_t& _count, UErrorCode& status) {
301    if (U_SUCCESS(status)) {
302      if (!ids) {
303        ids = new UnicodeString[count];
304        if (!ids) {
305          status = U_MEMORY_ALLOCATION_ERROR;
306          _count = 0;
307          return NULL;
308        }
309
310        for (int i = 0; i < count; ++i) {
311          ids[i] = info[i]->locale.getName();
312        }
313      }
314
315      _count = count;
316      return ids;
317    }
318    return NULL;
319  }
320
321  virtual inline UClassID getDynamicClassID() const {
322    return (UClassID)&gClassID;
323  }
324
325  static UClassID getStaticClassID() {
326    return (UClassID)&gClassID;
327  }
328
329private:
330  static char gClassID;
331};
332
333char TestFactory::gClassID = 0;
334#endif
335
336void CollationServiceTest::TestRegisterFactory(void)
337{
338#if !UCONFIG_NO_SERVICE
339    int32_t n1, n2, n3;
340    Locale fu_FU("fu", "FU", "");
341    Locale fu_FU_FOO("fu", "FU", "FOO");
342
343    UErrorCode status = U_ZERO_ERROR;
344
345    Hashtable* fuFUNames = new Hashtable(FALSE, status);
346    if (!fuFUNames) {
347        errln("memory allocation error");
348        return;
349    }
350    fuFUNames->setValueDeleter(uhash_deleteUnicodeString);
351
352    fuFUNames->put(fu_FU.getName(), new UnicodeString("ze leetle bunny Fu-Fu"), status);
353    fuFUNames->put(fu_FU_FOO.getName(), new UnicodeString("zee leetel bunny Foo-Foo"), status);
354    fuFUNames->put(Locale::getDefault().getName(), new UnicodeString("little bunny Foo Foo"), status);
355
356    Collator* frcol = Collator::createInstance(Locale::getFrance(), status);
357    Collator* gecol = Collator::createInstance(Locale::getGermany(), status);
358    Collator* jpcol = Collator::createInstance(Locale::getJapan(), status);
359    if(U_FAILURE(status)) {
360      errcheckln(status, "Failed to create collators with %s", u_errorName(status));
361      delete frcol;
362      delete gecol;
363      delete jpcol;
364      delete fuFUNames;
365      return;
366    }
367
368    CollatorInfo** info = new CollatorInfo*[4];
369    if (!info) {
370        errln("memory allocation error");
371        return;
372    }
373
374    info[0] = new CollatorInfo(Locale::getUS(), frcol, NULL);
375    info[1] = new CollatorInfo(Locale::getFrance(), gecol, NULL);
376    info[2] = new CollatorInfo(fu_FU, jpcol, fuFUNames);
377    info[3] = NULL;
378
379    TestFactory* factory = new TestFactory(info);
380    if (!factory) {
381        errln("memory allocation error");
382        return;
383    }
384
385    Collator* uscol = Collator::createInstance(Locale::getUS(), status);
386    Collator* fucol = Collator::createInstance(fu_FU, status);
387
388    {
389        n1 = checkAvailable("before registerFactory");
390
391        URegistryKey key = Collator::registerFactory(factory, status);
392
393        n2 = checkAvailable("after registerFactory");
394        assertTrue("count after > count before", n2 > n1);
395
396        Collator* ncol = Collator::createInstance(Locale::getUS(), status);
397        if (*frcol != *ncol) {
398            errln("frcoll for en_US failed");
399        }
400        delete ncol; ncol = NULL;
401
402        ncol = Collator::createInstance(fu_FU_FOO, status);
403        if (*jpcol != *ncol) {
404            errln("jpcol for fu_FU_FOO failed");
405        }
406
407        Locale loc = ncol->getLocale(ULOC_REQUESTED_LOCALE, status);
408        if (loc != fu_FU_FOO) {
409            errln(UnicodeString("requested locale for fu_FU_FOO is not fu_FU_FOO but ") + loc.getName());
410        }
411        loc = ncol->getLocale(ULOC_VALID_LOCALE, status);
412        if (loc != fu_FU) {
413            errln(UnicodeString("valid locale for fu_FU_FOO is not fu_FU but ") + loc.getName());
414        }
415        delete ncol; ncol = NULL;
416
417        UnicodeString locName = fu_FU.getName();
418        StringEnumeration* localeEnum = Collator::getAvailableLocales();
419        UBool found = FALSE;
420        const UnicodeString* locStr;
421        for (locStr = localeEnum->snext(status);
422            !found && locStr != NULL;
423            locStr = localeEnum->snext(status))
424        {
425            if (locName == *locStr) {
426                found = TRUE;
427            }
428        }
429        delete localeEnum;
430
431        if (!found) {
432            errln("new locale fu_FU not reported as supported locale");
433        }
434
435        UnicodeString name;
436        Collator::getDisplayName(fu_FU, name);
437        if (name != "little bunny Foo Foo") {
438            errln(UnicodeString("found ") + name + " for fu_FU");
439        }
440
441        Collator::getDisplayName(fu_FU, fu_FU_FOO, name);
442        if (name != "zee leetel bunny Foo-Foo") {
443            errln(UnicodeString("found ") + name + " for fu_FU in fu_FU_FOO");
444        }
445
446        if (!Collator::unregister(key, status)) {
447            errln("failed to unregister factory");
448        }
449        // ja, fr, ge collators no longer valid
450
451        ncol = Collator::createInstance(fu_FU, status);
452        if (*fucol != *ncol) {
453            errln("collator after unregister does not match original fu_FU");
454        }
455        delete ncol;
456
457        n3 = checkAvailable("after unregister");
458        assertTrue("count after unregister == count before register", n3 == n1);
459    }
460
461    delete fucol;
462    delete uscol;
463#endif
464}
465
466/**
467 * Iterate through the given iterator, checking to see that all the strings
468 * in the expected array are present.
469 * @param expected array of strings we expect to see, or NULL
470 * @param expectedCount number of elements of expected, or 0
471 */
472int32_t CollationServiceTest::checkStringEnumeration(const char* msg,
473                                                     StringEnumeration& iter,
474                                                     const char** expected,
475                                                     int32_t expectedCount) {
476    UErrorCode ec = U_ZERO_ERROR;
477    U_ASSERT(expectedCount >= 0 && expectedCount < 31); // [sic] 31 not 32
478    int32_t i = 0, idxAfterReset = 0, n = iter.count(ec);
479    assertSuccess("count", ec);
480    UnicodeString buf, buffAfterReset;
481    int32_t seenMask = 0;
482    for (;; ++i) {
483        const UnicodeString* s = iter.snext(ec);
484        if (!assertSuccess("snext", ec) || s == NULL)
485            break;
486        if (i != 0)
487            buf.append(UNICODE_STRING_SIMPLE(", "));
488        buf.append(*s);
489        // check expected list
490        for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) {
491            if ((seenMask&bit)==0) {
492                UnicodeString exp(expected[j], (char*)NULL);
493                if (*s == exp) {
494                    seenMask |= bit;
495                    logln((UnicodeString)"Ok: \"" + exp + "\" seen");
496                }
497            }
498        }
499    }
500    // can't get pesky operator+(const US&, foo) to cooperate; use toString
501#if !UCONFIG_NO_FORMATTING
502    logln(UnicodeString() + msg + " = [" + buf + "] (" + toString(i) + ")");
503#else
504    logln(UnicodeString() + msg + " = [" + buf + "] (??? NO_FORMATTING)");
505#endif
506    assertTrue("count verified", i==n);
507    iter.reset(ec);
508    for (;; ++idxAfterReset) {
509        const UChar *s = iter.unext(NULL, ec);
510        if (!assertSuccess("unext", ec) || s == NULL)
511            break;
512        if (idxAfterReset != 0)
513            buffAfterReset.append(UNICODE_STRING_SIMPLE(", "));
514        buffAfterReset.append(s);
515    }
516    assertTrue("idxAfterReset verified", idxAfterReset==n);
517    assertTrue("buffAfterReset verified", buffAfterReset==buf);
518    // did we see all expected strings?
519    if (((1<<expectedCount)-1) != seenMask) {
520        for (int32_t j=0, bit=1; j<expectedCount; ++j, bit<<=1) {
521            if ((seenMask&bit)==0) {
522                errln((UnicodeString)"FAIL: \"" + expected[j] + "\" not seen");
523            }
524        }
525    }
526    return n;
527}
528
529/**
530 * Check the integrity of the results of Collator::getAvailableLocales().
531 * Return the number of items returned.
532 */
533#if !UCONFIG_NO_SERVICE
534int32_t CollationServiceTest::checkAvailable(const char* msg) {
535    StringEnumeration *iter = Collator::getAvailableLocales();
536    if (!assertTrue("getAvailableLocales != NULL", iter!=NULL)) return -1;
537    int32_t n = checkStringEnumeration(msg, *iter, NULL, 0);
538    delete iter;
539    return n;
540}
541#endif
542
543static const char* KW[] = {
544    "collation"
545};
546static const int32_t KW_COUNT = sizeof(KW)/sizeof(KW[0]);
547
548static const char* KWVAL[] = {
549    "phonebook",
550    "stroke"
551};
552static const int32_t KWVAL_COUNT = sizeof(KWVAL)/sizeof(KWVAL[0]);
553
554void CollationServiceTest::TestSeparateTree() {
555    UErrorCode ec = U_ZERO_ERROR;
556    StringEnumeration *iter = Collator::getKeywords(ec);
557    if (!assertTrue("getKeywords != NULL", iter!=NULL)) return;
558    if (!assertSuccess("getKeywords", ec)) return;
559    checkStringEnumeration("getKeywords", *iter, KW, KW_COUNT);
560    delete iter;
561
562    iter = Collator::getKeywordValues(KW[0], ec);
563    if (!assertTrue("getKeywordValues != NULL", iter!=NULL, FALSE, TRUE)) return;
564    if (!assertSuccess("getKeywordValues", ec)) return;
565    checkStringEnumeration("getKeywordValues", *iter, KWVAL, KWVAL_COUNT);
566    delete iter;
567
568    UBool isAvailable;
569    Locale equiv = Collator::getFunctionalEquivalent("collation",
570                                                     Locale::createFromName("fr"),
571                                                     isAvailable, ec);
572    assertSuccess("getFunctionalEquivalent", ec);
573    assertEquals("getFunctionalEquivalent(fr)", "fr", equiv.getName());
574    assertTrue("getFunctionalEquivalent(fr).isAvailable==TRUE",
575               isAvailable == TRUE);
576
577    equiv = Collator::getFunctionalEquivalent("collation",
578                                              Locale::createFromName("fr_FR"),
579                                              isAvailable, ec);
580    assertSuccess("getFunctionalEquivalent", ec);
581    assertEquals("getFunctionalEquivalent(fr_FR)", "fr", equiv.getName());
582    assertTrue("getFunctionalEquivalent(fr_FR).isAvailable==TRUE",
583               isAvailable == TRUE);
584}
585
586#endif
587