]>
Commit | Line | Data |
---|---|---|
f3c0d7a5 A |
1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
4388f060 | 3 | /******************************************************************** |
57a6839d | 4 | * Copyright (c) 2011-2014, International Business Machines Corporation |
4388f060 A |
5 | * and others. All Rights Reserved. |
6 | ********************************************************************/ | |
7 | /* C API TEST FOR PLURAL RULES */ | |
8 | ||
9 | #include "unicode/utypes.h" | |
10 | ||
11 | #if !UCONFIG_NO_FORMATTING | |
12 | ||
13 | #include "unicode/upluralrules.h" | |
14 | #include "unicode/ustring.h" | |
f3c0d7a5 | 15 | #include "unicode/uenum.h" |
3d1f044b | 16 | #include "unicode/unumberformatter.h" |
4388f060 A |
17 | #include "cintltst.h" |
18 | #include "cmemory.h" | |
f3c0d7a5 | 19 | #include "cstring.h" |
4388f060 A |
20 | |
21 | static void TestPluralRules(void); | |
51004dcb | 22 | static void TestOrdinalRules(void); |
f3c0d7a5 | 23 | static void TestGetKeywords(void); |
3d1f044b | 24 | static void TestFormatted(void); |
4388f060 A |
25 | |
26 | void addPluralRulesTest(TestNode** root); | |
27 | ||
28 | #define TESTCASE(x) addTest(root, &x, "tsformat/cpluralrulestest/" #x) | |
29 | ||
30 | void addPluralRulesTest(TestNode** root) | |
31 | { | |
32 | TESTCASE(TestPluralRules); | |
51004dcb | 33 | TESTCASE(TestOrdinalRules); |
f3c0d7a5 | 34 | TESTCASE(TestGetKeywords); |
3d1f044b | 35 | TESTCASE(TestFormatted); |
4388f060 A |
36 | } |
37 | ||
38 | typedef struct { | |
39 | const char * locale; | |
40 | double number; | |
41 | const char * keywordExpected; | |
f3c0d7a5 | 42 | const char * keywordExpectedForDecimals; |
4388f060 A |
43 | } PluralRulesTestItem; |
44 | ||
45 | /* Just a small set of tests for now, other functionality is tested in the C++ tests */ | |
46 | static const PluralRulesTestItem testItems[] = { | |
f3c0d7a5 A |
47 | { "en", 0, "other", "other" }, |
48 | { "en", 0.5, "other", "other" }, | |
49 | { "en", 1, "one", "other" }, | |
50 | { "en", 1.5, "other", "other" }, | |
51 | { "en", 2, "other", "other" }, | |
52 | ||
53 | { "pt_PT", 0, "other", "other" }, | |
54 | { "pt_PT", 0.5, "other", "other" }, | |
55 | { "pt_PT", 1, "one", "other" }, | |
56 | { "pt_PT", 1.5, "other", "other" }, | |
57 | { "pt_PT", 2, "other", "other" }, | |
58 | ||
59 | { "pt_BR", 0, "one", "one" }, | |
60 | { "pt_BR", 0.5, "one", "one" }, | |
61 | { "pt_BR", 1, "one", "one" }, | |
62 | { "pt_BR", 1.5, "one", "one" }, | |
63 | { "pt_BR", 2, "other", "other" }, | |
64 | ||
65 | { "fr", 0, "one", "one" }, | |
66 | { "fr", 0.5, "one", "one" }, | |
67 | { "fr", 1, "one", "one" }, | |
68 | { "fr", 1.5, "one", "one" }, | |
69 | { "fr", 2, "other", "other" }, | |
70 | ||
71 | { "ru", 0, "many", "other" }, | |
72 | { "ru", 0.5, "other", "other" }, | |
73 | { "ru", 1, "one", "other" }, | |
74 | { "ru", 1.5, "other", "other" }, | |
75 | { "ru", 2, "few", "other" }, | |
76 | { "ru", 5, "many", "other" }, | |
77 | { "ru", 10, "many", "other" }, | |
78 | { "ru", 11, "many", "other" }, | |
3d1f044b A |
79 | |
80 | // ru rules should not be affected by script/lang/keywords <rdar://problem/49268649> | |
81 | { "ru_Cyrl_RU", 0, "many", "other" }, | |
82 | { "ru_Cyrl_RU", 0.5, "other", "other" }, | |
83 | { "ru_Cyrl_RU", 1, "one", "other" }, | |
84 | { "ru_Cyrl_RU", 1.5, "other", "other" }, | |
85 | { "ru_Cyrl_RU", 2, "few", "other" }, | |
86 | { "ru_Cyrl_RU", 5, "many", "other" }, | |
87 | { "ru_Cyrl_RU", 10, "many", "other" }, | |
88 | { "ru_Cyrl_RU", 11, "many", "other" }, | |
89 | ||
90 | { "ru@numbers=latn", 0, "many", "other" }, | |
91 | { "ru@numbers=latn", 0.5, "other", "other" }, | |
92 | { "ru@numbers=latn", 1, "one", "other" }, | |
93 | { "ru@numbers=latn", 1.5, "other", "other" }, | |
94 | { "ru@numbers=latn", 2, "few", "other" }, | |
95 | { "ru@numbers=latn", 5, "many", "other" }, | |
96 | { "ru@numbers=latn", 10, "many", "other" }, | |
97 | { "ru@numbers=latn", 11, "many", "other" }, | |
98 | ||
99 | { "ru_Cyrl_RU@numbers=latn", 0, "many", "other" }, | |
100 | { "ru_Cyrl_RU@numbers=latn", 0.5, "other", "other" }, | |
101 | { "ru_Cyrl_RU@numbers=latn", 1, "one", "other" }, | |
102 | { "ru_Cyrl_RU@numbers=latn", 1.5, "other", "other" }, | |
103 | { "ru_Cyrl_RU@numbers=latn", 2, "few", "other" }, | |
104 | { "ru_Cyrl_RU@numbers=latn", 5, "many", "other" }, | |
105 | { "ru_Cyrl_RU@numbers=latn", 10, "many", "other" }, | |
106 | { "ru_Cyrl_RU@numbers=latn", 11, "many", "other" }, | |
107 | ||
f3c0d7a5 | 108 | { NULL, 0, NULL, NULL } |
4388f060 A |
109 | }; |
110 | ||
f3c0d7a5 A |
111 | static const UChar twoDecimalPat[] = { 0x23,0x30,0x2E,0x30,0x30,0 }; /* "#0.00" */ |
112 | ||
4388f060 A |
113 | enum { |
114 | kKeywordBufLen = 32 | |
115 | }; | |
116 | ||
117 | static void TestPluralRules() | |
118 | { | |
119 | const PluralRulesTestItem * testItemPtr; | |
120 | log_verbose("\nTesting uplrules_open() and uplrules_select() with various parameters\n"); | |
121 | for ( testItemPtr = testItems; testItemPtr->locale != NULL; ++testItemPtr ) { | |
122 | UErrorCode status = U_ZERO_ERROR; | |
123 | UPluralRules* uplrules = uplrules_open(testItemPtr->locale, &status); | |
124 | if ( U_SUCCESS(status) ) { | |
f3c0d7a5 | 125 | UNumberFormat* unumfmt; |
4388f060 A |
126 | UChar keyword[kKeywordBufLen]; |
127 | UChar keywordExpected[kKeywordBufLen]; | |
128 | int32_t keywdLen = uplrules_select(uplrules, testItemPtr->number, keyword, kKeywordBufLen, &status); | |
129 | if (keywdLen >= kKeywordBufLen) { | |
130 | keyword[kKeywordBufLen-1] = 0; | |
131 | } | |
132 | if ( U_SUCCESS(status) ) { | |
133 | u_unescape(testItemPtr->keywordExpected, keywordExpected, kKeywordBufLen); | |
134 | if ( u_strcmp(keyword, keywordExpected) != 0 ) { | |
135 | char bcharBuf[kKeywordBufLen]; | |
136 | log_data_err("ERROR: uplrules_select for locale %s, number %.1f: expect %s, get %s\n", | |
137 | testItemPtr->locale, testItemPtr->number, testItemPtr->keywordExpected, u_austrcpy(bcharBuf,keyword) ); | |
138 | } | |
139 | } else { | |
140 | log_err("FAIL: uplrules_select for locale %s, number %.1f: %s\n", | |
141 | testItemPtr->locale, testItemPtr->number, myErrorName(status) ); | |
142 | } | |
f3c0d7a5 A |
143 | |
144 | status = U_ZERO_ERROR; | |
145 | unumfmt = unum_open(UNUM_PATTERN_DECIMAL, twoDecimalPat, -1, testItemPtr->locale, NULL, &status); | |
146 | if ( U_SUCCESS(status) ) { | |
147 | keywdLen = uplrules_selectWithFormat(uplrules, testItemPtr->number, unumfmt, keyword, kKeywordBufLen, &status); | |
148 | if (keywdLen >= kKeywordBufLen) { | |
149 | keyword[kKeywordBufLen-1] = 0; | |
150 | } | |
151 | if ( U_SUCCESS(status) ) { | |
152 | u_unescape(testItemPtr->keywordExpectedForDecimals, keywordExpected, kKeywordBufLen); | |
153 | if ( u_strcmp(keyword, keywordExpected) != 0 ) { | |
154 | char bcharBuf[kKeywordBufLen]; | |
155 | log_data_err("ERROR: uplrules_selectWithFormat for locale %s, number %.1f: expect %s, get %s\n", | |
156 | testItemPtr->locale, testItemPtr->number, testItemPtr->keywordExpectedForDecimals, u_austrcpy(bcharBuf,keyword) ); | |
157 | } | |
158 | } else { | |
159 | log_err("FAIL: uplrules_selectWithFormat for locale %s, number %.1f: %s\n", | |
160 | testItemPtr->locale, testItemPtr->number, myErrorName(status) ); | |
161 | } | |
162 | unum_close(unumfmt); | |
163 | } else { | |
164 | log_err("FAIL: unum_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) ); | |
165 | } | |
166 | ||
4388f060 A |
167 | uplrules_close(uplrules); |
168 | } else { | |
169 | log_err("FAIL: uplrules_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) ); | |
170 | } | |
171 | } | |
172 | } | |
173 | ||
51004dcb A |
174 | static void TestOrdinalRules() { |
175 | U_STRING_DECL(two, "two", 3); | |
176 | UChar keyword[8]; | |
177 | int32_t length; | |
178 | UErrorCode errorCode = U_ZERO_ERROR; | |
179 | UPluralRules* upr = uplrules_openForType("en", UPLURAL_TYPE_ORDINAL, &errorCode); | |
180 | if (U_FAILURE(errorCode)) { | |
181 | log_err("uplrules_openForType(en, ordinal) failed - %s\n", u_errorName(errorCode)); | |
182 | return; | |
183 | } | |
184 | U_STRING_INIT(two, "two", 3); | |
185 | length = uplrules_select(upr, 2., keyword, 8, &errorCode); | |
186 | if (U_FAILURE(errorCode) || u_strCompare(keyword, length, two, 3, FALSE) != 0) { | |
187 | log_data_err("uplrules_select(en-ordinal, 2) failed - %s\n", u_errorName(errorCode)); | |
188 | } | |
189 | uplrules_close(upr); | |
190 | } | |
191 | ||
f3c0d7a5 A |
192 | /* items for TestGetKeywords */ |
193 | ||
194 | /* all possible plural keywords, in alphabetical order */ | |
195 | static const char* knownKeywords[] = { | |
196 | "few", | |
197 | "many", | |
198 | "one", | |
199 | "other", | |
200 | "two", | |
201 | "zero" | |
202 | }; | |
203 | enum { | |
204 | kNumKeywords = UPRV_LENGTHOF(knownKeywords) | |
205 | }; | |
206 | ||
207 | /* Return the index of keyword in knownKeywords[], or -1 if not found */ | |
208 | static int32_t getKeywordIndex(const char* keyword) { | |
209 | int32_t i, compare; | |
210 | for (i = 0; i < kNumKeywords && (compare = uprv_strcmp(keyword,knownKeywords[i])) >= 0; i++) { | |
211 | if (compare == 0) { | |
212 | return i; | |
213 | } | |
214 | } | |
215 | return -1; | |
216 | } | |
217 | ||
218 | typedef struct { | |
219 | const char* locale; | |
220 | const char* keywords[kNumKeywords + 1]; | |
221 | } KeywordsForLang; | |
222 | ||
223 | static const KeywordsForLang getKeywordsItems[] = { | |
224 | { "zh", { "other" } }, | |
225 | { "en", { "one", "other" } }, | |
226 | { "fr", { "one", "other" } }, | |
227 | { "lv", { "zero", "one", "other" } }, | |
228 | { "hr", { "one", "few", "other" } }, | |
229 | { "sl", { "one", "two", "few", "other" } }, | |
230 | { "he", { "one", "two", "many", "other" } }, | |
231 | { "cs", { "one", "few", "many", "other" } }, | |
232 | { "ar", { "zero", "one", "two", "few", "many" , "other" } }, | |
233 | { NULL, { NULL } } | |
234 | }; | |
235 | ||
236 | static void TestGetKeywords() { | |
237 | /* | |
238 | * We don't know the order in which the enumeration will return keywords, | |
239 | * so we have an array with known keywords in a fixed order and then | |
240 | * parallel arrays of flags for expected and actual results that indicate | |
241 | * which keywords are expected to be or actually are found. | |
242 | */ | |
243 | const KeywordsForLang* itemPtr = getKeywordsItems; | |
244 | for (; itemPtr->locale != NULL; itemPtr++) { | |
245 | UPluralRules* uplrules; | |
246 | UEnumeration* uenum; | |
247 | UBool expectKeywords[kNumKeywords]; | |
248 | UBool getKeywords[kNumKeywords]; | |
249 | int32_t i, iKnown; | |
250 | UErrorCode status = U_ZERO_ERROR; | |
251 | ||
252 | /* initialize arrays for expected and get results */ | |
253 | for (i = 0; i < kNumKeywords; i++) { | |
254 | expectKeywords[i] = FALSE; | |
255 | getKeywords[i] = FALSE; | |
256 | } | |
257 | for (i = 0; i < kNumKeywords && itemPtr->keywords[i] != NULL; i++) { | |
258 | iKnown = getKeywordIndex(itemPtr->keywords[i]); | |
259 | if (iKnown >= 0) { | |
260 | expectKeywords[iKnown] = TRUE; | |
261 | } | |
262 | } | |
263 | ||
264 | uplrules = uplrules_openForType(itemPtr->locale, UPLURAL_TYPE_CARDINAL, &status); | |
265 | if (U_FAILURE(status)) { | |
266 | log_err("FAIL: uplrules_openForType for locale %s, UPLURAL_TYPE_CARDINAL: %s\n", itemPtr->locale, myErrorName(status) ); | |
267 | continue; | |
268 | } | |
269 | uenum = uplrules_getKeywords(uplrules, &status); | |
270 | if (U_FAILURE(status)) { | |
271 | log_err("FAIL: uplrules_getKeywords for locale %s: %s\n", itemPtr->locale, myErrorName(status) ); | |
272 | } else { | |
273 | const char* keyword; | |
274 | int32_t keywordLen, keywordCount = 0; | |
275 | while ((keyword = uenum_next(uenum, &keywordLen, &status)) != NULL && U_SUCCESS(status)) { | |
276 | iKnown = getKeywordIndex(keyword); | |
277 | if (iKnown < 0) { | |
278 | log_err("FAIL: uplrules_getKeywords for locale %s, unknown keyword %s\n", itemPtr->locale, keyword ); | |
279 | } else { | |
280 | getKeywords[iKnown] = TRUE; | |
281 | } | |
282 | keywordCount++; | |
283 | } | |
284 | if (keywordCount > kNumKeywords) { | |
285 | log_err("FAIL: uplrules_getKeywords for locale %s, got too many keywords %d\n", itemPtr->locale, keywordCount ); | |
286 | } | |
287 | if (uprv_memcmp(expectKeywords, getKeywords, kNumKeywords) != 0) { | |
288 | log_err("FAIL: uplrules_getKeywords for locale %s, got wrong keyword set; with reference to knownKeywords:\n" | |
289 | " expected { %d %d %d %d %d %d },\n" | |
290 | " got { %d %d %d %d %d %d }\n", itemPtr->locale, | |
291 | expectKeywords[0], expectKeywords[1], expectKeywords[2], expectKeywords[3], expectKeywords[4], expectKeywords[5], | |
292 | getKeywords[0], getKeywords[1], getKeywords[2], getKeywords[3], getKeywords[4], getKeywords[5] ); | |
293 | } | |
294 | uenum_close(uenum); | |
295 | } | |
296 | ||
297 | uplrules_close(uplrules); | |
298 | } | |
299 | } | |
300 | ||
3d1f044b A |
301 | static void TestFormatted() { |
302 | UErrorCode ec = U_ZERO_ERROR; | |
303 | UNumberFormatter* unumf = NULL; | |
304 | UFormattedNumber* uresult = NULL; | |
305 | UPluralRules* uplrules = NULL; | |
306 | ||
307 | uplrules = uplrules_open("hr", &ec); | |
308 | if (!assertSuccess("open plural rules", &ec)) { | |
309 | goto cleanup; | |
310 | } | |
311 | ||
312 | unumf = unumf_openForSkeletonAndLocale(u".00", -1, "hr", &ec); | |
313 | if (!assertSuccess("open unumf", &ec)) { | |
314 | goto cleanup; | |
315 | } | |
316 | ||
317 | uresult = unumf_openResult(&ec); | |
318 | if (!assertSuccess("open result", &ec)) { | |
319 | goto cleanup; | |
320 | } | |
321 | ||
322 | unumf_formatDouble(unumf, 100.2, uresult, &ec); | |
323 | if (!assertSuccess("format", &ec)) { | |
324 | goto cleanup; | |
325 | } | |
326 | ||
327 | UChar buffer[40]; | |
328 | uplrules_selectFormatted(uplrules, uresult, buffer, 40, &ec); | |
329 | if (!assertSuccess("select", &ec)) { | |
330 | goto cleanup; | |
331 | } | |
332 | ||
333 | assertUEquals("0.20 is plural category 'other' in hr", u"other", buffer); | |
334 | ||
335 | cleanup: | |
336 | uplrules_close(uplrules); | |
337 | unumf_close(unumf); | |
338 | unumf_closeResult(uresult); | |
339 | } | |
340 | ||
4388f060 | 341 | #endif /* #if !UCONFIG_NO_FORMATTING */ |