Home | History | Annotate | Download | only in intltest
      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 */
    576