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