// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /******************************************************************** * Copyright (c) 2011-2014, International Business Machines Corporation * and others. All Rights Reserved. ********************************************************************/ /* C API TEST FOR PLURAL RULES */ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #include "unicode/upluralrules.h" #include "unicode/ustring.h" #include "unicode/uenum.h" #include "unicode/unumberformatter.h" #include "unicode/unumberrangeformatter.h" #include "cintltst.h" #include "cmemory.h" #include "cstring.h" static void TestPluralRules(void); static void TestOrdinalRules(void); static void TestGetKeywords(void); static void TestFormatted(void); static void TestSelectRange(void); void addPluralRulesTest(TestNode** root); #define TESTCASE(x) addTest(root, &x, "tsformat/cpluralrulestest/" #x) void addPluralRulesTest(TestNode** root) { TESTCASE(TestPluralRules); TESTCASE(TestOrdinalRules); TESTCASE(TestGetKeywords); TESTCASE(TestFormatted); TESTCASE(TestSelectRange); } typedef struct { const char * locale; double number; const char * keywordExpected; const char * keywordExpectedForDecimals; } PluralRulesTestItem; /* Just a small set of tests for now, other functionality is tested in the C++ tests */ static const PluralRulesTestItem testItems[] = { { "en", 0, "other", "other" }, { "en", 0.5, "other", "other" }, { "en", 1, "one", "other" }, { "en", 1.5, "other", "other" }, { "en", 2, "other", "other" }, { "fr", 0, "one", "one" }, { "fr", 0.5, "one", "one" }, { "fr", 1, "one", "one" }, { "fr", 1.5, "one", "one" }, { "fr", 2, "other", "other" }, { "ru", 0, "many", "other" }, { "ru", 0.5, "other", "other" }, { "ru", 1, "one", "other" }, { "ru", 1.5, "other", "other" }, { "ru", 2, "few", "other" }, { "ru", 5, "many", "other" }, { "ru", 10, "many", "other" }, { "ru", 11, "many", "other" }, { NULL, 0, NULL, NULL } }; static const UChar twoDecimalPat[] = { 0x23,0x30,0x2E,0x30,0x30,0 }; /* "#0.00" */ enum { kKeywordBufLen = 32 }; static void TestPluralRules() { const PluralRulesTestItem * testItemPtr; log_verbose("\nTesting uplrules_open() and uplrules_select() with various parameters\n"); for ( testItemPtr = testItems; testItemPtr->locale != NULL; ++testItemPtr ) { UErrorCode status = U_ZERO_ERROR; UPluralRules* uplrules = uplrules_open(testItemPtr->locale, &status); if ( U_SUCCESS(status) ) { UNumberFormat* unumfmt; UChar keyword[kKeywordBufLen]; UChar keywordExpected[kKeywordBufLen]; int32_t keywdLen = uplrules_select(uplrules, testItemPtr->number, keyword, kKeywordBufLen, &status); if (keywdLen >= kKeywordBufLen) { keyword[kKeywordBufLen-1] = 0; } if ( U_SUCCESS(status) ) { u_unescape(testItemPtr->keywordExpected, keywordExpected, kKeywordBufLen); if ( u_strcmp(keyword, keywordExpected) != 0 ) { char bcharBuf[kKeywordBufLen]; log_data_err("ERROR: uplrules_select for locale %s, number %.1f: expect %s, get %s\n", testItemPtr->locale, testItemPtr->number, testItemPtr->keywordExpected, u_austrcpy(bcharBuf,keyword) ); } } else { log_err("FAIL: uplrules_select for locale %s, number %.1f: %s\n", testItemPtr->locale, testItemPtr->number, myErrorName(status) ); } status = U_ZERO_ERROR; unumfmt = unum_open(UNUM_PATTERN_DECIMAL, twoDecimalPat, -1, testItemPtr->locale, NULL, &status); if ( U_SUCCESS(status) ) { keywdLen = uplrules_selectWithFormat(uplrules, testItemPtr->number, unumfmt, keyword, kKeywordBufLen, &status); if (keywdLen >= kKeywordBufLen) { keyword[kKeywordBufLen-1] = 0; } if ( U_SUCCESS(status) ) { u_unescape(testItemPtr->keywordExpectedForDecimals, keywordExpected, kKeywordBufLen); if ( u_strcmp(keyword, keywordExpected) != 0 ) { char bcharBuf[kKeywordBufLen]; log_data_err("ERROR: uplrules_selectWithFormat for locale %s, number %.1f: expect %s, get %s\n", testItemPtr->locale, testItemPtr->number, testItemPtr->keywordExpectedForDecimals, u_austrcpy(bcharBuf,keyword) ); } } else { log_err("FAIL: uplrules_selectWithFormat for locale %s, number %.1f: %s\n", testItemPtr->locale, testItemPtr->number, myErrorName(status) ); } unum_close(unumfmt); } else { log_err("FAIL: unum_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) ); } uplrules_close(uplrules); } else { log_err("FAIL: uplrules_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) ); } } } static void TestOrdinalRules() { U_STRING_DECL(two, "two", 3); UChar keyword[8]; int32_t length; UErrorCode errorCode = U_ZERO_ERROR; UPluralRules* upr = uplrules_openForType("en", UPLURAL_TYPE_ORDINAL, &errorCode); if (U_FAILURE(errorCode)) { log_err("uplrules_openForType(en, ordinal) failed - %s\n", u_errorName(errorCode)); return; } U_STRING_INIT(two, "two", 3); length = uplrules_select(upr, 2., keyword, 8, &errorCode); if (U_FAILURE(errorCode) || u_strCompare(keyword, length, two, 3, FALSE) != 0) { log_data_err("uplrules_select(en-ordinal, 2) failed - %s\n", u_errorName(errorCode)); } uplrules_close(upr); } /* items for TestGetKeywords */ /* all possible plural keywords, in alphabetical order */ static const char* knownKeywords[] = { "few", "many", "one", "other", "two", "zero" }; enum { kNumKeywords = UPRV_LENGTHOF(knownKeywords) }; /* Return the index of keyword in knownKeywords[], or -1 if not found */ static int32_t getKeywordIndex(const char* keyword) { int32_t i, compare; for (i = 0; i < kNumKeywords && (compare = uprv_strcmp(keyword,knownKeywords[i])) >= 0; i++) { if (compare == 0) { return i; } } return -1; } typedef struct { const char* locale; const char* keywords[kNumKeywords + 1]; } KeywordsForLang; static const KeywordsForLang getKeywordsItems[] = { { "zh", { "other" } }, { "en", { "one", "other" } }, { "fr", { "one", "many", "other" } }, { "lv", { "zero", "one", "other" } }, { "hr", { "one", "few", "other" } }, { "sl", { "one", "two", "few", "other" } }, { "he", { "one", "two", "many", "other" } }, { "cs", { "one", "few", "many", "other" } }, { "ar", { "zero", "one", "two", "few", "many" , "other" } }, { NULL, { NULL } } }; static void TestGetKeywords() { /* * We don't know the order in which the enumeration will return keywords, * so we have an array with known keywords in a fixed order and then * parallel arrays of flags for expected and actual results that indicate * which keywords are expected to be or actually are found. */ const KeywordsForLang* itemPtr = getKeywordsItems; for (; itemPtr->locale != NULL; itemPtr++) { UPluralRules* uplrules; UEnumeration* uenum; UBool expectKeywords[kNumKeywords]; UBool getKeywords[kNumKeywords]; int32_t i, iKnown; UErrorCode status = U_ZERO_ERROR; /* initialize arrays for expected and get results */ for (i = 0; i < kNumKeywords; i++) { expectKeywords[i] = FALSE; getKeywords[i] = FALSE; } for (i = 0; i < kNumKeywords && itemPtr->keywords[i] != NULL; i++) { iKnown = getKeywordIndex(itemPtr->keywords[i]); if (iKnown >= 0) { expectKeywords[iKnown] = TRUE; } } uplrules = uplrules_openForType(itemPtr->locale, UPLURAL_TYPE_CARDINAL, &status); if (U_FAILURE(status)) { log_err("FAIL: uplrules_openForType for locale %s, UPLURAL_TYPE_CARDINAL: %s\n", itemPtr->locale, myErrorName(status) ); continue; } uenum = uplrules_getKeywords(uplrules, &status); if (U_FAILURE(status)) { log_err("FAIL: uplrules_getKeywords for locale %s: %s\n", itemPtr->locale, myErrorName(status) ); } else { const char* keyword; int32_t keywordLen, keywordCount = 0; while ((keyword = uenum_next(uenum, &keywordLen, &status)) != NULL && U_SUCCESS(status)) { iKnown = getKeywordIndex(keyword); if (iKnown < 0) { log_err("FAIL: uplrules_getKeywords for locale %s, unknown keyword %s\n", itemPtr->locale, keyword ); } else { getKeywords[iKnown] = TRUE; } keywordCount++; } if (keywordCount > kNumKeywords) { log_err("FAIL: uplrules_getKeywords for locale %s, got too many keywords %d\n", itemPtr->locale, keywordCount ); } if (uprv_memcmp(expectKeywords, getKeywords, kNumKeywords) != 0) { log_err("FAIL: uplrules_getKeywords for locale %s, got wrong keyword set; with reference to knownKeywords:\n" " expected { %d %d %d %d %d %d },\n" " got { %d %d %d %d %d %d }\n", itemPtr->locale, expectKeywords[0], expectKeywords[1], expectKeywords[2], expectKeywords[3], expectKeywords[4], expectKeywords[5], getKeywords[0], getKeywords[1], getKeywords[2], getKeywords[3], getKeywords[4], getKeywords[5] ); } uenum_close(uenum); } uplrules_close(uplrules); } } static void TestFormatted() { UErrorCode ec = U_ZERO_ERROR; UNumberFormatter* unumf = NULL; UFormattedNumber* uresult = NULL; UPluralRules* uplrules = NULL; uplrules = uplrules_open("hr", &ec); if (!assertSuccess("open plural rules", &ec)) { goto cleanup; } unumf = unumf_openForSkeletonAndLocale(u".00", -1, "hr", &ec); if (!assertSuccess("open unumf", &ec)) { goto cleanup; } uresult = unumf_openResult(&ec); if (!assertSuccess("open result", &ec)) { goto cleanup; } unumf_formatDouble(unumf, 100.2, uresult, &ec); if (!assertSuccess("format", &ec)) { goto cleanup; } UChar buffer[40]; uplrules_selectFormatted(uplrules, uresult, buffer, 40, &ec); if (!assertSuccess("select", &ec)) { goto cleanup; } assertUEquals("0.20 is plural category 'other' in hr", u"other", buffer); cleanup: uplrules_close(uplrules); unumf_close(unumf); unumf_closeResult(uresult); } static void TestSelectRange() { UErrorCode ec = U_ZERO_ERROR; UNumberRangeFormatter* unumrf = NULL; UFormattedNumberRange* uresult = NULL; UPluralRules* uplrules = NULL; int32_t d1 = 102; int32_t d2 = 201; // Locale sl has interesting data: one + two => few uplrules = uplrules_open("sl", &ec); if (!assertSuccess("open plural rules", &ec)) { goto cleanup; } unumrf = unumrf_openForSkeletonWithCollapseAndIdentityFallback( u"", 0, UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, "sl", NULL, &ec); if (!assertSuccess("open unumrf", &ec)) { goto cleanup; } uresult = unumrf_openResult(&ec); if (!assertSuccess("open result", &ec)) { goto cleanup; } unumrf_formatDoubleRange(unumrf, d1, d2, uresult, &ec); if (!assertSuccess("format", &ec)) { goto cleanup; } UChar buffer[40]; int32_t len = uplrules_selectForRange(uplrules, uresult, buffer, 40, &ec); if (!assertSuccess("select", &ec)) { goto cleanup; } assertUEquals("102-201 is plural category 'few' in sl", u"few", buffer); assertIntEquals("Length should be as expected", u_strlen(buffer), len); cleanup: uplrules_close(uplrules); unumrf_close(unumrf); unumrf_closeResult(uresult); } #endif /* #if !UCONFIG_NO_FORMATTING */