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