]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/plurults.cpp
ICU-491.11.3.tar.gz
[apple/icu.git] / icuSources / test / intltest / plurults.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2011, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************************
6
7 * File PLURRULTS.cpp
8 *
9 ********************************************************************************
10 */
11
12 #include "unicode/utypes.h"
13
14 #if !UCONFIG_NO_FORMATTING
15
16 #include <stdlib.h> // for strtod
17 #include "plurults.h"
18 #include "unicode/plurrule.h"
19
20 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof(array[0]))
21
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);
25
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.
29
30 void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
31 {
32 if (exec) logln("TestSuite PluralRulesAPI");
33 switch (index) {
34 TESTCASE(0, testAPI);
35 TESTCASE(1, testGetUniqueKeywordValue);
36 TESTCASE(2, testGetSamples);
37 TESTCASE(3, testWithin);
38 TESTCASE(4, testGetAllKeywordValues);
39 default: name = ""; break;
40 }
41 }
42
43 #define PLURAL_TEST_NUM 18
44 /**
45 * Test various generic API methods of PluralRules for API coverage.
46 */
47 void PluralRulesTest::testAPI(/*char *par*/)
48 {
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"),
67 "",
68 };
69 static const int32_t pluralTestResult[PLURAL_TEST_NUM][30] = {
70 {1, 0},
71 {2,12,22, 0},
72 {0,2,3,4,5,0},
73 {0,2,3,5,6,8,9,0},
74 {2,3,4,5,0},
75 {2,3,4,5,0},
76 {0,1,6,7,8, 0},
77 {0,1,6,7,8, 0},
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},
80 {2,22,32,42,0},
81 {2,3,5,12,13,15,22,23,25,0},
82 {2,3,5,12,13,15,22,23,25,0},
83 {1,4,23,0},
84 {1,5,7,9,11,0},
85 {1,5,7,9,11,0},
86 {1,3,5,7,9,11,13,15,16,0},
87 };
88 UErrorCode status = U_ZERO_ERROR;
89
90 // ======= Test constructors
91 logln("Testing PluralRules constructors");
92
93
94 logln("\n start default locale test case ..\n");
95
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");
101 delete test;
102 return;
103 }
104
105 // ======= Test clone, assignment operator && == operator.
106 PluralRules *dupRule = defRule.clone();
107 if (dupRule==NULL) {
108 errln("ERROR: clone plural rules test failed!");
109 delete test;
110 return;
111 } else {
112 if ( *dupRule != defRule ) {
113 errln("ERROR: clone plural rules test failed!");
114 }
115 }
116 *dupRule = *newEnPlural;
117 if (dupRule!=NULL) {
118 if ( *dupRule != *newEnPlural ) {
119 errln("ERROR: clone plural rules test failed!");
120 }
121 delete dupRule;
122 }
123
124 delete newEnPlural;
125
126 // ======= Test empty plural rules
127 logln("Testing Simple PluralRules");
128
129 PluralRules* empRule = test->createRules(UNICODE_STRING_SIMPLE("a:n"), status);
130 UnicodeString key;
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");
135 }
136 }
137 if (empRule!=NULL) {
138 delete empRule;
139 }
140
141 // ======= Test simple plural rules
142 logln("Testing Simple PluralRules");
143
144 char result[100];
145 int32_t max;
146
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");
152 delete test;
153 return;
154 }
155 if (newRules!=NULL) {
156 delete newRules;
157 }
158 }
159
160
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");
167 char cRuleResult[] =
168 {
169 0x6F, // 'o'
170 0x63, // 'c'
171 0x61, // 'a'
172 0x61, // 'a'
173 0x61, // 'a'
174 0x61, // 'a'
175 0x62, // 'b'
176 0x62, // 'b'
177 0x62, // 'b'
178 0x63, // 'c'
179 0x6F, // 'o'
180 0x63 // 'c'
181 };
182 PluralRules *newRules = test->createRules(complexRule, status);
183 if ( !checkEqual(newRules, cRuleResult, 12) ) {
184 errln("ERROR: complex plural rules failed! - exitting");
185 delete test;
186 return;
187 }
188 if (newRules!=NULL) {
189 delete newRules;
190 newRules=NULL;
191 }
192 newRules = test->createRules(complexRule2, status);
193 if ( !checkEqual(newRules, cRuleResult, 12) ) {
194 errln("ERROR: complex plural rules failed! - exitting");
195 delete test;
196 return;
197 }
198 if (newRules!=NULL) {
199 delete newRules;
200 newRules=NULL;
201 }
202
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");
210 delete test;
211 return;
212 }
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!");
219 }
220 }
221 if (newRules!=NULL) {
222 delete newRules;
223 newRules=NULL;
224 }
225
226
227
228 // ======= Test Equality
229 logln("Testing Equality of PluralRules");
230
231
232 if ( !testEquality(test) ) {
233 errln("ERROR: complex plural rules failed! - exitting");
234 delete test;
235 return;
236 }
237
238
239 // ======= Test getStaticClassID()
240 logln("Testing getStaticClassID()");
241
242 if(test->getDynamicClassID() != PluralRules::getStaticClassID()) {
243 errln("ERROR: getDynamicClassID() didn't return the expected value");
244 }
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!");
251 }
252 }
253 delete en;
254 delete en_UK;
255
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!");
261 }
262 }
263 delete zh_Hant;
264 delete zh;
265 delete test;
266 }
267
268 void setupResult(const int32_t testSource[], char result[], int32_t* max) {
269 int32_t i=0;
270 int32_t curIndex=0;
271
272 do {
273 while (curIndex < testSource[i]) {
274 result[curIndex++]=0x6F; //'o' other
275 }
276 result[curIndex++]=0x61; // 'a'
277
278 } while(testSource[++i]>0);
279 *max=curIndex;
280 }
281
282
283 UBool checkEqual(PluralRules *test, char *result, int32_t max) {
284 UnicodeString key;
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] ) {
289 isEqual = FALSE;
290 }
291 }
292 return isEqual;
293 }
294
295 #define MAX_EQ_ROW 2
296 #define MAX_EQ_COL 5
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"),
302 "",
303 },
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"),
307 "",
308 }
309 };
310 UErrorCode status = U_ZERO_ERROR;
311 UnicodeString key[MAX_EQ_COL];
312 UBool ret=TRUE;
313 for (int32_t i=0; i<MAX_EQ_ROW; ++i) {
314 PluralRules* rules[MAX_EQ_COL];
315
316 for (int32_t j=0; j<MAX_EQ_COL; ++j) {
317 rules[j]=NULL;
318 }
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);
322 totalRules++;
323 }
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);
327 }
328 for(int32_t j=0; j<totalRules-1;++j) {
329 if (key[j]!=key[j+1]) {
330 ret= FALSE;
331 break;
332 }
333 }
334
335 }
336 for (int32_t j=0; j<MAX_EQ_COL; ++j) {
337 if (rules[j]!=NULL) {
338 delete rules[j];
339 }
340 }
341 }
342
343 return ret;
344 }
345
346 void
347 PluralRulesTest::assertRuleValue(const UnicodeString& rule, double expected) {
348 assertRuleKeyValue("a:" + rule, "a", expected);
349 }
350
351 void
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);
357 delete pr;
358 if (expected != result) {
359 errln("expected %g but got %g", expected, result);
360 }
361 }
362
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
377 }
378
379 void PluralRulesTest::testGetSamples() {
380 // no get functional equivalent API in ICU4C, so just
381 // test every locale...
382 UErrorCode status = U_ZERO_ERROR;
383 int32_t numLocales;
384 const Locale* locales = Locale::getAvailableLocales(numLocales);
385
386 double values[4];
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)) {
390 break;
391 }
392 StringEnumeration *keywords = rules->getKeywords(status);
393 if (U_FAILURE(status)) {
394 delete rules;
395 break;
396 }
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);
404 continue;
405 }
406 if (count == 0) {
407 errln("no samples for keyword");
408 }
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);
415 }
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");
419 } else {
420 UnicodeString resultKeyword = rules->select(values[j]);
421 if (*keyword != resultKeyword) {
422 errln("keywords don't match");
423 }
424 }
425 }
426 }
427 delete keywords;
428 delete rules;
429 }
430 }
431
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);
437 if (!rules) {
438 errln("couldn't instantiate rules");
439 return;
440 }
441
442 UnicodeString keyword = rules->select((int32_t)26);
443 if (keyword != "a") {
444 errln("expected 'a' for 26 but didn't get it.");
445 }
446
447 keyword = rules->select(26.5);
448 if (keyword != "other") {
449 errln("expected 'other' for 26.5 but didn't get it.");
450 }
451
452 delete rules;
453 }
454
455 void
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",
475 NULL
476 };
477
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];
482
483 logln("[%d] %s", i >> 1, data[i]);
484
485 PluralRules *p = PluralRules::createRules(ruleDescription, status);
486 if (U_FAILURE(status)) {
487 logln("could not create rules from '%s'\n", data[i]);
488 continue;
489 }
490
491 const char* rp = result;
492 while (*rp) {
493 while (*rp == ' ') ++rp;
494 if (!rp) {
495 break;
496 }
497
498 const char* ep = rp;
499 while (*ep && *ep != ':') ++ep;
500
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);
507 break;
508 }
509
510 if (count > 4) {
511 errln("count > 4 for keyword %s", rp);
512 count = 4;
513 }
514
515 if (*ep) {
516 ++ep; // skip colon
517 while (*ep && *ep == ' ') ++ep; // and spaces
518 }
519
520 UBool ok = TRUE;
521 if (count == -1) {
522 if (*ep != 'n') {
523 errln("expected values for keyword %s but got -1 (%s)", rp, ep);
524 ok = FALSE;
525 }
526 } else if (*ep == 'n') {
527 errln("expected count of -1, got %d, for keyword %s (%s)", count, rp, ep);
528 ok = FALSE;
529 }
530
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.
535
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);
540 ok = FALSE;
541 break;
542 }
543 char* xp;
544 double expectedVal = strtod(ep, &xp);
545 if (xp == ep) {
546 // internal error
547 errln("yikes!");
548 ok = FALSE;
549 break;
550 }
551 ep = xp;
552 if (expectedVal != val) {
553 errln("expected %g but got %g", expectedVal, val);
554 ok = FALSE;
555 break;
556 }
557 if (*ep == ',') ++ep;
558 }
559
560 if (ok && count != -1) {
561 if (!(*ep == 0 || *ep == ';')) {
562 errln("didn't get expected value: %s", ep);
563 ok = FALSE;
564 }
565 }
566
567 while (*ep && *ep != ';') ++ep;
568 if (*ep == ';') ++ep;
569 rp = ep;
570 }
571 delete p;
572 }
573 }
574
575 #endif /* #if !UCONFIG_NO_FORMATTING */