• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)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  */
testAPI()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 
setupResult(const int32_t testSource[],char result[],int32_t * max)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 
checkEqual(PluralRules * test,char * result,int32_t max)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
testEquality(PluralRules * test)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
assertRuleValue(const UnicodeString & rule,double expected)347 PluralRulesTest::assertRuleValue(const UnicodeString& rule, double expected) {
348   assertRuleKeyValue("a:" + rule, "a", expected);
349 }
350 
351 void
assertRuleKeyValue(const UnicodeString & rule,const UnicodeString & key,double 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);
357   delete pr;
358   if (expected != result) {
359     errln("expected %g but got %g", expected, result);
360   }
361 }
362 
testGetUniqueKeywordValue()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 
testGetSamples()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 
testWithin()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
testGetAllKeywordValues()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 */
576