1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2003-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_COLLATION
15 #include "unicode/coll.h"
16 #include "unicode/strenum.h"
21 #include "cstring.h" // internal api used to compare locale strings
23 void CollationServiceTest::runIndexedTest(int32_t index
, UBool exec
, const char* &name
, char* /*par */)
25 if (exec
) logln("TestSuite CollationServiceTest: ");
27 TESTCASE(0, TestRegister
);
28 TESTCASE(1, TestRegisterFactory
);
29 TESTCASE(2, TestSeparateTree
);
30 default: name
= ""; break;
34 void CollationServiceTest::TestRegister()
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");
42 UErrorCode status
= U_ZERO_ERROR
;
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
));
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.
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");
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());
71 status
= U_ZERO_ERROR
;
72 loc
= ncol
->getLocale(ULOC_VALID_LOCALE
, status
);
74 errln(UnicodeString("valid locale for en_US_FOO is not en_US but ") + loc
.getName());
76 loc
= ncol
->getLocale(ULOC_ACTUAL_LOCALE
, status
);
78 errln(UnicodeString("actual locale for en_US_FOO is not en_US but ") + loc
.getName());
80 delete ncol
; ncol
= NULL
;
82 if (!Collator::unregister(key
, status
)) {
83 errln("failed to unregister french collator");
86 ncol
= Collator::createInstance(US
, status
);
87 if (*uscol
!= *ncol
) {
88 errln("collator after unregister does not match original");
90 delete ncol
; ncol
= NULL
;
94 frcol
= Collator::createInstance(FR
, status
);
96 LocalUCollatorPointer
frFR(ucol_open("fr_FR", &status
));
98 { // try create collator for new locale
99 Locale
fu_FU_FOO("fu", "FU", "FOO");
100 Locale
fu_FU("fu", "FU", "");
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");
112 UnicodeString locName
= fu_FU
.getName();
113 StringEnumeration
* localeEnum
= Collator::getAvailableLocales();
115 const UnicodeString
* locStr
, *ls2
;
116 for (locStr
= localeEnum
->snext(status
);
117 !found
&& locStr
!= NULL
;
118 locStr
= localeEnum
->snext(status
)) {
120 if (locName
== *locStr
) {
125 StringEnumeration
*le2
= NULL
;
126 localeEnum
->reset(status
);
128 count
= localeEnum
->count(status
);
129 for(i
= 0; i
< count
; ++i
) {
131 le2
= localeEnum
->clone();
132 if(le2
== NULL
|| count
!= le2
->count(status
)) {
133 errln("ServiceEnumeration.clone() failed");
138 locStr
= localeEnum
->snext(status
);
139 ls2
= le2
->snext(status
);
140 if(*locStr
!= *ls2
) {
141 errln("ServiceEnumeration.clone() failed for item %d", i
);
144 localeEnum
->snext(status
);
152 errln("new locale fu_FU not reported as supported locale");
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");
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");
175 LocalUCollatorPointer
fufu(ucol_open("fu_FU_FOO", &status
));
177 errln("could not open fu_FU_FOO with ucol_open");
179 if (*Collator::fromUCollator(fufu
.getAlias()) !=
180 *Collator::fromUCollator(frFR
.getAlias())) {
181 errln("collator fufu != collator frFR");
185 if (!Collator::unregister(key
, status
)) {
186 errln("failed to unregister french collator");
188 // !!! note frcoll invalid again, but we're no longer using it
190 // other collators should still work ok
191 Locale nloc
= ncol
->getLocale(ULOC_VALID_LOCALE
, status
);
193 errln(UnicodeString("asked for nloc valid locale after close and got") + nloc
.getName());
195 delete ncol
; ncol
= NULL
;
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
);
204 ncol
= Collator::createInstance(fu_FU
, status
);
205 if (*fucol
!= *ncol
) {
206 errln("collator after unregister does not match original fu_FU");
208 delete uscol
; uscol
= NULL
;
209 delete ncol
; ncol
= NULL
;
210 delete fucol
; fucol
= NULL
;
215 // ------------------
217 #if !UCONFIG_NO_SERVICE
218 struct CollatorInfo
{
221 Hashtable
* displayNames
; // locale name -> string
223 CollatorInfo(const Locale
& locale
, Collator
* collatorToAdopt
, Hashtable
* displayNamesToAdopt
);
225 UnicodeString
& getDisplayName(const Locale
& displayLocale
, UnicodeString
& name
) const;
228 CollatorInfo::CollatorInfo(const Locale
& _locale
, Collator
* _collator
, Hashtable
* _displayNames
)
230 , collator(_collator
)
231 , displayNames(_displayNames
)
233 collator
->setLocales(locale
, locale
, locale
);
236 CollatorInfo::~CollatorInfo() {
242 CollatorInfo::getDisplayName(const Locale
& displayLocale
, UnicodeString
& name
) const {
244 UnicodeString
* val
= (UnicodeString
*)displayNames
->get(displayLocale
.getName());
251 return locale
.getDisplayName(displayLocale
, name
);
256 class TestFactory
: public CollatorFactory
{
261 const CollatorInfo
* getInfo(const Locale
& loc
) const {
262 for (CollatorInfo
** p
= info
; *p
; ++p
) {
263 if (loc
== (**p
).locale
) {
271 TestFactory(CollatorInfo
** _info
)
277 for (p
= info
; *p
; ++p
) {}
278 count
= (int32_t)(p
- info
);
282 for (CollatorInfo
** p
= info
; *p
; ++p
) {
289 virtual Collator
* createCollator(const Locale
& loc
) {
290 const CollatorInfo
* ci
= getInfo(loc
);
292 return ci
->collator
->clone();
297 virtual UnicodeString
& getDisplayName(const Locale
& objectLocale
,
298 const Locale
& displayLocale
,
299 UnicodeString
& result
)
301 const CollatorInfo
* ci
= getInfo(objectLocale
);
303 ci
->getDisplayName(displayLocale
, result
);
310 const UnicodeString
* getSupportedIDs(int32_t& _count
, UErrorCode
& status
) {
311 if (U_SUCCESS(status
)) {
313 ids
= new UnicodeString
[count
];
315 status
= U_MEMORY_ALLOCATION_ERROR
;
320 for (int i
= 0; i
< count
; ++i
) {
321 ids
[i
] = info
[i
]->locale
.getName();
331 virtual inline UClassID
getDynamicClassID() const {
332 return (UClassID
)&gClassID
;
335 static UClassID
getStaticClassID() {
336 return (UClassID
)&gClassID
;
340 static char gClassID
;
343 char TestFactory::gClassID
= 0;
346 void CollationServiceTest::TestRegisterFactory(void)
348 #if !UCONFIG_NO_SERVICE
350 Locale
fu_FU("fu", "FU", "");
351 Locale
fu_FU_FOO("fu", "FU", "FOO");
353 UErrorCode status
= U_ZERO_ERROR
;
355 Hashtable
* fuFUNames
= new Hashtable(FALSE
, status
);
357 errln("memory allocation error");
360 fuFUNames
->setValueDeleter(uprv_deleteUObject
);
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
);
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
));
378 CollatorInfo
** info
= new CollatorInfo
*[4];
380 errln("memory allocation error");
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
);
389 TestFactory
* factory
= new TestFactory(info
);
391 errln("memory allocation error");
395 Collator
* uscol
= Collator::createInstance(Locale::getUS(), status
);
396 Collator
* fucol
= Collator::createInstance(fu_FU
, status
);
399 n1
= checkAvailable("before registerFactory");
401 URegistryKey key
= Collator::registerFactory(factory
, status
);
403 n2
= checkAvailable("after registerFactory");
404 assertTrue("count after > count before", n2
> n1
);
406 Collator
* ncol
= Collator::createInstance(Locale::getUS(), status
);
407 if (*frcol
!= *ncol
) {
408 errln("frcoll for en_US failed");
410 delete ncol
; ncol
= NULL
;
412 ncol
= Collator::createInstance(fu_FU_FOO
, status
);
413 if (*jpcol
!= *ncol
) {
414 errln("jpcol for fu_FU_FOO failed");
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());
423 status
= U_ZERO_ERROR
;
424 loc
= ncol
->getLocale(ULOC_VALID_LOCALE
, status
);
426 errln(UnicodeString("valid locale for fu_FU_FOO is not fu_FU but ") + loc
.getName());
428 delete ncol
; ncol
= NULL
;
430 UnicodeString locName
= fu_FU
.getName();
431 StringEnumeration
* localeEnum
= Collator::getAvailableLocales();
433 const UnicodeString
* locStr
;
434 for (locStr
= localeEnum
->snext(status
);
435 !found
&& locStr
!= NULL
;
436 locStr
= localeEnum
->snext(status
))
438 if (locName
== *locStr
) {
445 errln("new locale fu_FU not reported as supported locale");
449 Collator::getDisplayName(fu_FU
, name
);
450 if (name
!= "little bunny Foo Foo") {
451 errln(UnicodeString("found ") + name
+ " for fu_FU");
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");
459 if (!Collator::unregister(key
, status
)) {
460 errln("failed to unregister factory");
462 // ja, fr, ge collators no longer valid
464 ncol
= Collator::createInstance(fu_FU
, status
);
465 if (*fucol
!= *ncol
) {
466 errln("collator after unregister does not match original fu_FU");
470 n3
= checkAvailable("after unregister");
471 assertTrue("count after unregister == count before register", n3
== n1
);
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
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;
496 const UnicodeString
* s
= iter
.snext(ec
);
497 if (!assertSuccess("snext", ec
) || s
== NULL
)
500 buf
.append(UNICODE_STRING_SIMPLE(", "));
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
);
508 logln((UnicodeString
)"Ok: \"" + exp
+ "\" seen");
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
) + ")");
517 logln(UnicodeString() + msg
+ " = [" + buf
+ "] (??? NO_FORMATTING)");
519 assertTrue("count verified", i
==n
);
521 for (;; ++idxAfterReset
) {
522 const UChar
*s
= iter
.unext(NULL
, ec
);
523 if (!assertSuccess("unext", ec
) || s
== NULL
)
525 if (idxAfterReset
!= 0)
526 buffAfterReset
.append(UNICODE_STRING_SIMPLE(", "));
527 buffAfterReset
.append(s
);
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");
543 * Check the integrity of the results of Collator::getAvailableLocales().
544 * Return the number of items returned.
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);
556 static const char* KW
[] = {
559 static const int32_t KW_COUNT
= UPRV_LENGTHOF(KW
);
561 static const char* KWVAL
[] = {
565 static const int32_t KWVAL_COUNT
= UPRV_LENGTHOF(KWVAL
);
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
);
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
);
582 Locale equiv
= Collator::getFunctionalEquivalent("collation",
583 Locale::createFromName("de"),
585 assertSuccess("getFunctionalEquivalent", ec
);
586 assertEquals("getFunctionalEquivalent(de)", "root", equiv
.getName());
587 assertTrue("getFunctionalEquivalent(de).isAvailable==TRUE",
588 isAvailable
== TRUE
);
590 equiv
= Collator::getFunctionalEquivalent("collation",
591 Locale::createFromName("de_DE"),
593 assertSuccess("getFunctionalEquivalent", ec
);
594 assertEquals("getFunctionalEquivalent(de_DE)", "root", equiv
.getName());
595 assertTrue("getFunctionalEquivalent(de_DE).isAvailable==FALSE",
596 isAvailable
== FALSE
);
598 equiv
= Collator::getFunctionalEquivalent("collation",
599 Locale::createFromName("sv"),
601 assertSuccess("getFunctionalEquivalent", ec
);
602 assertEquals("getFunctionalEquivalent(sv)", "sv", equiv
.getName());
603 assertTrue("getFunctionalEquivalent(sv).isAvailable==TRUE",
604 isAvailable
== TRUE
);
606 equiv
= Collator::getFunctionalEquivalent("collation",
607 Locale::createFromName("sv_SE"),
609 assertSuccess("getFunctionalEquivalent", ec
);
610 assertEquals("getFunctionalEquivalent(sv_SE)", "sv", equiv
.getName());
611 assertTrue("getFunctionalEquivalent(sv_SE).isAvailable==FALSE",
612 isAvailable
== FALSE
);