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 |