]>
Commit | Line | Data |
---|---|---|
46f4442e A |
1 | /* |
2 | ******************************************************************************* | |
57a6839d | 3 | * Copyright (C) 2007-2014, International Business Machines Corporation and |
46f4442e A |
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 | ||
57a6839d A |
16 | #include <stdlib.h> |
17 | #include <stdarg.h> | |
18 | #include <string.h> | |
19 | ||
51004dcb | 20 | #include "unicode/localpointer.h" |
46f4442e | 21 | #include "unicode/plurrule.h" |
57a6839d A |
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" | |
46f4442e | 29 | |
46f4442e | 30 | void setupResult(const int32_t testSource[], char result[], int32_t* max); |
51004dcb A |
31 | UBool checkEqual(const PluralRules &test, char *result, int32_t max); |
32 | UBool testEquality(const PluralRules &test); | |
46f4442e A |
33 | |
34 | // This is an API test, not a unit test. It doesn't test very many cases, and doesn'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* &name, char* /*par*/ ) | |
39 | { | |
40 | if (exec) logln("TestSuite PluralRulesAPI"); | |
51004dcb A |
41 | TESTCASE_AUTO_BEGIN; |
42 | TESTCASE_AUTO(testAPI); | |
57a6839d | 43 | // TESTCASE_AUTO(testGetUniqueKeywordValue); |
51004dcb A |
44 | TESTCASE_AUTO(testGetSamples); |
45 | TESTCASE_AUTO(testWithin); | |
46 | TESTCASE_AUTO(testGetAllKeywordValues); | |
47 | TESTCASE_AUTO(testOrdinal); | |
57a6839d A |
48 | TESTCASE_AUTO(testSelect); |
49 | TESTCASE_AUTO(testAvailbleLocales); | |
50 | TESTCASE_AUTO(testParseErrors); | |
51 | TESTCASE_AUTO(testFixedDecimal); | |
51004dcb | 52 | TESTCASE_AUTO_END; |
46f4442e A |
53 | } |
54 | ||
57a6839d A |
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, (uint32_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 | ||
46f4442e A |
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"); | |
4388f060 A |
123 | |
124 | ||
46f4442e | 125 | logln("\n start default locale test case ..\n"); |
4388f060 A |
126 | |
127 | PluralRules defRule(status); | |
b331163b | 128 | LocalPointer<PluralRules> test(new PluralRules(status), status); |
46f4442e A |
129 | if(U_FAILURE(status)) { |
130 | dataerrln("ERROR: Could not create PluralRules (default) - exitting"); | |
46f4442e A |
131 | return; |
132 | } | |
b331163b A |
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 | } | |
4388f060 | 138 | |
46f4442e | 139 | // ======= Test clone, assignment operator && == operator. |
51004dcb | 140 | LocalPointer<PluralRules> dupRule(defRule.clone()); |
4388f060 A |
141 | if (dupRule==NULL) { |
142 | errln("ERROR: clone plural rules test failed!"); | |
4388f060 A |
143 | return; |
144 | } else { | |
46f4442e A |
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 | } | |
46f4442e A |
154 | } |
155 | ||
4388f060 | 156 | // ======= Test empty plural rules |
46f4442e | 157 | logln("Testing Simple PluralRules"); |
4388f060 | 158 | |
51004dcb | 159 | LocalPointer<PluralRules> empRule(test->createRules(UNICODE_STRING_SIMPLE("a:n"), status)); |
46f4442e A |
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 | } | |
4388f060 A |
167 | |
168 | // ======= Test simple plural rules | |
46f4442e | 169 | logln("Testing Simple PluralRules"); |
4388f060 | 170 | |
46f4442e A |
171 | char result[100]; |
172 | int32_t max; | |
4388f060 | 173 | |
46f4442e | 174 | for (int32_t i=0; i<PLURAL_TEST_NUM-1; ++i) { |
51004dcb | 175 | LocalPointer<PluralRules> newRules(test->createRules(pluralTestData[i], status)); |
46f4442e | 176 | setupResult(pluralTestResult[i], result, &max); |
51004dcb | 177 | if ( !checkEqual(*newRules, result, max) ) { |
46f4442e | 178 | errln("ERROR: simple plural rules failed! - exitting"); |
46f4442e A |
179 | return; |
180 | } | |
46f4442e | 181 | } |
46f4442e | 182 | |
4388f060 | 183 | // ======= Test complex plural rules |
46f4442e | 184 | logln("Testing Complex PluralRules"); |
4388f060 | 185 | // TODO: the complex test data is hard coded. It's better to implement |
46f4442e A |
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"); | |
4388f060 A |
188 | UnicodeString complexRule2 = UNICODE_STRING_SIMPLE("a: n within 2..5; b: n within 5..8; c: n mod 2 is 1"); |
189 | char cRuleResult[] = | |
46f4442e A |
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 | }; | |
51004dcb A |
204 | LocalPointer<PluralRules> newRules(test->createRules(complexRule, status)); |
205 | if ( !checkEqual(*newRules, cRuleResult, 12) ) { | |
46f4442e | 206 | errln("ERROR: complex plural rules failed! - exitting"); |
46f4442e | 207 | return; |
46f4442e | 208 | } |
51004dcb A |
209 | newRules.adoptInstead(test->createRules(complexRule2, status)); |
210 | if ( !checkEqual(*newRules, cRuleResult, 12) ) { | |
46f4442e | 211 | errln("ERROR: complex plural rules failed! - exitting"); |
46f4442e | 212 | return; |
46f4442e | 213 | } |
4388f060 | 214 | |
46f4442e A |
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; | |
51004dcb | 219 | newRules.adoptInstead(test->createRules(decimalRule, status)); |
46f4442e A |
220 | if (U_FAILURE(status)) { |
221 | dataerrln("ERROR: Could not create PluralRules for testing fractions - exitting"); | |
46f4442e A |
222 | return; |
223 | } | |
57a6839d A |
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 }; | |
b331163b | 226 | for (int32_t i=0; i<UPRV_LENGTHOF(fData); i++) { |
46f4442e | 227 | if ((newRules->select(fData[i])== KEYWORD_A) != isKeywordA[i]) { |
57a6839d A |
228 | errln("File %s, Line %d, ERROR: plural rules for decimal fractions test failed!\n" |
229 | " number = %g, expected %s", __FILE__, __LINE__, fData[i], isKeywordA[i]?"TRUE":"FALSE"); | |
46f4442e A |
230 | } |
231 | } | |
4388f060 | 232 | |
46f4442e A |
233 | // ======= Test Equality |
234 | logln("Testing Equality of PluralRules"); | |
4388f060 | 235 | |
51004dcb | 236 | if ( !testEquality(*test) ) { |
46f4442e | 237 | errln("ERROR: complex plural rules failed! - exitting"); |
46f4442e A |
238 | return; |
239 | } | |
240 | ||
4388f060 | 241 | |
46f4442e A |
242 | // ======= Test getStaticClassID() |
243 | logln("Testing getStaticClassID()"); | |
4388f060 | 244 | |
46f4442e A |
245 | if(test->getDynamicClassID() != PluralRules::getStaticClassID()) { |
246 | errln("ERROR: getDynamicClassID() didn't return the expected value"); | |
247 | } | |
248 | // ====== Test fallback to parent locale | |
51004dcb A |
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()) { | |
46f4442e A |
252 | if ( *en_UK != *en ) { |
253 | errln("ERROR: test locale fallback failed!"); | |
254 | } | |
255 | } | |
46f4442e | 256 | |
51004dcb A |
257 | LocalPointer<PluralRules> zh_Hant(test->forLocale(Locale::getTaiwan(), status)); |
258 | LocalPointer<PluralRules> zh(test->forLocale(Locale::getChinese(), status)); | |
259 | if (zh_Hant.isValid() && zh.isValid()) { | |
46f4442e A |
260 | if ( *zh_Hant != *zh ) { |
261 | errln("ERROR: test locale fallback failed!"); | |
262 | } | |
263 | } | |
46f4442e A |
264 | } |
265 | ||
266 | void setupResult(const int32_t testSource[], char result[], int32_t* max) { | |
267 | int32_t i=0; | |
268 | int32_t curIndex=0; | |
4388f060 | 269 | |
46f4442e A |
270 | do { |
271 | while (curIndex < testSource[i]) { | |
272 | result[curIndex++]=0x6F; //'o' other | |
273 | } | |
274 | result[curIndex++]=0x61; // 'a' | |
4388f060 | 275 | |
46f4442e A |
276 | } while(testSource[++i]>0); |
277 | *max=curIndex; | |
278 | } | |
279 | ||
280 | ||
51004dcb | 281 | UBool checkEqual(const PluralRules &test, char *result, int32_t max) { |
46f4442e A |
282 | UnicodeString key; |
283 | UBool isEqual = TRUE; | |
284 | for (int32_t i=0; i<max; ++i) { | |
51004dcb | 285 | key= test.select(i); |
46f4442e A |
286 | if ( key.charAt(0)!=result[i] ) { |
287 | isEqual = FALSE; | |
288 | } | |
289 | } | |
290 | return isEqual; | |
291 | } | |
292 | ||
57a6839d A |
293 | |
294 | ||
295 | static const int32_t MAX_EQ_ROW = 2; | |
296 | static const int32_t MAX_EQ_COL = 5; | |
51004dcb | 297 | UBool testEquality(const PluralRules &test) { |
46f4442e A |
298 | UnicodeString testEquRules[MAX_EQ_ROW][MAX_EQ_COL] = { |
299 | { UNICODE_STRING_SIMPLE("a: n in 2..3"), | |
4388f060 | 300 | UNICODE_STRING_SIMPLE("a: n is 2 or n is 3"), |
46f4442e A |
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]; | |
4388f060 | 315 | |
46f4442e A |
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) ) { | |
51004dcb | 321 | rules[totalRules]=test.createRules(testEquRules[i][totalRules], status); |
46f4442e A |
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 | } | |
4388f060 | 334 | |
46f4442e A |
335 | } |
336 | for (int32_t j=0; j<MAX_EQ_COL; ++j) { | |
337 | if (rules[j]!=NULL) { | |
338 | delete rules[j]; | |
339 | } | |
340 | } | |
341 | } | |
4388f060 | 342 | |
46f4442e A |
343 | return ret; |
344 | } | |
46f4442e | 345 | |
4388f060 A |
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 | ||
57a6839d A |
363 | // TODO: UniqueKeywordValue() is not currently supported. |
364 | // If it never will be, this test code should be removed. | |
4388f060 A |
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); // key not defined | |
378 | assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key matches default rule | |
379 | } | |
380 | ||
381 | void PluralRulesTest::testGetSamples() { | |
57a6839d A |
382 | // TODO: fix samples, re-enable this test. |
383 | ||
4388f060 A |
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 | ||
57a6839d | 390 | double values[1000]; |
4388f060 A |
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))) { | |
b331163b | 403 | int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status); |
4388f060 A |
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) { | |
57a6839d A |
411 | // TODO: Lots of these. |
412 | // errln(UNICODE_STRING_SIMPLE("no samples for keyword ") + *keyword + UNICODE_STRING_SIMPLE(" in locale ") + locales[i].getName() ); | |
4388f060 | 413 | } |
b331163b | 414 | if (count > UPRV_LENGTHOF(values)) { |
4388f060 A |
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); | |
b331163b | 419 | count = UPRV_LENGTHOF(values); |
4388f060 A |
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]); | |
57a6839d A |
426 | // if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only. |
427 | // std::cout << " uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl; | |
428 | // } | |
4388f060 | 429 | if (*keyword != resultKeyword) { |
57a6839d A |
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(), values[j], values[j], US(resultKeyword).cstr()); | |
4388f060 A |
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 out, 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 these | |
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, we 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); | |
57a6839d A |
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)); | |
4388f060 A |
499 | continue; |
500 | } | |
501 | ||
57a6839d A |
502 | // TODO: fix samples implementation, re-enable test. |
503 | (void)result; | |
504 | #if 0 | |
505 | ||
4388f060 A |
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, status); | |
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 so 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, this test | |
549 | // must change. There's no actual restriction on the order of the samples. | |
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 == ';')) { | |
57a6839d | 577 | errln("file: %s, line %d, didn't get expected value: %s", __FILE__, __LINE__, ep); |
4388f060 A |
578 | ok = FALSE; |
579 | } | |
580 | } | |
581 | ||
582 | while (*ep && *ep != ';') ++ep; | |
583 | if (*ep == ';') ++ep; | |
584 | rp = ep; | |
585 | } | |
57a6839d A |
586 | #endif |
587 | delete p; | |
4388f060 A |
588 | } |
589 | } | |
590 | ||
51004dcb A |
591 | void PluralRulesTest::testOrdinal() { |
592 | IcuTestErrorCode errorCode(*this, "testOrdinal"); | |
593 | LocalPointer<PluralRules> pr(PluralRules::forLocale("en", UPLURAL_TYPE_ORDINAL, 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 | ||
57a6839d A |
603 | |
604 | static const char * END_MARK = "999.999"; // Mark end of varargs data. | |
605 | ||
606 | void PluralRulesTest::checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status, | |
607 | int32_t line, const char *keyword, ...) { | |
608 | // The varargs parameters are a const char* strings, each being a decimal number. | |
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 (which keyword is selected). | |
611 | // Note: rules parameter is a LocalPointer reference rather than a PluralRules * 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_errorName(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_errorName(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. Expected %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", status)); | |
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_MARK); | |
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_MARK); | |
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", END_MARK); | |
707 | checkSelect(pr, status, __LINE__, "other", "1", "7.7", "15.01", "16", END_MARK); | |
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_MARK); | |
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", END_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.123", 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; mod: 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", END_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 10 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", status)); | |
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 and 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 mod 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_MARK); | |
826 | ||
827 | // Extended Syntax, with '=', '!=' and '%' operators. | |
828 | pr.adoptInstead(PluralRules::createRules("a: n = 1..8 and n!= 2,3,4,5", status)); | |
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_compareUnicodeString, 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_errorName(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, &status); | |
862 | if (oldVal != 0) { | |
863 | errln("file %s, line %d: locale %s was seen before.", __FILE__, __LINE__, 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 | }; | |
b331163b | 944 | for (int i=0; i<UPRV_LENGTHOF(testCases); i++) { |
57a6839d A |
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 digits 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 | ||
b331163b | 986 | for (int i=0; i<UPRV_LENGTHOF(testCases); ++i) { |
57a6839d A |
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, numFractionDigits); | |
992 | continue; | |
993 | } | |
994 | int64_t actualFractionDigits = FixedDecimal::getFractionalDigits(tc.n, numFractionDigits); | |
995 | if (actualFractionDigits != tc.fractionDigits) { | |
996 | errln("file %s, line %d: getFractionDigits(%g, %d): expected %ld, got %ld", | |
997 | __FILE__, __LINE__, tc.n, numFractionDigits, tc.fractionDigits, actualFractionDigits); | |
998 | } | |
999 | } | |
1000 | } | |
1001 | ||
1002 | ||
1003 | ||
4388f060 | 1004 | #endif /* #if !UCONFIG_NO_FORMATTING */ |