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