2 *******************************************************************************
3 * Copyright (C) 2007-2011, 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/plurrule.h"
20 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof(array[0]))
22 void setupResult(const int32_t testSource
[], char result
[], int32_t* max
);
23 UBool
checkEqual(PluralRules
*test
, char *result
, int32_t max
);
24 UBool
testEquality(PluralRules
*test
);
26 // This is an API test, not a unit test. It doesn't test very many cases, and doesn't
27 // try to test the full functionality. It just calls each function in the class and
28 // verifies that it works on a basic level.
30 void PluralRulesTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
32 if (exec
) logln("TestSuite PluralRulesAPI");
35 TESTCASE(1, testGetUniqueKeywordValue
);
36 TESTCASE(2, testGetSamples
);
37 TESTCASE(3, testWithin
);
38 TESTCASE(4, testGetAllKeywordValues
);
39 default: name
= ""; break;
43 #define PLURAL_TEST_NUM 18
45 * Test various generic API methods of PluralRules for API coverage.
47 void PluralRulesTest::testAPI(/*char *par*/)
49 UnicodeString pluralTestData
[PLURAL_TEST_NUM
] = {
50 UNICODE_STRING_SIMPLE("a: n is 1"),
51 UNICODE_STRING_SIMPLE("a: n mod 10 is 2"),
52 UNICODE_STRING_SIMPLE("a: n is not 1"),
53 UNICODE_STRING_SIMPLE("a: n mod 3 is not 1"),
54 UNICODE_STRING_SIMPLE("a: n in 2..5"),
55 UNICODE_STRING_SIMPLE("a: n within 2..5"),
56 UNICODE_STRING_SIMPLE("a: n not in 2..5"),
57 UNICODE_STRING_SIMPLE("a: n not within 2..5"),
58 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..5"),
59 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..5"),
60 UNICODE_STRING_SIMPLE("a: n mod 10 is 2 and n is not 12"),
61 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..3 or n mod 10 is 5"),
62 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..3 or n mod 10 is 5"),
63 UNICODE_STRING_SIMPLE("a: n is 1 or n is 4 or n is 23"),
64 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n in 1..11"),
65 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n within 1..11"),
66 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 or n mod 5 is 1 and n is not 6"),
69 static const int32_t pluralTestResult
[PLURAL_TEST_NUM
][30] = {
78 {2,3,4,5,12,13,14,15,22,23,24,25,0},
79 {2,3,4,5,12,13,14,15,22,23,24,25,0},
81 {2,3,5,12,13,15,22,23,25,0},
82 {2,3,5,12,13,15,22,23,25,0},
86 {1,3,5,7,9,11,13,15,16,0},
88 UErrorCode status
= U_ZERO_ERROR
;
90 // ======= Test constructors
91 logln("Testing PluralRules constructors");
94 logln("\n start default locale test case ..\n");
96 PluralRules
defRule(status
);
97 PluralRules
* test
=new PluralRules(status
);
98 PluralRules
* newEnPlural
= test
->forLocale(Locale::getEnglish(), status
);
99 if(U_FAILURE(status
)) {
100 dataerrln("ERROR: Could not create PluralRules (default) - exitting");
105 // ======= Test clone, assignment operator && == operator.
106 PluralRules
*dupRule
= defRule
.clone();
108 errln("ERROR: clone plural rules test failed!");
112 if ( *dupRule
!= defRule
) {
113 errln("ERROR: clone plural rules test failed!");
116 *dupRule
= *newEnPlural
;
118 if ( *dupRule
!= *newEnPlural
) {
119 errln("ERROR: clone plural rules test failed!");
126 // ======= Test empty plural rules
127 logln("Testing Simple PluralRules");
129 PluralRules
* empRule
= test
->createRules(UNICODE_STRING_SIMPLE("a:n"), status
);
131 for (int32_t i
=0; i
<10; ++i
) {
132 key
= empRule
->select(i
);
133 if ( key
.charAt(0)!= 0x61 ) { // 'a'
134 errln("ERROR: empty plural rules test failed! - exitting");
141 // ======= Test simple plural rules
142 logln("Testing Simple PluralRules");
147 for (int32_t i
=0; i
<PLURAL_TEST_NUM
-1; ++i
) {
148 PluralRules
*newRules
= test
->createRules(pluralTestData
[i
], status
);
149 setupResult(pluralTestResult
[i
], result
, &max
);
150 if ( !checkEqual(newRules
, result
, max
) ) {
151 errln("ERROR: simple plural rules failed! - exitting");
155 if (newRules
!=NULL
) {
161 // ======= Test complex plural rules
162 logln("Testing Complex PluralRules");
163 // TODO: the complex test data is hard coded. It's better to implement
164 // a parser to parse the test data.
165 UnicodeString complexRule
= UNICODE_STRING_SIMPLE("a: n in 2..5; b: n in 5..8; c: n mod 2 is 1");
166 UnicodeString complexRule2
= UNICODE_STRING_SIMPLE("a: n within 2..5; b: n within 5..8; c: n mod 2 is 1");
182 PluralRules
*newRules
= test
->createRules(complexRule
, status
);
183 if ( !checkEqual(newRules
, cRuleResult
, 12) ) {
184 errln("ERROR: complex plural rules failed! - exitting");
188 if (newRules
!=NULL
) {
192 newRules
= test
->createRules(complexRule2
, status
);
193 if ( !checkEqual(newRules
, cRuleResult
, 12) ) {
194 errln("ERROR: complex plural rules failed! - exitting");
198 if (newRules
!=NULL
) {
203 // ======= Test decimal fractions plural rules
204 UnicodeString decimalRule
= UNICODE_STRING_SIMPLE("a: n not in 0..100;");
205 UnicodeString KEYWORD_A
= UNICODE_STRING_SIMPLE("a");
206 status
= U_ZERO_ERROR
;
207 newRules
= test
->createRules(decimalRule
, status
);
208 if (U_FAILURE(status
)) {
209 dataerrln("ERROR: Could not create PluralRules for testing fractions - exitting");
213 double fData
[10] = {-100, -1, -0.0, 0, 0.1, 1, 1.999, 2.0, 100, 100.001 };
214 UBool isKeywordA
[10] = {
215 TRUE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, TRUE
};
216 for (int32_t i
=0; i
<10; i
++) {
217 if ((newRules
->select(fData
[i
])== KEYWORD_A
) != isKeywordA
[i
]) {
218 errln("ERROR: plural rules for decimal fractions test failed!");
221 if (newRules
!=NULL
) {
228 // ======= Test Equality
229 logln("Testing Equality of PluralRules");
232 if ( !testEquality(test
) ) {
233 errln("ERROR: complex plural rules failed! - exitting");
239 // ======= Test getStaticClassID()
240 logln("Testing getStaticClassID()");
242 if(test
->getDynamicClassID() != PluralRules::getStaticClassID()) {
243 errln("ERROR: getDynamicClassID() didn't return the expected value");
245 // ====== Test fallback to parent locale
246 PluralRules
*en_UK
= test
->forLocale(Locale::getUK(), status
);
247 PluralRules
*en
= test
->forLocale(Locale::getEnglish(), status
);
248 if (en_UK
!= NULL
&& en
!= NULL
) {
249 if ( *en_UK
!= *en
) {
250 errln("ERROR: test locale fallback failed!");
256 PluralRules
*zh_Hant
= test
->forLocale(Locale::getTaiwan(), status
);
257 PluralRules
*zh
= test
->forLocale(Locale::getChinese(), status
);
258 if (zh_Hant
!= NULL
&& zh
!= NULL
) {
259 if ( *zh_Hant
!= *zh
) {
260 errln("ERROR: test locale fallback failed!");
268 void setupResult(const int32_t testSource
[], char result
[], int32_t* max
) {
273 while (curIndex
< testSource
[i
]) {
274 result
[curIndex
++]=0x6F; //'o' other
276 result
[curIndex
++]=0x61; // 'a'
278 } while(testSource
[++i
]>0);
283 UBool
checkEqual(PluralRules
*test
, char *result
, int32_t max
) {
285 UBool isEqual
= TRUE
;
286 for (int32_t i
=0; i
<max
; ++i
) {
287 key
= test
->select(i
);
288 if ( key
.charAt(0)!=result
[i
] ) {
297 UBool
testEquality(PluralRules
*test
) {
298 UnicodeString testEquRules
[MAX_EQ_ROW
][MAX_EQ_COL
] = {
299 { UNICODE_STRING_SIMPLE("a: n in 2..3"),
300 UNICODE_STRING_SIMPLE("a: n is 2 or n is 3"),
301 UNICODE_STRING_SIMPLE( "a:n is 3 and n in 2..5 or n is 2"),
304 { UNICODE_STRING_SIMPLE("a: n is 12; b:n mod 10 in 2..3"),
305 UNICODE_STRING_SIMPLE("b: n mod 10 in 2..3 and n is not 12; a: n in 12..12"),
306 UNICODE_STRING_SIMPLE("b: n is 13; a: n in 12..13; b: n mod 10 is 2 or n mod 10 is 3"),
310 UErrorCode status
= U_ZERO_ERROR
;
311 UnicodeString key
[MAX_EQ_COL
];
313 for (int32_t i
=0; i
<MAX_EQ_ROW
; ++i
) {
314 PluralRules
* rules
[MAX_EQ_COL
];
316 for (int32_t j
=0; j
<MAX_EQ_COL
; ++j
) {
319 int32_t totalRules
=0;
320 while((totalRules
<MAX_EQ_COL
) && (testEquRules
[i
][totalRules
].length()>0) ) {
321 rules
[totalRules
]=test
->createRules(testEquRules
[i
][totalRules
], status
);
324 for (int32_t n
=0; n
<300 && ret
; ++n
) {
325 for(int32_t j
=0; j
<totalRules
;++j
) {
326 key
[j
] = rules
[j
]->select(n
);
328 for(int32_t j
=0; j
<totalRules
-1;++j
) {
329 if (key
[j
]!=key
[j
+1]) {
336 for (int32_t j
=0; j
<MAX_EQ_COL
; ++j
) {
337 if (rules
[j
]!=NULL
) {
347 PluralRulesTest::assertRuleValue(const UnicodeString
& rule
, double expected
) {
348 assertRuleKeyValue("a:" + rule
, "a", expected
);
352 PluralRulesTest::assertRuleKeyValue(const UnicodeString
& rule
,
353 const UnicodeString
& key
, double expected
) {
354 UErrorCode status
= U_ZERO_ERROR
;
355 PluralRules
*pr
= PluralRules::createRules(rule
, status
);
356 double result
= pr
->getUniqueKeywordValue(key
);
358 if (expected
!= result
) {
359 errln("expected %g but got %g", expected
, result
);
363 void PluralRulesTest::testGetUniqueKeywordValue() {
364 assertRuleValue("n is 1", 1);
365 assertRuleValue("n in 2..2", 2);
366 assertRuleValue("n within 2..2", 2);
367 assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE
);
368 assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE
);
369 assertRuleValue("n is 2 or n is 2", 2);
370 assertRuleValue("n is 2 and n is 2", 2);
371 assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE
);
372 assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE
);
373 assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE
);
374 assertRuleValue("n is 2 and n in 2..3", 2);
375 assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE
); // key not defined
376 assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE
); // key matches default rule
379 void PluralRulesTest::testGetSamples() {
380 // no get functional equivalent API in ICU4C, so just
381 // test every locale...
382 UErrorCode status
= U_ZERO_ERROR
;
384 const Locale
* locales
= Locale::getAvailableLocales(numLocales
);
387 for (int32_t i
= 0; U_SUCCESS(status
) && i
< numLocales
; ++i
) {
388 PluralRules
*rules
= PluralRules::forLocale(locales
[i
], status
);
389 if (U_FAILURE(status
)) {
392 StringEnumeration
*keywords
= rules
->getKeywords(status
);
393 if (U_FAILURE(status
)) {
397 const UnicodeString
* keyword
;
398 while (NULL
!= (keyword
= keywords
->snext(status
))) {
399 int32_t count
= rules
->getSamples(*keyword
, values
, 4, status
);
400 if (U_FAILURE(status
)) {
401 errln(UNICODE_STRING_SIMPLE("getSamples() failed for locale ") +
402 locales
[i
].getName() +
403 UNICODE_STRING_SIMPLE(", keyword ") + *keyword
);
407 errln("no samples for keyword");
409 if (count
> LENGTHOF(values
)) {
410 errln(UNICODE_STRING_SIMPLE("getSamples()=") + count
+
411 UNICODE_STRING_SIMPLE(", too many values, for locale ") +
412 locales
[i
].getName() +
413 UNICODE_STRING_SIMPLE(", keyword ") + *keyword
);
414 count
= LENGTHOF(values
);
416 for (int32_t j
= 0; j
< count
; ++j
) {
417 if (values
[j
] == UPLRULES_NO_UNIQUE_VALUE
) {
418 errln("got 'no unique value' among values");
420 UnicodeString resultKeyword
= rules
->select(values
[j
]);
421 if (*keyword
!= resultKeyword
) {
422 errln("keywords don't match");
432 void PluralRulesTest::testWithin() {
433 // goes to show you what lack of testing will do.
434 // of course, this has been broken for two years and no one has noticed...
435 UErrorCode status
= U_ZERO_ERROR
;
436 PluralRules
*rules
= PluralRules::createRules("a: n mod 10 in 5..8", status
);
438 errln("couldn't instantiate rules");
442 UnicodeString keyword
= rules
->select((int32_t)26);
443 if (keyword
!= "a") {
444 errln("expected 'a' for 26 but didn't get it.");
447 keyword
= rules
->select(26.5);
448 if (keyword
!= "other") {
449 errln("expected 'other' for 26.5 but didn't get it.");
456 PluralRulesTest::testGetAllKeywordValues() {
457 const char* data
[] = {
458 "a: n in 2..5", "a: 2,3,4,5; other: null; b:",
459 "a: n not in 2..5", "a: null; other: null",
460 "a: n within 2..5", "a: null; other: null",
461 "a: n not within 2..5", "a: null; other: null",
462 "a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on out, always null
463 "a: n in 2..5 and n within 6..8", "a:",
464 "a: n in 2..5 and n within 5..8", "a: 5",
465 "a: n within 2..5 and n within 6..8", "a:", // our sampling catches these
466 "a: n within 2..5 and n within 5..8", "a: 5", // ''
467 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4",
468 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 "
469 "or n within 5..6 and n within 6..7", "a: null", // but not this...
470 "a: n mod 3 is 0", "a: null",
471 "a: n mod 3 is 0 and n within 1..2", "a:",
472 "a: n mod 3 is 0 and n within 0..5", "a: 0,3",
473 "a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, we don't catch...
474 "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12",
478 for (int i
= 0; data
[i
] != NULL
; i
+= 2) {
479 UErrorCode status
= U_ZERO_ERROR
;
480 UnicodeString
ruleDescription(data
[i
], -1, US_INV
);
481 const char* result
= data
[i
+1];
483 logln("[%d] %s", i
>> 1, data
[i
]);
485 PluralRules
*p
= PluralRules::createRules(ruleDescription
, status
);
486 if (U_FAILURE(status
)) {
487 logln("could not create rules from '%s'\n", data
[i
]);
491 const char* rp
= result
;
493 while (*rp
== ' ') ++rp
;
499 while (*ep
&& *ep
!= ':') ++ep
;
501 status
= U_ZERO_ERROR
;
502 UnicodeString
keyword(rp
, ep
- rp
, US_INV
);
503 double samples
[4]; // no test above should have more samples than 4
504 int32_t count
= p
->getAllKeywordValues(keyword
, &samples
[0], 4, status
);
505 if (U_FAILURE(status
)) {
506 errln("error getting samples for %s", rp
);
511 errln("count > 4 for keyword %s", rp
);
517 while (*ep
&& *ep
== ' ') ++ep
; // and spaces
523 errln("expected values for keyword %s but got -1 (%s)", rp
, ep
);
526 } else if (*ep
== 'n') {
527 errln("expected count of -1, got %d, for keyword %s (%s)", count
, rp
, ep
);
531 // We'll cheat a bit here. The samples happend to be in order and so are our
532 // expected values, so we'll just test in order until a failure. If the
533 // implementation changes to return samples in an arbitrary order, this test
534 // must change. There's no actual restriction on the order of the samples.
536 for (int j
= 0; ok
&& j
< count
; ++j
) { // we've verified count < 4
537 double val
= samples
[j
];
538 if (*ep
== 0 || *ep
== ';') {
539 errln("got unexpected value[%d]: %g", j
, val
);
544 double expectedVal
= strtod(ep
, &xp
);
552 if (expectedVal
!= val
) {
553 errln("expected %g but got %g", expectedVal
, val
);
557 if (*ep
== ',') ++ep
;
560 if (ok
&& count
!= -1) {
561 if (!(*ep
== 0 || *ep
== ';')) {
562 errln("didn't get expected value: %s", ep
);
567 while (*ep
&& *ep
!= ';') ++ep
;
568 if (*ep
== ';') ++ep
;
575 #endif /* #if !UCONFIG_NO_FORMATTING */