| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 ******************************************************************************* | |
| 3 * Copyright (C) 2007-2014, 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> | |
| 17 #include <stdarg.h> | |
| 18 #include <string.h> | |
| 19 | |
| 20 #include "unicode/localpointer.h" | |
| 21 #include "unicode/plurrule.h" | |
| 22 #include "unicode/stringpiece.h" | |
| 23 | |
| 24 #include "cmemory.h" | |
| 25 #include "digitlst.h" | |
| 26 #include "plurrule_impl.h" | |
| 27 #include "plurults.h" | |
| 28 #include "uhash.h" | |
| 29 | |
| 30 void setupResult(const int32_t testSource[], char result[], int32_t* max); | |
| 31 UBool checkEqual(const PluralRules &test, char *result, int32_t max); | |
| 32 UBool testEquality(const PluralRules &test); | |
| 33 | |
| 34 // This is an API test, not a unit test. It doesn't test very many cases, and d
oesn't | |
| 35 // try to test the full functionality. It just calls each function in the class
and | |
| 36 // verifies that it works on a basic level. | |
| 37 | |
| 38 void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
me, char* /*par*/ ) | |
| 39 { | |
| 40 if (exec) logln("TestSuite PluralRulesAPI"); | |
| 41 TESTCASE_AUTO_BEGIN; | |
| 42 TESTCASE_AUTO(testAPI); | |
| 43 // TESTCASE_AUTO(testGetUniqueKeywordValue); | |
| 44 TESTCASE_AUTO(testGetSamples); | |
| 45 TESTCASE_AUTO(testWithin); | |
| 46 TESTCASE_AUTO(testGetAllKeywordValues); | |
| 47 TESTCASE_AUTO(testOrdinal); | |
| 48 TESTCASE_AUTO(testSelect); | |
| 49 TESTCASE_AUTO(testAvailbleLocales); | |
| 50 TESTCASE_AUTO(testParseErrors); | |
| 51 TESTCASE_AUTO(testFixedDecimal); | |
| 52 TESTCASE_AUTO_END; | |
| 53 } | |
| 54 | |
| 55 | |
| 56 // Quick and dirty class for putting UnicodeStrings in char * messages. | |
| 57 // TODO: something like this should be generally available. | |
| 58 class US { | |
| 59 private: | |
| 60 char *buf; | |
| 61 public: | |
| 62 US(const UnicodeString &us) { | |
| 63 int32_t bufLen = us.extract((int32_t)0, us.length(), (char *)NULL, (uint3
2_t)0) + 1; | |
| 64 buf = (char *)uprv_malloc(bufLen); | |
| 65 us.extract(0, us.length(), buf, bufLen); }; | |
| 66 const char *cstr() {return buf;}; | |
| 67 ~US() { uprv_free(buf);}; | |
| 68 }; | |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 #define PLURAL_TEST_NUM 18 | |
| 75 /** | |
| 76 * Test various generic API methods of PluralRules for API coverage. | |
| 77 */ | |
| 78 void PluralRulesTest::testAPI(/*char *par*/) | |
| 79 { | |
| 80 UnicodeString pluralTestData[PLURAL_TEST_NUM] = { | |
| 81 UNICODE_STRING_SIMPLE("a: n is 1"), | |
| 82 UNICODE_STRING_SIMPLE("a: n mod 10 is 2"), | |
| 83 UNICODE_STRING_SIMPLE("a: n is not 1"), | |
| 84 UNICODE_STRING_SIMPLE("a: n mod 3 is not 1"), | |
| 85 UNICODE_STRING_SIMPLE("a: n in 2..5"), | |
| 86 UNICODE_STRING_SIMPLE("a: n within 2..5"), | |
| 87 UNICODE_STRING_SIMPLE("a: n not in 2..5"), | |
| 88 UNICODE_STRING_SIMPLE("a: n not within 2..5"), | |
| 89 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..5"), | |
| 90 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..5"), | |
| 91 UNICODE_STRING_SIMPLE("a: n mod 10 is 2 and n is not 12"), | |
| 92 UNICODE_STRING_SIMPLE("a: n mod 10 in 2..3 or n mod 10 is 5"), | |
| 93 UNICODE_STRING_SIMPLE("a: n mod 10 within 2..3 or n mod 10 is 5"), | |
| 94 UNICODE_STRING_SIMPLE("a: n is 1 or n is 4 or n is 23"), | |
| 95 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n in 1..11
"), | |
| 96 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n within 1
..11"), | |
| 97 UNICODE_STRING_SIMPLE("a: n mod 2 is 1 or n mod 5 is 1 and n is not
6"), | |
| 98 "", | |
| 99 }; | |
| 100 static const int32_t pluralTestResult[PLURAL_TEST_NUM][30] = { | |
| 101 {1, 0}, | |
| 102 {2,12,22, 0}, | |
| 103 {0,2,3,4,5,0}, | |
| 104 {0,2,3,5,6,8,9,0}, | |
| 105 {2,3,4,5,0}, | |
| 106 {2,3,4,5,0}, | |
| 107 {0,1,6,7,8, 0}, | |
| 108 {0,1,6,7,8, 0}, | |
| 109 {2,3,4,5,12,13,14,15,22,23,24,25,0}, | |
| 110 {2,3,4,5,12,13,14,15,22,23,24,25,0}, | |
| 111 {2,22,32,42,0}, | |
| 112 {2,3,5,12,13,15,22,23,25,0}, | |
| 113 {2,3,5,12,13,15,22,23,25,0}, | |
| 114 {1,4,23,0}, | |
| 115 {1,5,7,9,11,0}, | |
| 116 {1,5,7,9,11,0}, | |
| 117 {1,3,5,7,9,11,13,15,16,0}, | |
| 118 }; | |
| 119 UErrorCode status = U_ZERO_ERROR; | |
| 120 | |
| 121 // ======= Test constructors | |
| 122 logln("Testing PluralRules constructors"); | |
| 123 | |
| 124 | |
| 125 logln("\n start default locale test case ..\n"); | |
| 126 | |
| 127 PluralRules defRule(status); | |
| 128 LocalPointer<PluralRules> test(new PluralRules(status), status); | |
| 129 if(U_FAILURE(status)) { | |
| 130 dataerrln("ERROR: Could not create PluralRules (default) - exitting"); | |
| 131 return; | |
| 132 } | |
| 133 LocalPointer<PluralRules> newEnPlural(test->forLocale(Locale::getEnglish(),
status), status); | |
| 134 if(U_FAILURE(status)) { | |
| 135 dataerrln("ERROR: Could not create PluralRules (English) - exitting"); | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 // ======= Test clone, assignment operator && == operator. | |
| 140 LocalPointer<PluralRules> dupRule(defRule.clone()); | |
| 141 if (dupRule==NULL) { | |
| 142 errln("ERROR: clone plural rules test failed!"); | |
| 143 return; | |
| 144 } else { | |
| 145 if ( *dupRule != defRule ) { | |
| 146 errln("ERROR: clone plural rules test failed!"); | |
| 147 } | |
| 148 } | |
| 149 *dupRule = *newEnPlural; | |
| 150 if (dupRule!=NULL) { | |
| 151 if ( *dupRule != *newEnPlural ) { | |
| 152 errln("ERROR: clone plural rules test failed!"); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 // ======= Test empty plural rules | |
| 157 logln("Testing Simple PluralRules"); | |
| 158 | |
| 159 LocalPointer<PluralRules> empRule(test->createRules(UNICODE_STRING_SIMPLE("a
:n"), status)); | |
| 160 UnicodeString key; | |
| 161 for (int32_t i=0; i<10; ++i) { | |
| 162 key = empRule->select(i); | |
| 163 if ( key.charAt(0)!= 0x61 ) { // 'a' | |
| 164 errln("ERROR: empty plural rules test failed! - exitting"); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 // ======= Test simple plural rules | |
| 169 logln("Testing Simple PluralRules"); | |
| 170 | |
| 171 char result[100]; | |
| 172 int32_t max; | |
| 173 | |
| 174 for (int32_t i=0; i<PLURAL_TEST_NUM-1; ++i) { | |
| 175 LocalPointer<PluralRules> newRules(test->createRules(pluralTestData[i], s
tatus)); | |
| 176 setupResult(pluralTestResult[i], result, &max); | |
| 177 if ( !checkEqual(*newRules, result, max) ) { | |
| 178 errln("ERROR: simple plural rules failed! - exitting"); | |
| 179 return; | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 // ======= Test complex plural rules | |
| 184 logln("Testing Complex PluralRules"); | |
| 185 // TODO: the complex test data is hard coded. It's better to implement | |
| 186 // a parser to parse the test data. | |
| 187 UnicodeString complexRule = UNICODE_STRING_SIMPLE("a: n in 2..5; b: n in 5..
8; c: n mod 2 is 1"); | |
| 188 UnicodeString complexRule2 = UNICODE_STRING_SIMPLE("a: n within 2..5; b: n w
ithin 5..8; c: n mod 2 is 1"); | |
| 189 char cRuleResult[] = | |
| 190 { | |
| 191 0x6F, // 'o' | |
| 192 0x63, // 'c' | |
| 193 0x61, // 'a' | |
| 194 0x61, // 'a' | |
| 195 0x61, // 'a' | |
| 196 0x61, // 'a' | |
| 197 0x62, // 'b' | |
| 198 0x62, // 'b' | |
| 199 0x62, // 'b' | |
| 200 0x63, // 'c' | |
| 201 0x6F, // 'o' | |
| 202 0x63 // 'c' | |
| 203 }; | |
| 204 LocalPointer<PluralRules> newRules(test->createRules(complexRule, status)); | |
| 205 if ( !checkEqual(*newRules, cRuleResult, 12) ) { | |
| 206 errln("ERROR: complex plural rules failed! - exitting"); | |
| 207 return; | |
| 208 } | |
| 209 newRules.adoptInstead(test->createRules(complexRule2, status)); | |
| 210 if ( !checkEqual(*newRules, cRuleResult, 12) ) { | |
| 211 errln("ERROR: complex plural rules failed! - exitting"); | |
| 212 return; | |
| 213 } | |
| 214 | |
| 215 // ======= Test decimal fractions plural rules | |
| 216 UnicodeString decimalRule= UNICODE_STRING_SIMPLE("a: n not in 0..100;"); | |
| 217 UnicodeString KEYWORD_A = UNICODE_STRING_SIMPLE("a"); | |
| 218 status = U_ZERO_ERROR; | |
| 219 newRules.adoptInstead(test->createRules(decimalRule, status)); | |
| 220 if (U_FAILURE(status)) { | |
| 221 dataerrln("ERROR: Could not create PluralRules for testing fractions - e
xitting"); | |
| 222 return; | |
| 223 } | |
| 224 double fData[] = {-101, -100, -1, -0.0, 0, 0.1, 1, 1.999,
2.0, 100, 100.001 }; | |
| 225 UBool isKeywordA[] = {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE,
FALSE, FALSE, TRUE }; | |
| 226 for (int32_t i=0; i<UPRV_LENGTHOF(fData); i++) { | |
| 227 if ((newRules->select(fData[i])== KEYWORD_A) != isKeywordA[i]) { | |
| 228 errln("File %s, Line %d, ERROR: plural rules for decimal fractions
test failed!\n" | |
| 229 " number = %g, expected %s", __FILE__, __LINE__, fData[i], i
sKeywordA[i]?"TRUE":"FALSE"); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 // ======= Test Equality | |
| 234 logln("Testing Equality of PluralRules"); | |
| 235 | |
| 236 if ( !testEquality(*test) ) { | |
| 237 errln("ERROR: complex plural rules failed! - exitting"); | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 | |
| 242 // ======= Test getStaticClassID() | |
| 243 logln("Testing getStaticClassID()"); | |
| 244 | |
| 245 if(test->getDynamicClassID() != PluralRules::getStaticClassID()) { | |
| 246 errln("ERROR: getDynamicClassID() didn't return the expected value"); | |
| 247 } | |
| 248 // ====== Test fallback to parent locale | |
| 249 LocalPointer<PluralRules> en_UK(test->forLocale(Locale::getUK(), status)); | |
| 250 LocalPointer<PluralRules> en(test->forLocale(Locale::getEnglish(), status)); | |
| 251 if (en_UK.isValid() && en.isValid()) { | |
| 252 if ( *en_UK != *en ) { | |
| 253 errln("ERROR: test locale fallback failed!"); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 LocalPointer<PluralRules> zh_Hant(test->forLocale(Locale::getTaiwan(), statu
s)); | |
| 258 LocalPointer<PluralRules> zh(test->forLocale(Locale::getChinese(), status)); | |
| 259 if (zh_Hant.isValid() && zh.isValid()) { | |
| 260 if ( *zh_Hant != *zh ) { | |
| 261 errln("ERROR: test locale fallback failed!"); | |
| 262 } | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 void setupResult(const int32_t testSource[], char result[], int32_t* max) { | |
| 267 int32_t i=0; | |
| 268 int32_t curIndex=0; | |
| 269 | |
| 270 do { | |
| 271 while (curIndex < testSource[i]) { | |
| 272 result[curIndex++]=0x6F; //'o' other | |
| 273 } | |
| 274 result[curIndex++]=0x61; // 'a' | |
| 275 | |
| 276 } while(testSource[++i]>0); | |
| 277 *max=curIndex; | |
| 278 } | |
| 279 | |
| 280 | |
| 281 UBool checkEqual(const PluralRules &test, char *result, int32_t max) { | |
| 282 UnicodeString key; | |
| 283 UBool isEqual = TRUE; | |
| 284 for (int32_t i=0; i<max; ++i) { | |
| 285 key= test.select(i); | |
| 286 if ( key.charAt(0)!=result[i] ) { | |
| 287 isEqual = FALSE; | |
| 288 } | |
| 289 } | |
| 290 return isEqual; | |
| 291 } | |
| 292 | |
| 293 | |
| 294 | |
| 295 static const int32_t MAX_EQ_ROW = 2; | |
| 296 static const int32_t MAX_EQ_COL = 5; | |
| 297 UBool testEquality(const 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], stat
us); | |
| 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 // TODO: UniqueKeywordValue() is not currently supported. | |
| 364 // If it never will be, this test code should be removed. | |
| 365 void PluralRulesTest::testGetUniqueKeywordValue() { | |
| 366 assertRuleValue("n is 1", 1); | |
| 367 assertRuleValue("n in 2..2", 2); | |
| 368 assertRuleValue("n within 2..2", 2); | |
| 369 assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE); | |
| 370 assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE); | |
| 371 assertRuleValue("n is 2 or n is 2", 2); | |
| 372 assertRuleValue("n is 2 and n is 2", 2); | |
| 373 assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE); | |
| 374 assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE); | |
| 375 assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE); | |
| 376 assertRuleValue("n is 2 and n in 2..3", 2); | |
| 377 assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE); // k
ey not defined | |
| 378 assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key mat
ches default rule | |
| 379 } | |
| 380 | |
| 381 void PluralRulesTest::testGetSamples() { | |
| 382 // TODO: fix samples, re-enable this test. | |
| 383 | |
| 384 // no get functional equivalent API in ICU4C, so just | |
| 385 // test every locale... | |
| 386 UErrorCode status = U_ZERO_ERROR; | |
| 387 int32_t numLocales; | |
| 388 const Locale* locales = Locale::getAvailableLocales(numLocales); | |
| 389 | |
| 390 double values[1000]; | |
| 391 for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) { | |
| 392 PluralRules *rules = PluralRules::forLocale(locales[i], status); | |
| 393 if (U_FAILURE(status)) { | |
| 394 break; | |
| 395 } | |
| 396 StringEnumeration *keywords = rules->getKeywords(status); | |
| 397 if (U_FAILURE(status)) { | |
| 398 delete rules; | |
| 399 break; | |
| 400 } | |
| 401 const UnicodeString* keyword; | |
| 402 while (NULL != (keyword = keywords->snext(status))) { | |
| 403 int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values),
status); | |
| 404 if (U_FAILURE(status)) { | |
| 405 errln(UNICODE_STRING_SIMPLE("getSamples() failed for locale ") + | |
| 406 locales[i].getName() + | |
| 407 UNICODE_STRING_SIMPLE(", keyword ") + *keyword); | |
| 408 continue; | |
| 409 } | |
| 410 if (count == 0) { | |
| 411 // TODO: Lots of these. | |
| 412 // errln(UNICODE_STRING_SIMPLE("no samples for keyword ") + *keyword +
UNICODE_STRING_SIMPLE(" in locale ") + locales[i].getName() ); | |
| 413 } | |
| 414 if (count > UPRV_LENGTHOF(values)) { | |
| 415 errln(UNICODE_STRING_SIMPLE("getSamples()=") + count + | |
| 416 UNICODE_STRING_SIMPLE(", too many values, for locale ") + | |
| 417 locales[i].getName() + | |
| 418 UNICODE_STRING_SIMPLE(", keyword ") + *keyword); | |
| 419 count = UPRV_LENGTHOF(values); | |
| 420 } | |
| 421 for (int32_t j = 0; j < count; ++j) { | |
| 422 if (values[j] == UPLRULES_NO_UNIQUE_VALUE) { | |
| 423 errln("got 'no unique value' among values"); | |
| 424 } else { | |
| 425 UnicodeString resultKeyword = rules->select(values[j]); | |
| 426 // if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only. | |
| 427 // std::cout << " uk " << US(resultKeyword).cstr() << " " << valu
es[j] << std::endl; | |
| 428 // } | |
| 429 if (*keyword != resultKeyword) { | |
| 430 errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %g,
select(%g) returns keyword \"%s\"", | |
| 431 __FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), v
alues[j], values[j], US(resultKeyword).cstr()); | |
| 432 } | |
| 433 } | |
| 434 } | |
| 435 } | |
| 436 delete keywords; | |
| 437 delete rules; | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 void PluralRulesTest::testWithin() { | |
| 442 // goes to show you what lack of testing will do. | |
| 443 // of course, this has been broken for two years and no one has noticed... | |
| 444 UErrorCode status = U_ZERO_ERROR; | |
| 445 PluralRules *rules = PluralRules::createRules("a: n mod 10 in 5..8", status); | |
| 446 if (!rules) { | |
| 447 errln("couldn't instantiate rules"); | |
| 448 return; | |
| 449 } | |
| 450 | |
| 451 UnicodeString keyword = rules->select((int32_t)26); | |
| 452 if (keyword != "a") { | |
| 453 errln("expected 'a' for 26 but didn't get it."); | |
| 454 } | |
| 455 | |
| 456 keyword = rules->select(26.5); | |
| 457 if (keyword != "other") { | |
| 458 errln("expected 'other' for 26.5 but didn't get it."); | |
| 459 } | |
| 460 | |
| 461 delete rules; | |
| 462 } | |
| 463 | |
| 464 void | |
| 465 PluralRulesTest::testGetAllKeywordValues() { | |
| 466 const char* data[] = { | |
| 467 "a: n in 2..5", "a: 2,3,4,5; other: null; b:", | |
| 468 "a: n not in 2..5", "a: null; other: null", | |
| 469 "a: n within 2..5", "a: null; other: null", | |
| 470 "a: n not within 2..5", "a: null; other: null", | |
| 471 "a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on ou
t, always null | |
| 472 "a: n in 2..5 and n within 6..8", "a:", | |
| 473 "a: n in 2..5 and n within 5..8", "a: 5", | |
| 474 "a: n within 2..5 and n within 6..8", "a:", // our sampling catches thes
e | |
| 475 "a: n within 2..5 and n within 5..8", "a: 5", // '' | |
| 476 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5",
"a: 2,4", | |
| 477 "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 " | |
| 478 "or n within 5..6 and n within 6..7", "a: null", // but not this... | |
| 479 "a: n mod 3 is 0", "a: null", | |
| 480 "a: n mod 3 is 0 and n within 1..2", "a:", | |
| 481 "a: n mod 3 is 0 and n within 0..5", "a: 0,3", | |
| 482 "a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, w
e don't catch... | |
| 483 "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12", | |
| 484 NULL | |
| 485 }; | |
| 486 | |
| 487 for (int i = 0; data[i] != NULL; i += 2) { | |
| 488 UErrorCode status = U_ZERO_ERROR; | |
| 489 UnicodeString ruleDescription(data[i], -1, US_INV); | |
| 490 const char* result = data[i+1]; | |
| 491 | |
| 492 logln("[%d] %s", i >> 1, data[i]); | |
| 493 | |
| 494 PluralRules *p = PluralRules::createRules(ruleDescription, status); | |
| 495 if (p == NULL || U_FAILURE(status)) { | |
| 496 errln("file %s, line %d: could not create rules from '%s'\n" | |
| 497 " ErrorCode: %s\n", | |
| 498 __FILE__, __LINE__, data[i], u_errorName(status)); | |
| 499 continue; | |
| 500 } | |
| 501 | |
| 502 // TODO: fix samples implementation, re-enable test. | |
| 503 (void)result; | |
| 504 #if 0 | |
| 505 | |
| 506 const char* rp = result; | |
| 507 while (*rp) { | |
| 508 while (*rp == ' ') ++rp; | |
| 509 if (!rp) { | |
| 510 break; | |
| 511 } | |
| 512 | |
| 513 const char* ep = rp; | |
| 514 while (*ep && *ep != ':') ++ep; | |
| 515 | |
| 516 status = U_ZERO_ERROR; | |
| 517 UnicodeString keyword(rp, ep - rp, US_INV); | |
| 518 double samples[4]; // no test above should have more samples than 4 | |
| 519 int32_t count = p->getAllKeywordValues(keyword, &samples[0], 4, stat
us); | |
| 520 if (U_FAILURE(status)) { | |
| 521 errln("error getting samples for %s", rp); | |
| 522 break; | |
| 523 } | |
| 524 | |
| 525 if (count > 4) { | |
| 526 errln("count > 4 for keyword %s", rp); | |
| 527 count = 4; | |
| 528 } | |
| 529 | |
| 530 if (*ep) { | |
| 531 ++ep; // skip colon | |
| 532 while (*ep && *ep == ' ') ++ep; // and spaces | |
| 533 } | |
| 534 | |
| 535 UBool ok = TRUE; | |
| 536 if (count == -1) { | |
| 537 if (*ep != 'n') { | |
| 538 errln("expected values for keyword %s but got -1 (%s)", rp,
ep); | |
| 539 ok = FALSE; | |
| 540 } | |
| 541 } else if (*ep == 'n') { | |
| 542 errln("expected count of -1, got %d, for keyword %s (%s)", count
, rp, ep); | |
| 543 ok = FALSE; | |
| 544 } | |
| 545 | |
| 546 // We'll cheat a bit here. The samples happend to be in order and s
o are our | |
| 547 // expected values, so we'll just test in order until a failure. If
the | |
| 548 // implementation changes to return samples in an arbitrary order, t
his test | |
| 549 // must change. There's no actual restriction on the order of the s
amples. | |
| 550 | |
| 551 for (int j = 0; ok && j < count; ++j ) { // we've verified count < 4 | |
| 552 double val = samples[j]; | |
| 553 if (*ep == 0 || *ep == ';') { | |
| 554 errln("got unexpected value[%d]: %g", j, val); | |
| 555 ok = FALSE; | |
| 556 break; | |
| 557 } | |
| 558 char* xp; | |
| 559 double expectedVal = strtod(ep, &xp); | |
| 560 if (xp == ep) { | |
| 561 // internal error | |
| 562 errln("yikes!"); | |
| 563 ok = FALSE; | |
| 564 break; | |
| 565 } | |
| 566 ep = xp; | |
| 567 if (expectedVal != val) { | |
| 568 errln("expected %g but got %g", expectedVal, val); | |
| 569 ok = FALSE; | |
| 570 break; | |
| 571 } | |
| 572 if (*ep == ',') ++ep; | |
| 573 } | |
| 574 | |
| 575 if (ok && count != -1) { | |
| 576 if (!(*ep == 0 || *ep == ';')) { | |
| 577 errln("file: %s, line %d, didn't get expected value: %s", __
FILE__, __LINE__, ep); | |
| 578 ok = FALSE; | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 while (*ep && *ep != ';') ++ep; | |
| 583 if (*ep == ';') ++ep; | |
| 584 rp = ep; | |
| 585 } | |
| 586 #endif | |
| 587 delete p; | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 void PluralRulesTest::testOrdinal() { | |
| 592 IcuTestErrorCode errorCode(*this, "testOrdinal"); | |
| 593 LocalPointer<PluralRules> pr(PluralRules::forLocale("en", UPLURAL_TYPE_ORDIN
AL, errorCode)); | |
| 594 if (errorCode.logIfFailureAndReset("PluralRules::forLocale(en, UPLURAL_TYPE_
ORDINAL) failed")) { | |
| 595 return; | |
| 596 } | |
| 597 UnicodeString keyword = pr->select(2.); | |
| 598 if (keyword != UNICODE_STRING("two", 3)) { | |
| 599 dataerrln("PluralRules(en-ordinal).select(2) failed"); | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 | |
| 604 static const char * END_MARK = "999.999"; // Mark end of varargs data. | |
| 605 | |
| 606 void PluralRulesTest::checkSelect(const LocalPointer<PluralRules> &rules, UError
Code &status, | |
| 607 int32_t line, const char *keyword, ...) { | |
| 608 // The varargs parameters are a const char* strings, each being a decimal nu
mber. | |
| 609 // The formatting of the numbers as strings is significant, e.g. | |
| 610 // the difference between "2" and "2.0" can affect which rule matches (w
hich keyword is selected). | |
| 611 // Note: rules parameter is a LocalPointer reference rather than a PluralRul
es * to avoid having | |
| 612 // to write getAlias() at every (numerous) call site. | |
| 613 | |
| 614 if (U_FAILURE(status)) { | |
| 615 errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_error
Name(status)); | |
| 616 status = U_ZERO_ERROR; | |
| 617 return; | |
| 618 } | |
| 619 | |
| 620 if (rules == NULL) { | |
| 621 errln("file %s, line %d: rules pointer is NULL", __FILE__, line); | |
| 622 return; | |
| 623 } | |
| 624 | |
| 625 va_list ap; | |
| 626 va_start(ap, keyword); | |
| 627 for (;;) { | |
| 628 const char *num = va_arg(ap, const char *); | |
| 629 if (strcmp(num, END_MARK) == 0) { | |
| 630 break; | |
| 631 } | |
| 632 | |
| 633 // DigitList is a convenient way to parse the decimal number string and
get a double. | |
| 634 DigitList dl; | |
| 635 dl.set(StringPiece(num), status); | |
| 636 if (U_FAILURE(status)) { | |
| 637 errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_e
rrorName(status)); | |
| 638 status = U_ZERO_ERROR; | |
| 639 continue; | |
| 640 } | |
| 641 double numDbl = dl.getDouble(); | |
| 642 const char *decimalPoint = strchr(num, '.'); | |
| 643 int fractionDigitCount = decimalPoint == NULL ? 0 : (num + strlen(num) -
1) - decimalPoint; | |
| 644 int fractionDigits = fractionDigitCount == 0 ? 0 : atoi(decimalPoint + 1
); | |
| 645 FixedDecimal ni(numDbl, fractionDigitCount, fractionDigits); | |
| 646 | |
| 647 UnicodeString actualKeyword = rules->select(ni); | |
| 648 if (actualKeyword != UnicodeString(keyword)) { | |
| 649 errln("file %s, line %d, select(%s) returned incorrect keyword. Expe
cted %s, got %s", | |
| 650 __FILE__, line, num, keyword, US(actualKeyword).cstr()); | |
| 651 } | |
| 652 } | |
| 653 va_end(ap); | |
| 654 } | |
| 655 | |
| 656 void PluralRulesTest::testSelect() { | |
| 657 UErrorCode status = U_ZERO_ERROR; | |
| 658 LocalPointer<PluralRules> pr(PluralRules::createRules("s: n in 1,3,4,6", sta
tus)); | |
| 659 checkSelect(pr, status, __LINE__, "s", "1.0", "3.0", "4.0", "6.0", END_MARK)
; | |
| 660 checkSelect(pr, status, __LINE__, "other", "0.0", "2.0", "3.1", "7.0", END_M
ARK); | |
| 661 | |
| 662 pr.adoptInstead(PluralRules::createRules("s: n not in 1,3,4,6", status)); | |
| 663 checkSelect(pr, status, __LINE__, "other", "1.0", "3.0", "4.0", "6.0", END_M
ARK); | |
| 664 checkSelect(pr, status, __LINE__, "s", "0.0", "2.0", "3.1", "7.0", END_MARK)
; | |
| 665 | |
| 666 pr.adoptInstead(PluralRules::createRules("r: n in 1..4, 7..10, 14 .. 17;" | |
| 667 "s: n is 29;", status)); | |
| 668 checkSelect(pr, status, __LINE__, "r", "1.0", "3.0", "7.0", "8.0", "10.0", "
14.0", "17.0", END_MARK); | |
| 669 checkSelect(pr, status, __LINE__, "s", "29.0", END_MARK); | |
| 670 checkSelect(pr, status, __LINE__, "other", "28.0", "29.1", END_MARK); | |
| 671 | |
| 672 pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 1; b: n mod 100 is
0 ", status)); | |
| 673 checkSelect(pr, status, __LINE__, "a", "1", "11", "41", "101", "301.00", END
_MARK); | |
| 674 checkSelect(pr, status, __LINE__, "b", "0", "100", "200.0", "300.", "1000",
"1100", "110000", END_MARK); | |
| 675 checkSelect(pr, status, __LINE__, "other", "0.01", "1.01", "0.99", "2", "3",
"99", "102", END_MARK); | |
| 676 | |
| 677 // Rules that end with or without a ';' and with or without trailing spaces. | |
| 678 // (There was a rule parser bug here with these.) | |
| 679 pr.adoptInstead(PluralRules::createRules("a: n is 1", status)); | |
| 680 checkSelect(pr, status, __LINE__, "a", "1", END_MARK); | |
| 681 checkSelect(pr, status, __LINE__, "other", "2", END_MARK); | |
| 682 | |
| 683 pr.adoptInstead(PluralRules::createRules("a: n is 1 ", status)); | |
| 684 checkSelect(pr, status, __LINE__, "a", "1", END_MARK); | |
| 685 checkSelect(pr, status, __LINE__, "other", "2", END_MARK); | |
| 686 | |
| 687 pr.adoptInstead(PluralRules::createRules("a: n is 1;", status)); | |
| 688 checkSelect(pr, status, __LINE__, "a", "1", END_MARK); | |
| 689 checkSelect(pr, status, __LINE__, "other", "2", END_MARK); | |
| 690 | |
| 691 pr.adoptInstead(PluralRules::createRules("a: n is 1 ; ", status)); | |
| 692 checkSelect(pr, status, __LINE__, "a", "1", END_MARK); | |
| 693 checkSelect(pr, status, __LINE__, "other", "2", END_MARK); | |
| 694 | |
| 695 // First match when rules for different keywords are not disjoint. | |
| 696 // Also try spacing variations around ':' and '..' | |
| 697 pr.adoptInstead(PluralRules::createRules("c: n in 5..15; b : n in 1..10 ;a:
n in 10 .. 20", status)); | |
| 698 checkSelect(pr, status, __LINE__, "a", "20", END_MARK); | |
| 699 checkSelect(pr, status, __LINE__, "b", "1", END_MARK); | |
| 700 checkSelect(pr, status, __LINE__, "c", "10", END_MARK); | |
| 701 checkSelect(pr, status, __LINE__, "other", "0", "21", "10.1", END_MARK); | |
| 702 | |
| 703 // in vs within | |
| 704 pr.adoptInstead(PluralRules::createRules("a: n in 2..10; b: n within 8..15",
status)); | |
| 705 checkSelect(pr, status, __LINE__, "a", "2", "8", "10", END_MARK); | |
| 706 checkSelect(pr, status, __LINE__, "b", "8.01", "9.5", "11", "14.99", "15", E
ND_MARK); | |
| 707 checkSelect(pr, status, __LINE__, "other", "1", "7.7", "15.01", "16", END_MA
RK); | |
| 708 | |
| 709 // OR and AND chains. | |
| 710 pr.adoptInstead(PluralRules::createRules("a: n in 2..10 and n in 4..12 and n
not in 5..7", status)); | |
| 711 checkSelect(pr, status, __LINE__, "a", "4", "8", "9", "10", END_MARK); | |
| 712 checkSelect(pr, status, __LINE__, "other", "2", "3", "5", "7", "11", END_MAR
K); | |
| 713 pr.adoptInstead(PluralRules::createRules("a: n is 2 or n is 5 or n in 7..11
and n in 11..13", status)); | |
| 714 checkSelect(pr, status, __LINE__, "a", "2", "5", "11", END_MARK); | |
| 715 checkSelect(pr, status, __LINE__, "other", "3", "4", "6", "8", "10", "12", "
13", END_MARK); | |
| 716 | |
| 717 // Number attributes - | |
| 718 // n: the number itself | |
| 719 // i: integer digits | |
| 720 // f: visible fraction digits | |
| 721 // t: f with trailing zeros removed. | |
| 722 // v: number of visible fraction digits | |
| 723 // j: = n if there are no visible fraction digits | |
| 724 // != anything if there are visible fraction digits | |
| 725 | |
| 726 pr.adoptInstead(PluralRules::createRules("a: i is 123", status)); | |
| 727 checkSelect(pr, status, __LINE__, "a", "123", "123.0", "123.1", "0123.99", E
ND_MARK); | |
| 728 checkSelect(pr, status, __LINE__, "other", "124", "122.0", END_MARK); | |
| 729 | |
| 730 pr.adoptInstead(PluralRules::createRules("a: f is 120", status)); | |
| 731 checkSelect(pr, status, __LINE__, "a", "1.120", "0.120", "11123.120", "0123.
120", END_MARK); | |
| 732 checkSelect(pr, status, __LINE__, "other", "1.121", "122.1200", "1.12", "120
", END_MARK); | |
| 733 | |
| 734 pr.adoptInstead(PluralRules::createRules("a: t is 12", status)); | |
| 735 checkSelect(pr, status, __LINE__, "a", "1.120", "0.12", "11123.12000", "0123
.1200000", END_MARK); | |
| 736 checkSelect(pr, status, __LINE__, "other", "1.121", "122.1200001", "1.11", "
12", END_MARK); | |
| 737 | |
| 738 pr.adoptInstead(PluralRules::createRules("a: v is 3", status)); | |
| 739 checkSelect(pr, status, __LINE__, "a", "1.120", "0.000", "11123.100", "0123.
124", ".666", END_MARK); | |
| 740 checkSelect(pr, status, __LINE__, "other", "1.1212", "122.12", "1.1", "122",
"0.0000", END_MARK); | |
| 741 | |
| 742 pr.adoptInstead(PluralRules::createRules("a: v is 0 and i is 123", status)); | |
| 743 checkSelect(pr, status, __LINE__, "a", "123", "123.", END_MARK); | |
| 744 checkSelect(pr, status, __LINE__, "other", "123.0", "123.1", "123.123", "0.1
23", END_MARK); | |
| 745 | |
| 746 // The reserved words from the rule syntax will also function as keywords. | |
| 747 pr.adoptInstead(PluralRules::createRules("a: n is 21; n: n is 22; i: n is 23
; f: n is 24;" | |
| 748 "t: n is 25; v: n is 26; w: n is 27
; j: n is 28" | |
| 749 , status)); | |
| 750 checkSelect(pr, status, __LINE__, "other", "20", "29", END_MARK); | |
| 751 checkSelect(pr, status, __LINE__, "a", "21", END_MARK); | |
| 752 checkSelect(pr, status, __LINE__, "n", "22", END_MARK); | |
| 753 checkSelect(pr, status, __LINE__, "i", "23", END_MARK); | |
| 754 checkSelect(pr, status, __LINE__, "f", "24", END_MARK); | |
| 755 checkSelect(pr, status, __LINE__, "t", "25", END_MARK); | |
| 756 checkSelect(pr, status, __LINE__, "v", "26", END_MARK); | |
| 757 checkSelect(pr, status, __LINE__, "w", "27", END_MARK); | |
| 758 checkSelect(pr, status, __LINE__, "j", "28", END_MARK); | |
| 759 | |
| 760 | |
| 761 pr.adoptInstead(PluralRules::createRules("not: n=31; and: n=32; or: n=33; mo
d: n=34;" | |
| 762 "in: n=35; within: n=36;is:n=37" | |
| 763 , status)); | |
| 764 checkSelect(pr, status, __LINE__, "other", "30", "39", END_MARK); | |
| 765 checkSelect(pr, status, __LINE__, "not", "31", END_MARK); | |
| 766 checkSelect(pr, status, __LINE__, "and", "32", END_MARK); | |
| 767 checkSelect(pr, status, __LINE__, "or", "33", END_MARK); | |
| 768 checkSelect(pr, status, __LINE__, "mod", "34", END_MARK); | |
| 769 checkSelect(pr, status, __LINE__, "in", "35", END_MARK); | |
| 770 checkSelect(pr, status, __LINE__, "within", "36", END_MARK); | |
| 771 checkSelect(pr, status, __LINE__, "is", "37", END_MARK); | |
| 772 | |
| 773 // Test cases from ICU4J PluralRulesTest.parseTestData | |
| 774 | |
| 775 pr.adoptInstead(PluralRules::createRules("a: n is 1", status)); | |
| 776 checkSelect(pr, status, __LINE__, "a", "1", END_MARK); | |
| 777 pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 2", status)); | |
| 778 checkSelect(pr, status, __LINE__, "a", "2", "12", "22", END_MARK); | |
| 779 pr.adoptInstead(PluralRules::createRules("a: n is not 1", status)); | |
| 780 checkSelect(pr, status, __LINE__, "a", "0", "2", "3", "4", "5", END_MARK); | |
| 781 pr.adoptInstead(PluralRules::createRules("a: n mod 3 is not 1", status)); | |
| 782 checkSelect(pr, status, __LINE__, "a", "0", "2", "3", "5", "6", "8", "9", EN
D_MARK); | |
| 783 pr.adoptInstead(PluralRules::createRules("a: n in 2..5", status)); | |
| 784 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK); | |
| 785 pr.adoptInstead(PluralRules::createRules("a: n within 2..5", status)); | |
| 786 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK); | |
| 787 pr.adoptInstead(PluralRules::createRules("a: n not in 2..5", status)); | |
| 788 checkSelect(pr, status, __LINE__, "a", "0", "1", "6", "7", "8", END_MARK); | |
| 789 pr.adoptInstead(PluralRules::createRules("a: n not within 2..5", status)); | |
| 790 checkSelect(pr, status, __LINE__, "a", "0", "1", "6", "7", "8", END_MARK); | |
| 791 pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2..5", status)); | |
| 792 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "12", "13", "14",
"15", "22", "23", "24", "25", END_MARK); | |
| 793 pr.adoptInstead(PluralRules::createRules("a: n mod 10 within 2..5", status))
; | |
| 794 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "12", "13", "14",
"15", "22", "23", "24", "25", END_MARK); | |
| 795 pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 2 and n is not 12",
status)); | |
| 796 checkSelect(pr, status, __LINE__, "a", "2", "22", "32", "42", END_MARK); | |
| 797 pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2..3 or n mod 10 is
5", status)); | |
| 798 checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "12", "13", "15", "22"
, "23", "25", END_MARK); | |
| 799 pr.adoptInstead(PluralRules::createRules("a: n mod 10 within 2..3 or n mod 1
0 is 5", status)); | |
| 800 checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "12", "13", "15", "22"
, "23", "25", END_MARK); | |
| 801 pr.adoptInstead(PluralRules::createRules("a: n is 1 or n is 4 or n is 23", s
tatus)); | |
| 802 checkSelect(pr, status, __LINE__, "a", "1", "4", "23", END_MARK); | |
| 803 pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 and n is not 3 and
n in 1..11", status)); | |
| 804 checkSelect(pr, status, __LINE__, "a", "1", "5", "7", "9", "11", END_MARK); | |
| 805 pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 and n is not 3 and
n within 1..11", status)); | |
| 806 checkSelect(pr, status, __LINE__, "a", "1", "5", "7", "9", "11", END_MARK); | |
| 807 pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 or n mod 5 is 1 an
d n is not 6", status)); | |
| 808 checkSelect(pr, status, __LINE__, "a", "1", "3", "5", "7", "9", "11", "13",
"15", "16", END_MARK); | |
| 809 pr.adoptInstead(PluralRules::createRules("a: n in 2..5; b: n in 5..8; c: n m
od 2 is 1", status)); | |
| 810 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK); | |
| 811 checkSelect(pr, status, __LINE__, "b", "6", "7", "8", END_MARK); | |
| 812 checkSelect(pr, status, __LINE__, "c", "1", "9", "11", END_MARK); | |
| 813 pr.adoptInstead(PluralRules::createRules("a: n within 2..5; b: n within 5..8
; c: n mod 2 is 1", status)); | |
| 814 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK); | |
| 815 checkSelect(pr, status, __LINE__, "b", "6", "7", "8", END_MARK); | |
| 816 checkSelect(pr, status, __LINE__, "c", "1", "9", "11", END_MARK); | |
| 817 pr.adoptInstead(PluralRules::createRules("a: n in 2, 4..6; b: n within 7..9,
11..12,20", status)); | |
| 818 checkSelect(pr, status, __LINE__, "a", "2", "4", "5", "6", END_MARK); | |
| 819 checkSelect(pr, status, __LINE__, "b", "7", "8", "9", "11", "12", "20", END_
MARK); | |
| 820 pr.adoptInstead(PluralRules::createRules("a: n in 2..8, 12 and n not in 4..6
", status)); | |
| 821 checkSelect(pr, status, __LINE__, "a", "2", "3", "7", "8", "12", END_MARK); | |
| 822 pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2, 3,5..7 and n is
not 12", status)); | |
| 823 checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "6", "7", "13", "15",
"16", "17", END_MARK); | |
| 824 pr.adoptInstead(PluralRules::createRules("a: n in 2..6, 3..7", status)); | |
| 825 checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "6", "7", END_MAR
K); | |
| 826 | |
| 827 // Extended Syntax, with '=', '!=' and '%' operators. | |
| 828 pr.adoptInstead(PluralRules::createRules("a: n = 1..8 and n!= 2,3,4,5", stat
us)); | |
| 829 checkSelect(pr, status, __LINE__, "a", "1", "6", "7", "8", END_MARK); | |
| 830 checkSelect(pr, status, __LINE__, "other", "0", "2", "3", "4", "5", "9", END
_MARK); | |
| 831 pr.adoptInstead(PluralRules::createRules("a:n % 10 != 1", status)); | |
| 832 checkSelect(pr, status, __LINE__, "a", "2", "6", "7", "8", END_MARK); | |
| 833 checkSelect(pr, status, __LINE__, "other", "1", "21", "211", "91", END_MARK)
; | |
| 834 } | |
| 835 | |
| 836 | |
| 837 void PluralRulesTest::testAvailbleLocales() { | |
| 838 | |
| 839 // Hash set of (char *) strings. | |
| 840 UErrorCode status = U_ZERO_ERROR; | |
| 841 UHashtable *localeSet = uhash_open(uhash_hashUnicodeString, uhash_compareUni
codeString, uhash_compareLong, &status); | |
| 842 uhash_setKeyDeleter(localeSet, uprv_deleteUObject); | |
| 843 if (U_FAILURE(status)) { | |
| 844 errln("file %s, line %d: Error status = %s", __FILE__, __LINE__, u_erro
rName(status)); | |
| 845 return; | |
| 846 } | |
| 847 | |
| 848 // Check that each locale returned by the iterator is unique. | |
| 849 StringEnumeration *localesEnum = PluralRules::getAvailableLocales(status); | |
| 850 int localeCount = 0; | |
| 851 for (;;) { | |
| 852 const char *locale = localesEnum->next(NULL, status); | |
| 853 if (U_FAILURE(status)) { | |
| 854 dataerrln("file %s, line %d: Error status = %s", __FILE__, __LINE__
, u_errorName(status)); | |
| 855 return; | |
| 856 } | |
| 857 if (locale == NULL) { | |
| 858 break; | |
| 859 } | |
| 860 localeCount++; | |
| 861 int32_t oldVal = uhash_puti(localeSet, new UnicodeString(locale), 1, &st
atus); | |
| 862 if (oldVal != 0) { | |
| 863 errln("file %s, line %d: locale %s was seen before.", __FILE__, __L
INE__, locale); | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 // Reset the iterator, verify that we get the same count. | |
| 868 localesEnum->reset(status); | |
| 869 int32_t localeCount2 = 0; | |
| 870 while (localesEnum->next(NULL, status) != NULL) { | |
| 871 if (U_FAILURE(status)) { | |
| 872 errln("file %s, line %d: Error status = %s", __FILE__, __LINE__, u_
errorName(status)); | |
| 873 break; | |
| 874 } | |
| 875 localeCount2++; | |
| 876 } | |
| 877 if (localeCount != localeCount2) { | |
| 878 errln("file %s, line %d: locale counts differ. They are (%d, %d)", | |
| 879 __FILE__, __LINE__, localeCount, localeCount2); | |
| 880 } | |
| 881 | |
| 882 // Instantiate plural rules for each available locale. | |
| 883 localesEnum->reset(status); | |
| 884 for (;;) { | |
| 885 status = U_ZERO_ERROR; | |
| 886 const char *localeName = localesEnum->next(NULL, status); | |
| 887 if (U_FAILURE(status)) { | |
| 888 errln("file %s, line %d: Error status = %s, locale = %s", | |
| 889 __FILE__, __LINE__, u_errorName(status), localeName); | |
| 890 return; | |
| 891 } | |
| 892 if (localeName == NULL) { | |
| 893 break; | |
| 894 } | |
| 895 Locale locale = Locale::createFromName(localeName); | |
| 896 PluralRules *pr = PluralRules::forLocale(locale, status); | |
| 897 if (U_FAILURE(status)) { | |
| 898 errln("file %s, line %d: Error %s creating plural rules for locale
%s", | |
| 899 __FILE__, __LINE__, u_errorName(status), localeName); | |
| 900 continue; | |
| 901 } | |
| 902 if (pr == NULL) { | |
| 903 errln("file %s, line %d: Null plural rules for locale %s", __FILE__,
__LINE__, localeName); | |
| 904 continue; | |
| 905 } | |
| 906 | |
| 907 // Pump some numbers through the plural rules. Can't check for correct
results, | |
| 908 // mostly this to tickle any asserts or crashes that may be lurking. | |
| 909 for (double n=0; n<120.0; n+=0.5) { | |
| 910 UnicodeString keyword = pr->select(n); | |
| 911 if (keyword.length() == 0) { | |
| 912 errln("file %s, line %d, empty keyword for n = %g, locale %s", | |
| 913 __FILE__, __LINE__, n, localeName); | |
| 914 } | |
| 915 } | |
| 916 delete pr; | |
| 917 } | |
| 918 | |
| 919 uhash_close(localeSet); | |
| 920 delete localesEnum; | |
| 921 | |
| 922 } | |
| 923 | |
| 924 | |
| 925 void PluralRulesTest::testParseErrors() { | |
| 926 // Test rules with syntax errors. | |
| 927 // Creation of PluralRules from them should fail. | |
| 928 | |
| 929 static const char *testCases[] = { | |
| 930 "a: n mod 10, is 1", | |
| 931 "a: q is 13", | |
| 932 "a n is 13", | |
| 933 "a: n is 13,", | |
| 934 "a: n is 13, 15, b: n is 4", | |
| 935 "a: n is 1, 3, 4.. ", | |
| 936 "a: n within 5..4", | |
| 937 "A: n is 13", // Uppercase keywords not allowed. | |
| 938 "a: n ! = 3", // spaces in != operator | |
| 939 "a: n = not 3", // '=' not exact equivalent of 'is' | |
| 940 "a: n ! in 3..4" // '!' not exact equivalent of 'not' | |
| 941 "a: n % 37 ! in 3..4" | |
| 942 | |
| 943 }; | |
| 944 for (int i=0; i<UPRV_LENGTHOF(testCases); i++) { | |
| 945 const char *rules = testCases[i]; | |
| 946 UErrorCode status = U_ZERO_ERROR; | |
| 947 PluralRules *pr = PluralRules::createRules(UnicodeString(rules), status)
; | |
| 948 if (U_SUCCESS(status)) { | |
| 949 errln("file %s, line %d, expected failure with \"%s\".", __FILE__, _
_LINE__, rules); | |
| 950 } | |
| 951 if (pr != NULL) { | |
| 952 errln("file %s, line %d, expected NULL. Rules: \"%s\"", __FILE__, __
LINE__, rules); | |
| 953 } | |
| 954 } | |
| 955 return; | |
| 956 } | |
| 957 | |
| 958 | |
| 959 void PluralRulesTest::testFixedDecimal() { | |
| 960 struct DoubleTestCase { | |
| 961 double n; | |
| 962 int32_t fractionDigitCount; | |
| 963 int64_t fractionDigits; | |
| 964 }; | |
| 965 | |
| 966 // Check that the internal functions for extracting the decimal fraction dig
its from | |
| 967 // a double value are working. | |
| 968 static DoubleTestCase testCases[] = { | |
| 969 {1.0, 0, 0}, | |
| 970 {123456.0, 0, 0}, | |
| 971 {1.1, 1, 1}, | |
| 972 {1.23, 2, 23}, | |
| 973 {1.234, 3, 234}, | |
| 974 {1.2345, 4, 2345}, | |
| 975 {1.23456, 5, 23456}, | |
| 976 {.1234, 4, 1234}, | |
| 977 {.01234, 5, 1234}, | |
| 978 {.001234, 6, 1234}, | |
| 979 {.0001234, 7, 1234}, | |
| 980 {100.1234, 4, 1234}, | |
| 981 {100.01234, 5, 1234}, | |
| 982 {100.001234, 6, 1234}, | |
| 983 {100.0001234, 7, 1234} | |
| 984 }; | |
| 985 | |
| 986 for (int i=0; i<UPRV_LENGTHOF(testCases); ++i) { | |
| 987 DoubleTestCase &tc = testCases[i]; | |
| 988 int32_t numFractionDigits = FixedDecimal::decimals(tc.n); | |
| 989 if (numFractionDigits != tc.fractionDigitCount) { | |
| 990 errln("file %s, line %d: decimals(%g) expected %d, actual %d", | |
| 991 __FILE__, __LINE__, tc.n, tc.fractionDigitCount, numFractionD
igits); | |
| 992 continue; | |
| 993 } | |
| 994 int64_t actualFractionDigits = FixedDecimal::getFractionalDigits(tc.n, n
umFractionDigits); | |
| 995 if (actualFractionDigits != tc.fractionDigits) { | |
| 996 errln("file %s, line %d: getFractionDigits(%g, %d): expected %ld, go
t %ld", | |
| 997 __FILE__, __LINE__, tc.n, numFractionDigits, tc.fractionDigits
, actualFractionDigits); | |
| 998 } | |
| 999 } | |
| 1000 } | |
| 1001 | |
| 1002 | |
| 1003 | |
| 1004 #endif /* #if !UCONFIG_NO_FORMATTING */ | |
| OLD | NEW |