2 *******************************************************************************
3 * Copyright (C) 2007-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************************
9 ********************************************************************************
12 #include "unicode/utypes.h"
14 #if !UCONFIG_NO_FORMATTING
16 #include <stdlib.h> // for strtod
18 #include "unicode/localpointer.h"
19 #include "unicode/plurrule.h"
21 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof(array[0]))
23 void setupResult(const int32_t testSource
[], char result
[], int32_t* max
);
24 UBool
checkEqual(const PluralRules
&test
, char *result
, int32_t max
);
25 UBool
testEquality(const PluralRules
&test
);
27 // This is an API test, not a unit test. It doesn't test very many cases, and doesn't
28 // try to test the full functionality. It just calls each function in the class and
29 // verifies that it works on a basic level.
31 void PluralRulesTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
33 if (exec
) logln("TestSuite PluralRulesAPI");
35 TESTCASE_AUTO(testAPI
);
36 TESTCASE_AUTO(testGetUniqueKeywordValue
);
37 TESTCASE_AUTO(testGetSamples
);
38 TESTCASE_AUTO(testWithin
);
39 TESTCASE_AUTO(testGetAllKeywordValues
);
40 TESTCASE_AUTO(testOrdinal
);
44 #define PLURAL_TEST_NUM 18
46 * Test various generic API methods of PluralRules for API coverage.
48 void PluralRulesTest::testAPI(/*char *par*/)
50 UnicodeString pluralTestData
[PLURAL_TEST_NUM
] = {
51 UNICODE_STRING_SIMPLE("a: n is 1"),
52 UNICODE_STRING_SIMPLE("a: n mod 10 is 2"),
53 UNICODE_STRING_SIMPLE("a: n is not 1"),
54 UNICODE_STRING_SIMPLE("a: n mod 3 is not 1"),
55 UNICODE_STRING_SIMPLE("a: n in 2..5"),
56 UNICODE_STRING_SIMPLE("a: n within 2..5"),
57 UNICODE_STRING_SIMPLE("a: n not in 2..5"),
58 UNICODE_STRING_SIMPLE("a: n not within 2..5"),
59 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..5"),
60 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..5"),
61 UNICODE_STRING_SIMPLE("a: n mod 10 is 2 and n is not 12"),
62 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..3 or n mod 10 is 5"),
63 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..3 or n mod 10 is 5"),
64 UNICODE_STRING_SIMPLE("a: n is 1 or n is 4 or n is 23"),
65 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n in 1..11"),
66 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n within 1..11"),
67 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 or n mod 5 is 1 and n is not 6"),
70 static const int32_t pluralTestResult
[PLURAL_TEST_NUM
][30] = {
79 {2,3,4,5,12,13,14,15,22,23,24,25,0},
80 {2,3,4,5,12,13,14,15,22,23,24,25,0},
82 {2,3,5,12,13,15,22,23,25,0},
83 {2,3,5,12,13,15,22,23,25,0},
87 {1,3,5,7,9,11,13,15,16,0},
89 UErrorCode status
= U_ZERO_ERROR
;
91 // ======= Test constructors
92 logln("Testing PluralRules constructors");
95 logln("\n start default locale test case ..\n");
97 PluralRules
defRule(status
);
98 LocalPointer
<PluralRules
> test(new PluralRules(status
));
99 LocalPointer
<PluralRules
> newEnPlural(test
->forLocale(Locale::getEnglish(), status
));
100 if(U_FAILURE(status
)) {
101 dataerrln("ERROR: Could not create PluralRules (default) - exitting");
105 // ======= Test clone, assignment operator && == operator.
106 LocalPointer
<PluralRules
> dupRule(defRule
.clone());
108 errln("ERROR: clone plural rules test failed!");
111 if ( *dupRule
!= defRule
) {
112 errln("ERROR: clone plural rules test failed!");
115 *dupRule
= *newEnPlural
;
117 if ( *dupRule
!= *newEnPlural
) {
118 errln("ERROR: clone plural rules test failed!");
122 // ======= Test empty plural rules
123 logln("Testing Simple PluralRules");
125 LocalPointer
<PluralRules
> empRule(test
->createRules(UNICODE_STRING_SIMPLE("a:n"), status
));
127 for (int32_t i
=0; i
<10; ++i
) {
128 key
= empRule
->select(i
);
129 if ( key
.charAt(0)!= 0x61 ) { // 'a'
130 errln("ERROR: empty plural rules test failed! - exitting");
134 // ======= Test simple plural rules
135 logln("Testing Simple PluralRules");
140 for (int32_t i
=0; i
<PLURAL_TEST_NUM
-1; ++i
) {
141 LocalPointer
<PluralRules
> newRules(test
->createRules(pluralTestData
[i
], status
));
142 setupResult(pluralTestResult
[i
], result
, &max
);
143 if ( !checkEqual(*newRules
, result
, max
) ) {
144 errln("ERROR: simple plural rules failed! - exitting");
149 // ======= Test complex plural rules
150 logln("Testing Complex PluralRules");
151 // TODO: the complex test data is hard coded. It's better to implement
152 // a parser to parse the test data.
153 UnicodeString complexRule
= UNICODE_STRING_SIMPLE("a: n in 2..5; b: n in 5..8; c: n mod 2 is 1");
154 UnicodeString complexRule2
= UNICODE_STRING_SIMPLE("a: n within 2..5; b: n within 5..8; c: n mod 2 is 1");
170 LocalPointer
<PluralRules
> newRules(test
->createRules(complexRule
, status
));
171 if ( !checkEqual(*newRules
, cRuleResult
, 12) ) {
172 errln("ERROR: complex plural rules failed! - exitting");
175 newRules
.adoptInstead(test
->createRules(complexRule2
, status
));
176 if ( !checkEqual(*newRules
, cRuleResult
, 12) ) {
177 errln("ERROR: complex plural rules failed! - exitting");
181 // ======= Test decimal fractions plural rules
182 UnicodeString decimalRule
= UNICODE_STRING_SIMPLE("a: n not in 0..100;");
183 UnicodeString KEYWORD_A
= UNICODE_STRING_SIMPLE("a");
184 status
= U_ZERO_ERROR
;
185 newRules
.adoptInstead(test
->createRules(decimalRule
, status
));
186 if (U_FAILURE(status
)) {
187 dataerrln("ERROR: Could not create PluralRules for testing fractions - exitting");
190 double fData
[10] = {-100, -1, -0.0, 0, 0.1, 1, 1.999, 2.0, 100, 100.001 };
191 UBool isKeywordA
[10] = {
192 TRUE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, TRUE
};
193 for (int32_t i
=0; i
<10; i
++) {
194 if ((newRules
->select(fData
[i
])== KEYWORD_A
) != isKeywordA
[i
]) {
195 errln("ERROR: plural rules for decimal fractions test failed!");
199 // ======= Test Equality
200 logln("Testing Equality of PluralRules");
202 if ( !testEquality(*test
) ) {
203 errln("ERROR: complex plural rules failed! - exitting");
208 // ======= Test getStaticClassID()
209 logln("Testing getStaticClassID()");
211 if(test
->getDynamicClassID() != PluralRules::getStaticClassID()) {
212 errln("ERROR: getDynamicClassID() didn't return the expected value");
214 // ====== Test fallback to parent locale
215 LocalPointer
<PluralRules
> en_UK(test
->forLocale(Locale::getUK(), status
));
216 LocalPointer
<PluralRules
> en(test
->forLocale(Locale::getEnglish(), status
));
217 if (en_UK
.isValid() && en
.isValid()) {
218 if ( *en_UK
!= *en
) {
219 errln("ERROR: test locale fallback failed!");
223 LocalPointer
<PluralRules
> zh_Hant(test
->forLocale(Locale::getTaiwan(), status
));
224 LocalPointer
<PluralRules
> zh(test
->forLocale(Locale::getChinese(), status
));
225 if (zh_Hant
.isValid() && zh
.isValid()) {
226 if ( *zh_Hant
!= *zh
) {
227 errln("ERROR: test locale fallback failed!");
232 void setupResult(const int32_t testSource
[], char result
[], int32_t* max
) {
237 while (curIndex
< testSource
[i
]) {
238 result
[curIndex
++]=0x6F; //'o' other
240 result
[curIndex
++]=0x61; // 'a'
242 } while(testSource
[++i
]>0);
247 UBool
checkEqual(const PluralRules
&test
, char *result
, int32_t max
) {
249 UBool isEqual
= TRUE
;
250 for (int32_t i
=0; i
<max
; ++i
) {
252 if ( key
.charAt(0)!=result
[i
] ) {
261 UBool
testEquality(const PluralRules
&test
) {
262 UnicodeString testEquRules
[MAX_EQ_ROW
][MAX_EQ_COL
] = {
263 { UNICODE_STRING_SIMPLE("a: n in 2..3"),
264 UNICODE_STRING_SIMPLE("a: n is 2 or n is 3"),
265 UNICODE_STRING_SIMPLE( "a:n is 3 and n in 2..5 or n is 2"),
268 { UNICODE_STRING_SIMPLE("a: n is 12; b:n mod 10 in 2..3"),
269 UNICODE_STRING_SIMPLE("b: n mod 10 in 2..3 and n is not 12; a: n in 12..12"),
270 UNICODE_STRING_SIMPLE("b: n is 13; a: n in 12..13; b: n mod 10 is 2 or n mod 10 is 3"),
274 UErrorCode status
= U_ZERO_ERROR
;
275 UnicodeString key
[MAX_EQ_COL
];
277 for (int32_t i
=0; i
<MAX_EQ_ROW
; ++i
) {
278 PluralRules
* rules
[MAX_EQ_COL
];
280 for (int32_t j
=0; j
<MAX_EQ_COL
; ++j
) {
283 int32_t totalRules
=0;
284 while((totalRules
<MAX_EQ_COL
) && (testEquRules
[i
][totalRules
].length()>0) ) {
285 rules
[totalRules
]=test
.createRules(testEquRules
[i
][totalRules
], status
);
288 for (int32_t n
=0; n
<300 && ret
; ++n
) {
289 for(int32_t j
=0; j
<totalRules
;++j
) {
290 key
[j
] = rules
[j
]->select(n
);
292 for(int32_t j
=0; j
<totalRules
-1;++j
) {
293 if (key
[j
]!=key
[j
+1]) {
300 for (int32_t j
=0; j
<MAX_EQ_COL
; ++j
) {
301 if (rules
[j
]!=NULL
) {
311 PluralRulesTest::assertRuleValue(const UnicodeString
& rule
, double expected
) {
312 assertRuleKeyValue("a:" + rule
, "a", expected
);
316 PluralRulesTest::assertRuleKeyValue(const UnicodeString
& rule
,
317 const UnicodeString
& key
, double expected
) {
318 UErrorCode status
= U_ZERO_ERROR
;
319 PluralRules
*pr
= PluralRules::createRules(rule
, status
);
320 double result
= pr
->getUniqueKeywordValue(key
);
322 if (expected
!= result
) {
323 errln("expected %g but got %g", expected
, result
);
327 void PluralRulesTest::testGetUniqueKeywordValue() {
328 assertRuleValue("n is 1", 1);
329 assertRuleValue("n in 2..2", 2);
330 assertRuleValue("n within 2..2", 2);
331 assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE
);
332 assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE
);
333 assertRuleValue("n is 2 or n is 2", 2);
334 assertRuleValue("n is 2 and n is 2", 2);
335 assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE
);
336 assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE
);
337 assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE
);
338 assertRuleValue("n is 2 and n in 2..3", 2);
339 assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE
); // key not defined
340 assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE
); // key matches default rule
343 void PluralRulesTest::testGetSamples() {
344 // no get functional equivalent API in ICU4C, so just
345 // test every locale...
346 UErrorCode status
= U_ZERO_ERROR
;
348 const Locale
* locales
= Locale::getAvailableLocales(numLocales
);
351 for (int32_t i
= 0; U_SUCCESS(status
) && i
< numLocales
; ++i
) {
352 PluralRules
*rules
= PluralRules::forLocale(locales
[i
], status
);
353 if (U_FAILURE(status
)) {
356 StringEnumeration
*keywords
= rules
->getKeywords(status
);
357 if (U_FAILURE(status
)) {
361 const UnicodeString
* keyword
;
362 while (NULL
!= (keyword
= keywords
->snext(status
))) {
363 int32_t count
= rules
->getSamples(*keyword
, values
, 4, status
);
364 if (U_FAILURE(status
)) {
365 errln(UNICODE_STRING_SIMPLE("getSamples() failed for locale ") +
366 locales
[i
].getName() +
367 UNICODE_STRING_SIMPLE(", keyword ") + *keyword
);
371 errln("no samples for keyword");
373 if (count
> LENGTHOF(values
)) {
374 errln(UNICODE_STRING_SIMPLE("getSamples()=") + count
+
375 UNICODE_STRING_SIMPLE(", too many values, for locale ") +
376 locales
[i
].getName() +
377 UNICODE_STRING_SIMPLE(", keyword ") + *keyword
);
378 count
= LENGTHOF(values
);
380 for (int32_t j
= 0; j
< count
; ++j
) {
381 if (values
[j
] == UPLRULES_NO_UNIQUE_VALUE
) {
382 errln("got 'no unique value' among values");
384 UnicodeString resultKeyword
= rules
->select(values
[j
]);
385 if (*keyword
!= resultKeyword
) {
386 errln("keywords don't match");
396 void PluralRulesTest::testWithin() {
397 // goes to show you what lack of testing will do.
398 // of course, this has been broken for two years and no one has noticed...
399 UErrorCode status
= U_ZERO_ERROR
;
400 PluralRules
*rules
= PluralRules::createRules("a: n mod 10 in 5..8", status
);
402 errln("couldn't instantiate rules");
406 UnicodeString keyword
= rules
->select((int32_t)26);
407 if (keyword
!= "a") {
408 errln("expected 'a' for 26 but didn't get it.");
411 keyword
= rules
->select(26.5);
412 if (keyword
!= "other") {
413 errln("expected 'other' for 26.5 but didn't get it.");
420 PluralRulesTest::testGetAllKeywordValues() {
421 const char* data
[] = {
422 "a: n in 2..5", "a: 2,3,4,5; other: null; b:",
423 "a: n not in 2..5", "a: null; other: null",
424 "a: n within 2..5", "a: null; other: null",
425 "a: n not within 2..5", "a: null; other: null",
426 "a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on out, always null
427 "a: n in 2..5 and n within 6..8", "a:",
428 "a: n in 2..5 and n within 5..8", "a: 5",
429 "a: n within 2..5 and n within 6..8", "a:", // our sampling catches these
430 "a: n within 2..5 and n within 5..8", "a: 5", // ''
431 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4",
432 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 "
433 "or n within 5..6 and n within 6..7", "a: null", // but not this...
434 "a: n mod 3 is 0", "a: null",
435 "a: n mod 3 is 0 and n within 1..2", "a:",
436 "a: n mod 3 is 0 and n within 0..5", "a: 0,3",
437 "a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, we don't catch...
438 "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12",
442 for (int i
= 0; data
[i
] != NULL
; i
+= 2) {
443 UErrorCode status
= U_ZERO_ERROR
;
444 UnicodeString
ruleDescription(data
[i
], -1, US_INV
);
445 const char* result
= data
[i
+1];
447 logln("[%d] %s", i
>> 1, data
[i
]);
449 PluralRules
*p
= PluralRules::createRules(ruleDescription
, status
);
450 if (U_FAILURE(status
)) {
451 logln("could not create rules from '%s'\n", data
[i
]);
455 const char* rp
= result
;
457 while (*rp
== ' ') ++rp
;
463 while (*ep
&& *ep
!= ':') ++ep
;
465 status
= U_ZERO_ERROR
;
466 UnicodeString
keyword(rp
, ep
- rp
, US_INV
);
467 double samples
[4]; // no test above should have more samples than 4
468 int32_t count
= p
->getAllKeywordValues(keyword
, &samples
[0], 4, status
);
469 if (U_FAILURE(status
)) {
470 errln("error getting samples for %s", rp
);
475 errln("count > 4 for keyword %s", rp
);
481 while (*ep
&& *ep
== ' ') ++ep
; // and spaces
487 errln("expected values for keyword %s but got -1 (%s)", rp
, ep
);
490 } else if (*ep
== 'n') {
491 errln("expected count of -1, got %d, for keyword %s (%s)", count
, rp
, ep
);
495 // We'll cheat a bit here. The samples happend to be in order and so are our
496 // expected values, so we'll just test in order until a failure. If the
497 // implementation changes to return samples in an arbitrary order, this test
498 // must change. There's no actual restriction on the order of the samples.
500 for (int j
= 0; ok
&& j
< count
; ++j
) { // we've verified count < 4
501 double val
= samples
[j
];
502 if (*ep
== 0 || *ep
== ';') {
503 errln("got unexpected value[%d]: %g", j
, val
);
508 double expectedVal
= strtod(ep
, &xp
);
516 if (expectedVal
!= val
) {
517 errln("expected %g but got %g", expectedVal
, val
);
521 if (*ep
== ',') ++ep
;
524 if (ok
&& count
!= -1) {
525 if (!(*ep
== 0 || *ep
== ';')) {
526 errln("didn't get expected value: %s", ep
);
531 while (*ep
&& *ep
!= ';') ++ep
;
532 if (*ep
== ';') ++ep
;
539 void PluralRulesTest::testOrdinal() {
540 IcuTestErrorCode
errorCode(*this, "testOrdinal");
541 LocalPointer
<PluralRules
> pr(PluralRules::forLocale("en", UPLURAL_TYPE_ORDINAL
, errorCode
));
542 if (errorCode
.logIfFailureAndReset("PluralRules::forLocale(en, UPLURAL_TYPE_ORDINAL) failed")) {
545 UnicodeString keyword
= pr
->select(2.);
546 if (keyword
!= UNICODE_STRING("two", 3)) {
547 dataerrln("PluralRules(en-ordinal).select(2) failed");
551 #endif /* #if !UCONFIG_NO_FORMATTING */