• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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