Home | History | Annotate | Download | only in intltest
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2012, 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/localpointer.h"
     19 #include "unicode/plurrule.h"
     20 
     21 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof(array[0]))
     22 
     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);
     26 
     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.
     30 
     31 void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     32 {
     33     if (exec) logln("TestSuite PluralRulesAPI");
     34     TESTCASE_AUTO_BEGIN;
     35     TESTCASE_AUTO(testAPI);
     36     TESTCASE_AUTO(testGetUniqueKeywordValue);
     37     TESTCASE_AUTO(testGetSamples);
     38     TESTCASE_AUTO(testWithin);
     39     TESTCASE_AUTO(testGetAllKeywordValues);
     40     TESTCASE_AUTO(testOrdinal);
     41     TESTCASE_AUTO_END;
     42 }
     43 
     44 #define PLURAL_TEST_NUM    18
     45 /**
     46  * Test various generic API methods of PluralRules for API coverage.
     47  */
     48 void PluralRulesTest::testAPI(/*char *par*/)
     49 {
     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"),
     68             "",
     69     };
     70     static const int32_t pluralTestResult[PLURAL_TEST_NUM][30] = {
     71         {1, 0},
     72         {2,12,22, 0},
     73         {0,2,3,4,5,0},
     74         {0,2,3,5,6,8,9,0},
     75         {2,3,4,5,0},
     76         {2,3,4,5,0},
     77         {0,1,6,7,8, 0},
     78         {0,1,6,7,8, 0},
     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},
     81         {2,22,32,42,0},
     82         {2,3,5,12,13,15,22,23,25,0},
     83         {2,3,5,12,13,15,22,23,25,0},
     84         {1,4,23,0},
     85         {1,5,7,9,11,0},
     86         {1,5,7,9,11,0},
     87         {1,3,5,7,9,11,13,15,16,0},
     88     };
     89     UErrorCode status = U_ZERO_ERROR;
     90 
     91     // ======= Test constructors
     92     logln("Testing PluralRules constructors");
     93 
     94 
     95     logln("\n start default locale test case ..\n");
     96 
     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");
    102         return;
    103     }
    104 
    105     // ======= Test clone, assignment operator && == operator.
    106     LocalPointer<PluralRules> dupRule(defRule.clone());
    107     if (dupRule==NULL) {
    108         errln("ERROR: clone plural rules test failed!");
    109         return;
    110     } else {
    111         if ( *dupRule != defRule ) {
    112             errln("ERROR:  clone plural rules test failed!");
    113         }
    114     }
    115     *dupRule = *newEnPlural;
    116     if (dupRule!=NULL) {
    117         if ( *dupRule != *newEnPlural ) {
    118             errln("ERROR:  clone plural rules test failed!");
    119         }
    120     }
    121 
    122     // ======= Test empty plural rules
    123     logln("Testing Simple PluralRules");
    124 
    125     LocalPointer<PluralRules> empRule(test->createRules(UNICODE_STRING_SIMPLE("a:n"), status));
    126     UnicodeString key;
    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");
    131         }
    132     }
    133 
    134     // ======= Test simple plural rules
    135     logln("Testing Simple PluralRules");
    136 
    137     char result[100];
    138     int32_t max;
    139 
    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");
    145             return;
    146         }
    147     }
    148 
    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");
    155     char cRuleResult[] =
    156     {
    157        0x6F, // 'o'
    158        0x63, // 'c'
    159        0x61, // 'a'
    160        0x61, // 'a'
    161        0x61, // 'a'
    162        0x61, // 'a'
    163        0x62, // 'b'
    164        0x62, // 'b'
    165        0x62, // 'b'
    166        0x63, // 'c'
    167        0x6F, // 'o'
    168        0x63  // 'c'
    169     };
    170     LocalPointer<PluralRules> newRules(test->createRules(complexRule, status));
    171     if ( !checkEqual(*newRules, cRuleResult, 12) ) {
    172          errln("ERROR:  complex plural rules failed! - exitting");
    173          return;
    174     }
    175     newRules.adoptInstead(test->createRules(complexRule2, status));
    176     if ( !checkEqual(*newRules, cRuleResult, 12) ) {
    177          errln("ERROR:  complex plural rules failed! - exitting");
    178          return;
    179     }
    180 
    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");
    188         return;
    189     }
    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!");
    196         }
    197     }
    198 
    199     // ======= Test Equality
    200     logln("Testing Equality of PluralRules");
    201 
    202     if ( !testEquality(*test) ) {
    203          errln("ERROR:  complex plural rules failed! - exitting");
    204          return;
    205      }
    206 
    207 
    208     // ======= Test getStaticClassID()
    209     logln("Testing getStaticClassID()");
    210 
    211     if(test->getDynamicClassID() != PluralRules::getStaticClassID()) {
    212         errln("ERROR: getDynamicClassID() didn't return the expected value");
    213     }
    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!");
    220         }
    221     }
    222 
    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!");
    228         }
    229     }
    230 }
    231 
    232 void setupResult(const int32_t testSource[], char result[], int32_t* max) {
    233     int32_t i=0;
    234     int32_t curIndex=0;
    235 
    236     do {
    237         while (curIndex < testSource[i]) {
    238             result[curIndex++]=0x6F; //'o' other
    239         }
    240         result[curIndex++]=0x61; // 'a'
    241 
    242     } while(testSource[++i]>0);
    243     *max=curIndex;
    244 }
    245 
    246 
    247 UBool checkEqual(const PluralRules &test, char *result, int32_t max) {
    248     UnicodeString key;
    249     UBool isEqual = TRUE;
    250     for (int32_t i=0; i<max; ++i) {
    251         key= test.select(i);
    252         if ( key.charAt(0)!=result[i] ) {
    253             isEqual = FALSE;
    254         }
    255     }
    256     return isEqual;
    257 }
    258 
    259 #define MAX_EQ_ROW  2
    260 #define MAX_EQ_COL  5
    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"),
    266             "",
    267         },
    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"),
    271             "",
    272         }
    273     };
    274     UErrorCode status = U_ZERO_ERROR;
    275     UnicodeString key[MAX_EQ_COL];
    276     UBool ret=TRUE;
    277     for (int32_t i=0; i<MAX_EQ_ROW; ++i) {
    278         PluralRules* rules[MAX_EQ_COL];
    279 
    280         for (int32_t j=0; j<MAX_EQ_COL; ++j) {
    281             rules[j]=NULL;
    282         }
    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);
    286             totalRules++;
    287         }
    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);
    291             }
    292             for(int32_t j=0; j<totalRules-1;++j) {
    293                 if (key[j]!=key[j+1]) {
    294                     ret= FALSE;
    295                     break;
    296                 }
    297             }
    298 
    299         }
    300         for (int32_t j=0; j<MAX_EQ_COL; ++j) {
    301             if (rules[j]!=NULL) {
    302                 delete rules[j];
    303             }
    304         }
    305     }
    306 
    307     return ret;
    308 }
    309 
    310 void
    311 PluralRulesTest::assertRuleValue(const UnicodeString& rule, double expected) {
    312   assertRuleKeyValue("a:" + rule, "a", expected);
    313 }
    314 
    315 void
    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);
    321   delete pr;
    322   if (expected != result) {
    323     errln("expected %g but got %g", expected, result);
    324   }
    325 }
    326 
    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
    341 }
    342 
    343 void PluralRulesTest::testGetSamples() {
    344   // no get functional equivalent API in ICU4C, so just
    345   // test every locale...
    346   UErrorCode status = U_ZERO_ERROR;
    347   int32_t numLocales;
    348   const Locale* locales = Locale::getAvailableLocales(numLocales);
    349 
    350   double values[4];
    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)) {
    354       break;
    355     }
    356     StringEnumeration *keywords = rules->getKeywords(status);
    357     if (U_FAILURE(status)) {
    358       delete rules;
    359       break;
    360     }
    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);
    368         continue;
    369       }
    370       if (count == 0) {
    371         errln("no samples for keyword");
    372       }
    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);
    379       }
    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");
    383         } else {
    384           UnicodeString resultKeyword = rules->select(values[j]);
    385           if (*keyword != resultKeyword) {
    386             errln("keywords don't match");
    387           }
    388         }
    389       }
    390     }
    391     delete keywords;
    392     delete rules;
    393   }
    394 }
    395 
    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);
    401   if (!rules) {
    402     errln("couldn't instantiate rules");
    403     return;
    404   }
    405 
    406   UnicodeString keyword = rules->select((int32_t)26);
    407   if (keyword != "a") {
    408     errln("expected 'a' for 26 but didn't get it.");
    409   }
    410 
    411   keyword = rules->select(26.5);
    412   if (keyword != "other") {
    413     errln("expected 'other' for 26.5 but didn't get it.");
    414   }
    415 
    416   delete rules;
    417 }
    418 
    419 void
    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",
    439         NULL
    440     };
    441 
    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];
    446 
    447         logln("[%d] %s", i >> 1, data[i]);
    448 
    449         PluralRules *p = PluralRules::createRules(ruleDescription, status);
    450         if (U_FAILURE(status)) {
    451             logln("could not create rules from '%s'\n", data[i]);
    452             continue;
    453         }
    454 
    455         const char* rp = result;
    456         while (*rp) {
    457             while (*rp == ' ') ++rp;
    458             if (!rp) {
    459                 break;
    460             }
    461 
    462             const char* ep = rp;
    463             while (*ep && *ep != ':') ++ep;
    464 
    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);
    471                 break;
    472             }
    473 
    474             if (count > 4) {
    475               errln("count > 4 for keyword %s", rp);
    476               count = 4;
    477             }
    478 
    479             if (*ep) {
    480                 ++ep; // skip colon
    481                 while (*ep && *ep == ' ') ++ep; // and spaces
    482             }
    483 
    484             UBool ok = TRUE;
    485             if (count == -1) {
    486                 if (*ep != 'n') {
    487                     errln("expected values for keyword %s but got -1 (%s)", rp, ep);
    488                     ok = FALSE;
    489                 }
    490             } else if (*ep == 'n') {
    491                 errln("expected count of -1, got %d, for keyword %s (%s)", count, rp, ep);
    492                 ok = FALSE;
    493             }
    494 
    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.
    499 
    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);
    504                     ok = FALSE;
    505                     break;
    506                 }
    507                 char* xp;
    508                 double expectedVal = strtod(ep, &xp);
    509                 if (xp == ep) {
    510                     // internal error
    511                     errln("yikes!");
    512                     ok = FALSE;
    513                     break;
    514                 }
    515                 ep = xp;
    516                 if (expectedVal != val) {
    517                     errln("expected %g but got %g", expectedVal, val);
    518                     ok = FALSE;
    519                     break;
    520                 }
    521                 if (*ep == ',') ++ep;
    522             }
    523 
    524             if (ok && count != -1) {
    525                 if (!(*ep == 0 || *ep == ';')) {
    526                     errln("didn't get expected value: %s", ep);
    527                     ok = FALSE;
    528                 }
    529             }
    530 
    531             while (*ep && *ep != ';') ++ep;
    532             if (*ep == ';') ++ep;
    533             rp = ep;
    534         }
    535         delete p;
    536     }
    537 }
    538 
    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")) {
    543         return;
    544     }
    545     UnicodeString keyword = pr->select(2.);
    546     if (keyword != UNICODE_STRING("two", 3)) {
    547         dataerrln("PluralRules(en-ordinal).select(2) failed");
    548     }
    549 }
    550 
    551 #endif /* #if !UCONFIG_NO_FORMATTING */
    552