// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /******************************************************************** * COPYRIGHT: * Copyright (c) 1997-2016, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /***************************************************************************** * * File CLOCTST.C * * Modification History: * Name Description * Madhu Katragadda Ported for C API ****************************************************************************** */ #include "cloctst.h" #include #include #include #include "cintltst.h" #include "cmemory.h" #include "cstring.h" #include "uparse.h" #include "uresimp.h" #include "uassert.h" #include "unicode/putil.h" #include "unicode/ubrk.h" #include "unicode/uchar.h" #include "unicode/ucol.h" #include "unicode/udat.h" #include "unicode/uloc.h" #include "unicode/umsg.h" #include "unicode/ures.h" #include "unicode/uset.h" #include "unicode/ustring.h" #include "unicode/utypes.h" #include "unicode/ulocdata.h" #include "unicode/uldnames.h" #include "unicode/parseerr.h" /* may not be included with some uconfig switches */ #include "udbgutil.h" static void TestNullDefault(void); static void TestNonexistentLanguageExemplars(void); static void TestLocDataErrorCodeChaining(void); static void TestLocDataWithRgTag(void); static void TestLanguageExemplarsFallbacks(void); static void TestDisplayNameBrackets(void); static void TestIllegalArgumentWhenNoDataWithNoSubstitute(void); static void Test21157CorrectTerminating(void); static void TestUnicodeDefines(void); static void TestIsRightToLeft(void); static void TestBadLocaleIDs(void); static void TestBug20370(void); static void TestBug20321UnicodeLocaleKey(void); static void TestUsingDefaultWarning(void); static void TestExcessivelyLongIDs(void); #if !UCONFIG_NO_FORMATTING static void TestUldnNameVariants(void); #endif void PrintDataTable(); /*--------------------------------------------------- table of valid data --------------------------------------------------- */ #define LOCALE_SIZE 9 #define LOCALE_INFO_SIZE 28 static const char* const rawData2[LOCALE_INFO_SIZE][LOCALE_SIZE] = { /* language code */ { "en", "fr", "ca", "el", "no", "zh", "de", "es", "ja" }, /* script code */ { "", "", "", "", "", "", "", "", "" }, /* country code */ { "US", "FR", "ES", "GR", "NO", "CN", "DE", "", "JP" }, /* variant code */ { "", "", "", "", "NY", "", "", "", "" }, /* full name */ { "en_US", "fr_FR", "ca_ES", "el_GR", "no_NO_NY", "zh_Hans_CN", "de_DE@collation=phonebook", "es@collation=traditional", "ja_JP@calendar=japanese" }, /* ISO-3 language */ { "eng", "fra", "cat", "ell", "nor", "zho", "deu", "spa", "jpn" }, /* ISO-3 country */ { "USA", "FRA", "ESP", "GRC", "NOR", "CHN", "DEU", "", "JPN" }, /* LCID */ { "409", "40c", "403", "408", "814", "804", "10407", "40a", "411" }, /* display language (English) */ { "English", "French", "Catalan", "Greek", "Norwegian", "Chinese", "German", "Spanish", "Japanese" }, /* display script code (English) */ { "", "", "", "", "", "Simplified Han", "", "", "" }, /* display country (English) */ { "United States", "France", "Spain", "Greece", "Norway", "China", "Germany", "", "Japan" }, /* display variant (English) */ { "", "", "", "", "NY", "", "", "", "" }, /* display name (English) */ { "English (United States)", "French (France)", "Catalan (Spain)", "Greek (Greece)", "Norwegian (Norway, NY)", "Chinese (Simplified, China)", "German (Germany, Sort Order=Phonebook Sort Order)", "Spanish (Sort Order=Traditional Sort Order)", "Japanese (Japan, Calendar=Japanese Calendar)" }, /* display language (French) */ { "anglais", "fran\\u00E7ais", "catalan", "grec", "norv\\u00E9gien", "chinois", "allemand", "espagnol", "japonais" }, /* display script code (French) */ { "", "", "", "", "", "sinogrammes simplifi\\u00e9s", "", "", "" }, /* display country (French) */ { "\\u00C9tats-Unis", "France", "Espagne", "Gr\\u00E8ce", "Norv\\u00E8ge", "Chine", "Allemagne", "", "Japon" }, /* display variant (French) */ { "", "", "", "", "NY", "", "", "", "" }, /* display name (French) */ { "anglais (\\u00C9tats-Unis)", "fran\\u00E7ais (France)", "catalan (Espagne)", "grec (Gr\\u00E8ce)", "norv\\u00E9gien (Norv\\u00E8ge, NY)", "chinois (simplifi\\u00e9, Chine)", "allemand (Allemagne, ordre de tri=ordre de l\\u2019annuaire)", "espagnol (ordre de tri=ordre traditionnel)", "japonais (Japon, calendrier=calendrier japonais)" }, /* display language (Catalan) */ { "angl\\u00E8s", "franc\\u00E8s", "catal\\u00E0", "grec", "noruec", "xin\\u00E8s", "alemany", "espanyol", "japon\\u00E8s" }, /* display script code (Catalan) */ { "", "", "", "", "", "han simplificat", "", "", "" }, /* display country (Catalan) */ { "Estats Units", "Fran\\u00E7a", "Espanya", "Gr\\u00E8cia", "Noruega", "Xina", "Alemanya", "", "Jap\\u00F3" }, /* display variant (Catalan) */ { "", "", "", "", "NY", "", "", "", "" }, /* display name (Catalan) */ { "angl\\u00E8s (Estats Units)", "franc\\u00E8s (Fran\\u00E7a)", "catal\\u00E0 (Espanya)", "grec (Gr\\u00E8cia)", "noruec (Noruega, NY)", "xin\\u00E8s (simplificat, Xina)", "alemany (Alemanya, ordenaci\\u00F3=ordre de la guia telef\\u00F2nica)", "espanyol (ordenaci\\u00F3=ordre tradicional)", "japon\\u00E8s (Jap\\u00F3, calendari=calendari japon\\u00e8s)" }, /* display language (Greek) */ { "\\u0391\\u03b3\\u03b3\\u03bb\\u03b9\\u03ba\\u03ac", "\\u0393\\u03b1\\u03bb\\u03bb\\u03b9\\u03ba\\u03ac", "\\u039a\\u03b1\\u03c4\\u03b1\\u03bb\\u03b1\\u03bd\\u03b9\\u03ba\\u03ac", "\\u0395\\u03bb\\u03bb\\u03b7\\u03bd\\u03b9\\u03ba\\u03ac", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03b9\\u03ba\\u03ac", "\\u039A\\u03B9\\u03BD\\u03B5\\u03B6\\u03B9\\u03BA\\u03AC", "\\u0393\\u03B5\\u03C1\\u03BC\\u03B1\\u03BD\\u03B9\\u03BA\\u03AC", "\\u0399\\u03C3\\u03C0\\u03B1\\u03BD\\u03B9\\u03BA\\u03AC", "\\u0399\\u03B1\\u03C0\\u03C9\\u03BD\\u03B9\\u03BA\\u03AC" }, /* display script code (Greek) */ { "", "", "", "", "", "\\u0391\\u03c0\\u03bb\\u03bf\\u03c0\\u03bf\\u03b9\\u03b7\\u03bc\\u03ad\\u03bd\\u03bf \\u03a7\\u03b1\\u03bd", "", "", "" }, /* display country (Greek) */ { "\\u0397\\u03BD\\u03C9\\u03BC\\u03AD\\u03BD\\u03B5\\u03C2 \\u03A0\\u03BF\\u03BB\\u03B9\\u03C4\\u03B5\\u03AF\\u03B5\\u03C2", "\\u0393\\u03b1\\u03bb\\u03bb\\u03af\\u03b1", "\\u0399\\u03c3\\u03c0\\u03b1\\u03bd\\u03af\\u03b1", "\\u0395\\u03bb\\u03bb\\u03ac\\u03b4\\u03b1", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03af\\u03b1", "\\u039A\\u03AF\\u03BD\\u03B1", "\\u0393\\u03B5\\u03C1\\u03BC\\u03B1\\u03BD\\u03AF\\u03B1", "", "\\u0399\\u03B1\\u03C0\\u03C9\\u03BD\\u03AF\\u03B1" }, /* display variant (Greek) */ { "", "", "", "", "NY", "", "", "", "" }, /* TODO: currently there is no translation for NY in Greek fix this test when we have it */ /* display name (Greek) */ { "\\u0391\\u03b3\\u03b3\\u03bb\\u03b9\\u03ba\\u03ac (\\u0397\\u03BD\\u03C9\\u03BC\\u03AD\\u03BD\\u03B5\\u03C2 \\u03A0\\u03BF\\u03BB\\u03B9\\u03C4\\u03B5\\u03AF\\u03B5\\u03C2)", "\\u0393\\u03b1\\u03bb\\u03bb\\u03b9\\u03ba\\u03ac (\\u0393\\u03b1\\u03bb\\u03bb\\u03af\\u03b1)", "\\u039a\\u03b1\\u03c4\\u03b1\\u03bb\\u03b1\\u03bd\\u03b9\\u03ba\\u03ac (\\u0399\\u03c3\\u03c0\\u03b1\\u03bd\\u03af\\u03b1)", "\\u0395\\u03bb\\u03bb\\u03b7\\u03bd\\u03b9\\u03ba\\u03ac (\\u0395\\u03bb\\u03bb\\u03ac\\u03b4\\u03b1)", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03b9\\u03ba\\u03ac (\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03af\\u03b1, NY)", "\\u039A\\u03B9\\u03BD\\u03B5\\u03B6\\u03B9\\u03BA\\u03AC (\\u0391\\u03c0\\u03bb\\u03bf\\u03c0\\u03bf\\u03b9\\u03b7\\u03bc\\u03ad\\u03bd\\u03bf, \\u039A\\u03AF\\u03BD\\u03B1)", "\\u0393\\u03b5\\u03c1\\u03bc\\u03b1\\u03bd\\u03b9\\u03ba\\u03ac (\\u0393\\u03b5\\u03c1\\u03bc\\u03b1\\u03bd\\u03af\\u03b1, \\u03a3\\u03b5\\u03b9\\u03c1\\u03ac \\u03c4\\u03b1\\u03be\\u03b9\\u03bd\\u03cc\\u03bc\\u03b7\\u03c3\\u03b7\\u03c2=\\u03a3\\u03b5\\u03b9\\u03c1\\u03ac \\u03c4\\u03b1\\u03be\\u03b9\\u03bd\\u03cc\\u03bc\\u03b7\\u03c3\\u03b7\\u03c2 \\u03c4\\u03b7\\u03bb\\u03b5\\u03c6\\u03c9\\u03bd\\u03b9\\u03ba\\u03bf\\u03cd \\u03ba\\u03b1\\u03c4\\u03b1\\u03bb\\u03cc\\u03b3\\u03bf\\u03c5)", "\\u0399\\u03c3\\u03c0\\u03b1\\u03bd\\u03b9\\u03ba\\u03ac (\\u03a3\\u03b5\\u03b9\\u03c1\\u03ac \\u03c4\\u03b1\\u03be\\u03b9\\u03bd\\u03cc\\u03bc\\u03b7\\u03c3\\u03b7\\u03c2=\\u03a0\\u03b1\\u03c1\\u03b1\\u03b4\\u03bf\\u03c3\\u03b9\\u03b1\\u03ba\\u03ae \\u03c3\\u03b5\\u03b9\\u03c1\\u03ac \\u03c4\\u03b1\\u03be\\u03b9\\u03bd\\u03cc\\u03bc\\u03b7\\u03c3\\u03b7\\u03c2)", "\\u0399\\u03b1\\u03c0\\u03c9\\u03bd\\u03b9\\u03ba\\u03ac (\\u0399\\u03b1\\u03c0\\u03c9\\u03bd\\u03af\\u03b1, \\u0397\\u03bc\\u03b5\\u03c1\\u03bf\\u03bb\\u03cc\\u03b3\\u03b9\\u03bf=\\u0399\\u03b1\\u03c0\\u03c9\\u03bd\\u03b9\\u03ba\\u03cc \\u03b7\\u03bc\\u03b5\\u03c1\\u03bf\\u03bb\\u03cc\\u03b3\\u03b9\\u03bf)" } }; static UChar*** dataTable=0; enum { ENGLISH = 0, FRENCH = 1, CATALAN = 2, GREEK = 3, NORWEGIAN = 4 }; enum { LANG = 0, SCRIPT = 1, CTRY = 2, VAR = 3, NAME = 4, LANG3 = 5, CTRY3 = 6, LCID = 7, DLANG_EN = 8, DSCRIPT_EN = 9, DCTRY_EN = 10, DVAR_EN = 11, DNAME_EN = 12, DLANG_FR = 13, DSCRIPT_FR = 14, DCTRY_FR = 15, DVAR_FR = 16, DNAME_FR = 17, DLANG_CA = 18, DSCRIPT_CA = 19, DCTRY_CA = 20, DVAR_CA = 21, DNAME_CA = 22, DLANG_EL = 23, DSCRIPT_EL = 24, DCTRY_EL = 25, DVAR_EL = 26, DNAME_EL = 27 }; #define TESTCASE(name) addTest(root, &name, "tsutil/cloctst/" #name) void addLocaleTest(TestNode** root); void addLocaleTest(TestNode** root) { TESTCASE(TestObsoleteNames); /* srl- move */ TESTCASE(TestBasicGetters); TESTCASE(TestNullDefault); TESTCASE(TestPrefixes); TESTCASE(TestSimpleResourceInfo); TESTCASE(TestDisplayNames); TESTCASE(TestGetDisplayScriptPreFlighting21160); TESTCASE(TestGetAvailableLocales); TESTCASE(TestGetAvailableLocalesByType); TESTCASE(TestDataDirectory); #if !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION TESTCASE(TestISOFunctions); #endif TESTCASE(TestISO3Fallback); TESTCASE(TestUninstalledISO3Names); TESTCASE(TestSimpleDisplayNames); TESTCASE(TestVariantParsing); TESTCASE(TestKeywordVariants); TESTCASE(TestKeywordVariantParsing); TESTCASE(TestCanonicalization); TESTCASE(TestCanonicalizationBuffer); TESTCASE(TestKeywordSet); TESTCASE(TestKeywordSetError); TESTCASE(TestDisplayKeywords); TESTCASE(TestCanonicalization21749StackUseAfterScope); TESTCASE(TestDisplayKeywordValues); TESTCASE(TestGetBaseName); #if !UCONFIG_NO_FILE_IO TESTCASE(TestGetLocale); #endif TESTCASE(TestDisplayNameWarning); TESTCASE(Test21157CorrectTerminating); TESTCASE(TestNonexistentLanguageExemplars); TESTCASE(TestLocDataErrorCodeChaining); TESTCASE(TestLocDataWithRgTag); TESTCASE(TestLanguageExemplarsFallbacks); TESTCASE(TestCalendar); TESTCASE(TestDateFormat); TESTCASE(TestCollation); TESTCASE(TestULocale); TESTCASE(TestUResourceBundle); TESTCASE(TestDisplayName); TESTCASE(TestAcceptLanguage); TESTCASE(TestGetLocaleForLCID); TESTCASE(TestOrientation); TESTCASE(TestLikelySubtags); TESTCASE(TestToLanguageTag); TESTCASE(TestBug20132); TESTCASE(TestBug20149); TESTCASE(TestCDefaultLocale); TESTCASE(TestForLanguageTag); TESTCASE(TestLangAndRegionCanonicalize); TESTCASE(TestTrailingNull); TESTCASE(TestUnicodeDefines); TESTCASE(TestEnglishExemplarCharacters); TESTCASE(TestDisplayNameBrackets); TESTCASE(TestIllegalArgumentWhenNoDataWithNoSubstitute); TESTCASE(TestIsRightToLeft); TESTCASE(TestToUnicodeLocaleKey); TESTCASE(TestToLegacyKey); TESTCASE(TestToUnicodeLocaleType); TESTCASE(TestToLegacyType); TESTCASE(TestBadLocaleIDs); TESTCASE(TestBug20370); TESTCASE(TestBug20321UnicodeLocaleKey); TESTCASE(TestUsingDefaultWarning); TESTCASE(TestBug21449InfiniteLoop); TESTCASE(TestExcessivelyLongIDs); #if !UCONFIG_NO_FORMATTING TESTCASE(TestUldnNameVariants); #endif } /* testing uloc(), uloc_getName(), uloc_getLanguage(), uloc_getVariant(), uloc_getCountry() */ static void TestBasicGetters() { int32_t i; int32_t cap; UErrorCode status = U_ZERO_ERROR; char *testLocale = 0; char *temp = 0, *name = 0; log_verbose("Testing Basic Getters\n"); for (i = 0; i < LOCALE_SIZE; i++) { testLocale=(char*)malloc(sizeof(char) * (strlen(rawData2[NAME][i])+1)); strcpy(testLocale,rawData2[NAME][i]); log_verbose("Testing %s .....\n", testLocale); cap=uloc_getLanguage(testLocale, NULL, 0, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)malloc(sizeof(char) * (cap+1)); uloc_getLanguage(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getLanguage %s\n", myErrorName(status)); } if (0 !=strcmp(temp,rawData2[LANG][i])) { log_err(" Language code mismatch: %s versus %s\n", temp, rawData2[LANG][i]); } cap=uloc_getCountry(testLocale, temp, cap, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)realloc(temp, sizeof(char) * (cap+1)); uloc_getCountry(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getCountry %s\n", myErrorName(status)); } if (0 != strcmp(temp, rawData2[CTRY][i])) { log_err(" Country code mismatch: %s versus %s\n", temp, rawData2[CTRY][i]); } cap=uloc_getVariant(testLocale, temp, cap, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)realloc(temp, sizeof(char) * (cap+1)); uloc_getVariant(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getVariant %s\n", myErrorName(status)); } if (0 != strcmp(temp, rawData2[VAR][i])) { log_err("Variant code mismatch: %s versus %s\n", temp, rawData2[VAR][i]); } cap=uloc_getName(testLocale, NULL, 0, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; name=(char*)malloc(sizeof(char) * (cap+1)); uloc_getName(testLocale, name, cap+1, &status); } else if(status==U_ZERO_ERROR) { log_err("ERROR: in uloc_getName(%s,NULL,0,..), expected U_BUFFER_OVERFLOW_ERROR!\n", testLocale); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getName %s\n", myErrorName(status)); } if (0 != strcmp(name, rawData2[NAME][i])){ log_err(" Mismatch in getName: %s versus %s\n", name, rawData2[NAME][i]); } free(temp); free(name); free(testLocale); } } static void TestNullDefault() { UErrorCode status = U_ZERO_ERROR; char original[ULOC_FULLNAME_CAPACITY]; uprv_strcpy(original, uloc_getDefault()); uloc_setDefault("qq_BLA", &status); if (uprv_strcmp(uloc_getDefault(), "qq_BLA") != 0) { log_err(" Mismatch in uloc_setDefault: qq_BLA versus %s\n", uloc_getDefault()); } uloc_setDefault(NULL, &status); if (uprv_strcmp(uloc_getDefault(), original) != 0) { log_err(" uloc_setDefault(NULL, &status) didn't get the default locale back!\n"); } { /* Test that set & get of default locale work, and that * default locales are cached and reused, and not overwritten. */ const char *n_en_US; const char *n_fr_FR; const char *n2_en_US; status = U_ZERO_ERROR; uloc_setDefault("en_US", &status); n_en_US = uloc_getDefault(); if (strcmp(n_en_US, "en_US") != 0) { log_err("Wrong result from uloc_getDefault(). Expected \"en_US\", got \"%s\"\n", n_en_US); } uloc_setDefault("fr_FR", &status); n_fr_FR = uloc_getDefault(); if (strcmp(n_en_US, "en_US") != 0) { log_err("uloc_setDefault altered previously default string." "Expected \"en_US\", got \"%s\"\n", n_en_US); } if (strcmp(n_fr_FR, "fr_FR") != 0) { log_err("Wrong result from uloc_getDefault(). Expected \"fr_FR\", got %s\n", n_fr_FR); } uloc_setDefault("en_US", &status); n2_en_US = uloc_getDefault(); if (strcmp(n2_en_US, "en_US") != 0) { log_err("Wrong result from uloc_getDefault(). Expected \"en_US\", got \"%s\"\n", n_en_US); } if (n2_en_US != n_en_US) { log_err("Default locale cache failed to reuse en_US locale.\n"); } if (U_FAILURE(status)) { log_err("Failure returned from uloc_setDefault - \"%s\"\n", u_errorName(status)); } } uloc_setDefault(original, &status); if (U_FAILURE(status)) { log_err("Failed to change the default locale back to %s\n", original); } } /* Test the i- and x- and @ and . functionality */ #define PREFIXBUFSIZ 128 static void TestPrefixes() { int row = 0; int n; const char *loc, *expected; static const char * const testData[][7] = { /* NULL canonicalize() column means "expect same as getName()" */ {"sv", "", "FI", "AL", "sv-fi-al", "sv_FI_AL", NULL}, {"en", "", "GB", "", "en-gb", "en_GB", NULL}, {"i-hakka", "", "MT", "XEMXIJA", "i-hakka_MT_XEMXIJA", "i-hakka_MT_XEMXIJA", NULL}, {"i-hakka", "", "CN", "", "i-hakka_CN", "i-hakka_CN", NULL}, {"i-hakka", "", "MX", "", "I-hakka_MX", "i-hakka_MX", NULL}, {"x-klingon", "", "US", "SANJOSE", "X-KLINGON_us_SANJOSE", "x-klingon_US_SANJOSE", NULL}, {"hy", "", "", "AREVMDA", "hy_AREVMDA", "hy__AREVMDA", "hyw"}, {"de", "", "", "1901", "de-1901", "de__1901", NULL}, {"mr", "", "", "", "mr.utf8", "mr.utf8", "mr"}, {"de", "", "TV", "", "de-tv.koi8r", "de_TV.koi8r", "de_TV"}, {"x-piglatin", "", "ML", "", "x-piglatin_ML.MBE", "x-piglatin_ML.MBE", "x-piglatin_ML"}, /* Multibyte English */ {"i-cherokee", "","US", "", "i-Cherokee_US.utf7", "i-cherokee_US.utf7", "i-cherokee_US"}, {"x-filfli", "", "MT", "FILFLA", "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA"}, {"no", "", "NO", "NY", "no-no-ny.utf32@B", "no_NO_NY.utf32@B", "no_NO_NY_B"}, {"no", "", "NO", "", "no-no.utf32@B", "no_NO.utf32@B", "no_NO_B"}, {"no", "", "", "NY", "no__ny", "no__NY", NULL}, {"no", "", "", "", "no@ny", "no@ny", "no__NY"}, {"el", "Latn", "", "", "el-latn", "el_Latn", NULL}, {"en", "Cyrl", "RU", "", "en-cyrl-ru", "en_Cyrl_RU", NULL}, {"qq", "Qqqq", "QQ", "QQ", "qq_Qqqq_QQ_QQ", "qq_Qqqq_QQ_QQ", NULL}, {"qq", "Qqqq", "", "QQ", "qq_Qqqq__QQ", "qq_Qqqq__QQ", NULL}, {"ab", "Cdef", "GH", "IJ", "ab_cdef_gh_ij", "ab_Cdef_GH_IJ", NULL}, /* total garbage */ // Before ICU 64, ICU locale canonicalization had some additional mappings. // They were removed for ICU-20187 "drop support for long-obsolete locale ID variants". // The following now use standard canonicalization. {"zh", "Hans", "", "PINYIN", "zh-Hans-pinyin", "zh_Hans__PINYIN", "zh_Hans__PINYIN"}, {"zh", "Hant", "TW", "STROKE", "zh-hant_TW_STROKE", "zh_Hant_TW_STROKE", "zh_Hant_TW_STROKE"}, {NULL,NULL,NULL,NULL,NULL,NULL,NULL} }; static const char * const testTitles[] = { "uloc_getLanguage()", "uloc_getScript()", "uloc_getCountry()", "uloc_getVariant()", "name", "uloc_getName()", "uloc_canonicalize()" }; char buf[PREFIXBUFSIZ]; int32_t len; UErrorCode err; for(row=0;testData[row][0] != NULL;row++) { loc = testData[row][NAME]; log_verbose("Test #%d: %s\n", row, loc); err = U_ZERO_ERROR; len=0; buf[0]=0; for(n=0;n<=(NAME+2);n++) { if(n==NAME) continue; for(len=0;len [%s] (length %d)\n", row, testTitles[n], loc, buf, len); if(len != (int32_t)strlen(buf)) { log_err("#%d: %s on %s: -> [%s] (length returned %d, actual %d!)\n", row, testTitles[n], loc, buf, len, strlen(buf)+1); } /* see if they smashed something */ if(buf[len+1] != '%') { log_err("#%d: %s on %s: -> [%s] - wrote [%X] out ofbounds!\n", row, testTitles[n], loc, buf, buf[len+1]); } expected = testData[row][n]; if (expected == NULL && n == (NAME+2)) { /* NULL expected canonicalize() means "expect same as getName()" */ expected = testData[row][NAME+1]; } if(strcmp(buf, expected)) { log_err("#%d: %s on %s: -> [%s] (expected '%s'!)\n", row, testTitles[n], loc, buf, expected); } } } } } /* testing uloc_getISO3Language(), uloc_getISO3Country(), */ static void TestSimpleResourceInfo() { int32_t i; char* testLocale = 0; UChar* expected = 0; const char* temp; char temp2[20]; testLocale=(char*)malloc(sizeof(char) * 1); expected=(UChar*)malloc(sizeof(UChar) * 1); setUpDataTable(); log_verbose("Testing getISO3Language and getISO3Country\n"); for (i = 0; i < LOCALE_SIZE; i++) { testLocale=(char*)realloc(testLocale, sizeof(char) * (u_strlen(dataTable[NAME][i])+1)); u_austrcpy(testLocale, dataTable[NAME][i]); log_verbose("Testing %s ......\n", testLocale); temp=uloc_getISO3Language(testLocale); expected=(UChar*)realloc(expected, sizeof(UChar) * (strlen(temp) + 1)); u_uastrcpy(expected,temp); if (0 != u_strcmp(expected, dataTable[LANG3][i])) { log_err(" ISO-3 language code mismatch: %s versus %s\n", austrdup(expected), austrdup(dataTable[LANG3][i])); } temp=uloc_getISO3Country(testLocale); expected=(UChar*)realloc(expected, sizeof(UChar) * (strlen(temp) + 1)); u_uastrcpy(expected,temp); if (0 != u_strcmp(expected, dataTable[CTRY3][i])) { log_err(" ISO-3 Country code mismatch: %s versus %s\n", austrdup(expected), austrdup(dataTable[CTRY3][i])); } sprintf(temp2, "%x", (int)uloc_getLCID(testLocale)); if (strcmp(temp2, rawData2[LCID][i]) != 0) { log_err("LCID mismatch: %s versus %s\n", temp2 , rawData2[LCID][i]); } } free(expected); free(testLocale); cleanUpDataTable(); } /* if len < 0, we convert until we hit UChar 0x0000, which is not output. will add trailing null * if there's room but won't be included in result. result < 0 indicates an error. * Returns the number of chars written (not those that would be written if there's enough room.*/ static int32_t UCharsToEscapedAscii(const UChar* utext, int32_t len, char* resultChars, int32_t buflen) { static const struct { char escapedChar; UChar sourceVal; } ESCAPE_MAP[] = { /*a*/ {'a', 0x07}, /*b*/ {'b', 0x08}, /*e*/ {'e', 0x1b}, /*f*/ {'f', 0x0c}, /*n*/ {'n', 0x0a}, /*r*/ {'r', 0x0d}, /*t*/ {'t', 0x09}, /*v*/ {'v', 0x0b} }; static const int32_t ESCAPE_MAP_LENGTH = UPRV_LENGTHOF(ESCAPE_MAP); static const char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int32_t i, j; int32_t resultLen = 0; const int32_t limit = len<0 ? buflen : len; /* buflen is long enough to hit the buffer limit */ const int32_t escapeLimit1 = buflen-2; const int32_t escapeLimit2 = buflen-6; UChar uc; if(utext==NULL || resultChars==NULL || buflen<0) { return -1; } for(i=0;iescapeLimit1) { break; } resultChars[resultLen++]='\\'; resultChars[resultLen++]=ESCAPE_MAP[j].escapedChar; continue; } } else if(uc<0x7f) { u_austrncpy(resultChars + resultLen, &uc, 1); resultLen++; continue; } if(resultLen>escapeLimit2) { break; } /* have to escape the uchar */ resultChars[resultLen++]='\\'; resultChars[resultLen++]='u'; resultChars[resultLen++]=HEX_DIGITS[(uc>>12)&0xff]; resultChars[resultLen++]=HEX_DIGITS[(uc>>8)&0xff]; resultChars[resultLen++]=HEX_DIGITS[(uc>>4)&0xff]; resultChars[resultLen++]=HEX_DIGITS[uc&0xff]; } if(resultLen %s\n", aLocale, testL[i], u_errorName(errorCode)); } else { expectBuffer = CharsToUChars(expect[i]); if(u_strcmp(buffer,expectBuffer)) { log_data_err("FAIL in uloc_getDisplayName(%s,%s,..) expected '%s' got '%s' (Are you missing data?)\n", aLocale, testL[i], expect[i], austrdup(buffer)); } else { log_verbose("pass in uloc_getDisplayName(%s,%s,..) got '%s'\n", aLocale, testL[i], expect[i]); } free(expectBuffer); } } } /* test that we properly preflight and return data when there's a non-default pattern, see ticket #8262. */ { int32_t i; static const char *locale="az_Cyrl"; static const char *displayLocale="ja"; static const char *expectedChars = "\\u30a2\\u30bc\\u30eb\\u30d0\\u30a4\\u30b8\\u30e3\\u30f3\\u8a9e " "(\\u30ad\\u30ea\\u30eb\\u6587\\u5b57)"; UErrorCode ec=U_ZERO_ERROR; UChar result[256]; int32_t len; int32_t preflightLen=uloc_getDisplayName(locale, displayLocale, NULL, 0, &ec); /* inconvenient semantics when preflighting, this condition is expected... */ if(ec==U_BUFFER_OVERFLOW_ERROR) { ec=U_ZERO_ERROR; } len=uloc_getDisplayName(locale, displayLocale, result, UPRV_LENGTHOF(result), &ec); if(U_FAILURE(ec)) { log_err("uloc_getDisplayName(%s, %s...) returned error: %s", locale, displayLocale, u_errorName(ec)); } else { UChar *expected=CharsToUChars(expectedChars); int32_t expectedLen=u_strlen(expected); if(len!=expectedLen) { log_data_err("uloc_getDisplayName(%s, %s...) returned string of length %d, expected length %d", locale, displayLocale, len, expectedLen); } else if(preflightLen!=expectedLen) { log_err("uloc_getDisplayName(%s, %s...) returned preflight length %d, expected length %d", locale, displayLocale, preflightLen, expectedLen); } else if(u_strncmp(result, expected, len)) { int32_t cap=len*6+1; /* worst case + space for trailing null */ char* resultChars=(char*)malloc(cap); int32_t resultCharsLen=UCharsToEscapedAscii(result, len, resultChars, cap); if(resultCharsLen<0 || resultCharsLen=0;--i) { len=uloc_getDisplayName(locale, displayLocale, result, i, &ec); if(ec==U_BUFFER_OVERFLOW_ERROR) { ec=U_ZERO_ERROR; } if(U_FAILURE(ec)) { log_err("using buffer of length %d returned error %s", i, u_errorName(ec)); break; } if(len!=expectedLen) { log_err("with buffer of length %d, expected length %d but got %d", i, expectedLen, len); break; } /* There's no guarantee about what's in the buffer if we've overflowed, in particular, * we don't know that it's been filled, so no point in checking. */ } } free(expected); } } } /** * ICU-21160 test the pre-flighting call to uloc_getDisplayScript returns the actual length needed * for the result buffer. */ static void TestGetDisplayScriptPreFlighting21160() { const char* locale = "und-Latn"; const char* inlocale = "de"; UErrorCode ec = U_ZERO_ERROR; UChar* result = NULL; int32_t length = uloc_getDisplayScript(locale, inlocale, NULL, 0, &ec) + 1; ec = U_ZERO_ERROR; result=(UChar*)malloc(sizeof(UChar) * length); length = uloc_getDisplayScript(locale, inlocale, result, length, &ec); if (U_FAILURE(ec)) { log_err("uloc_getDisplayScript length %d returned error %s", length, u_errorName(ec)); } free(result); } /* test for uloc_getAvailable() and uloc_countAvailable()*/ static void TestGetAvailableLocales() { const char *locList; int32_t locCount,i; log_verbose("Testing the no of available locales\n"); locCount=uloc_countAvailable(); if (locCount == 0) log_data_err("countAvailable() returned an empty list!\n"); /* use something sensible w/o hardcoding the count */ else if(locCount < 0){ log_data_err("countAvailable() returned a wrong value!= %d\n", locCount); } else{ log_info("Number of locales returned = %d\n", locCount); } for(i=0;idisplayLocale != NULL; itemPtr++) { ULocaleDisplayNames * uldn; UErrorCode status; UChar expectRegionName[kDisplayNameBracketsMax]; UChar expectLocaleName[kDisplayNameBracketsMax]; UChar getName[kDisplayNameBracketsMax]; int32_t ulen; (void) u_unescape(itemPtr->regionName, expectRegionName, kDisplayNameBracketsMax); (void) u_unescape(itemPtr->localeName, expectLocaleName, kDisplayNameBracketsMax); status = U_ZERO_ERROR; ulen = uloc_getDisplayCountry(itemPtr->namedLocale, itemPtr->displayLocale, getName, kDisplayNameBracketsMax, &status); if ( U_FAILURE(status) || u_strcmp(getName, expectRegionName) != 0 ) { log_data_err("uloc_getDisplayCountry for displayLocale %s and namedLocale %s returns unexpected name or status %s\n", itemPtr->displayLocale, itemPtr->namedLocale, myErrorName(status)); } status = U_ZERO_ERROR; ulen = uloc_getDisplayName(itemPtr->namedLocale, itemPtr->displayLocale, getName, kDisplayNameBracketsMax, &status); if ( U_FAILURE(status) || u_strcmp(getName, expectLocaleName) != 0 ) { log_data_err("uloc_getDisplayName for displayLocale %s and namedLocale %s returns unexpected name or status %s\n", itemPtr->displayLocale, itemPtr->namedLocale, myErrorName(status)); } #if !UCONFIG_NO_FORMATTING status = U_ZERO_ERROR; uldn = uldn_open(itemPtr->displayLocale, ULDN_STANDARD_NAMES, &status); if (U_SUCCESS(status)) { status = U_ZERO_ERROR; ulen = uldn_regionDisplayName(uldn, itemPtr->namedRegion, getName, kDisplayNameBracketsMax, &status); if ( U_FAILURE(status) || u_strcmp(getName, expectRegionName) != 0 ) { log_data_err("uldn_regionDisplayName for displayLocale %s and namedRegion %s returns unexpected name or status %s\n", itemPtr->displayLocale, itemPtr->namedRegion, myErrorName(status)); } status = U_ZERO_ERROR; ulen = uldn_localeDisplayName(uldn, itemPtr->namedLocale, getName, kDisplayNameBracketsMax, &status); if ( U_FAILURE(status) || u_strcmp(getName, expectLocaleName) != 0 ) { log_data_err("uldn_localeDisplayName for displayLocale %s and namedLocale %s returns unexpected name or status %s\n", itemPtr->displayLocale, itemPtr->namedLocale, myErrorName(status)); } uldn_close(uldn); } else { log_data_err("uldn_open fails for displayLocale %s, status=%s\n", itemPtr->displayLocale, u_errorName(status)); } #endif (void)ulen; /* Suppress variable not used warning */ } } /*------------------------------ * TestIllegalArgumentWhenNoDataWithNoSubstitute */ static void TestIllegalArgumentWhenNoDataWithNoSubstitute() { #if !UCONFIG_NO_FORMATTING UErrorCode status = U_ZERO_ERROR; UChar getName[kDisplayNameBracketsMax]; UDisplayContext contexts[] = { UDISPCTX_NO_SUBSTITUTE, }; ULocaleDisplayNames* ldn = uldn_openForContext("en", contexts, 1, &status); uldn_localeDisplayName(ldn, "efg", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_localeDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_languageDisplayName(ldn, "zz", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_languageDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_scriptDisplayName(ldn, "Aaaa", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_scriptDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_regionDisplayName(ldn, "KK", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_regionDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_variantDisplayName(ldn, "ZZ", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_variantDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_keyDisplayName(ldn, "zz", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_keyDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } status = U_ZERO_ERROR; uldn_keyValueDisplayName(ldn, "ca", "zz", getName, kDisplayNameBracketsMax, &status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("FAIL uldn_keyValueDisplayName should return U_ILLEGAL_ARGUMENT_ERROR " "while no resource under UDISPCTX_NO_SUBSTITUTE"); } uldn_close(ldn); #endif } /*------------------------------ * TestISOFunctions */ #if !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION /* test for uloc_getISOLanguages, uloc_getISOCountries */ static void TestISOFunctions() { const char* const* str=uloc_getISOLanguages(); const char* const* str1=uloc_getISOCountries(); const char* test; const char *key = NULL; int32_t count = 0, skipped = 0; int32_t expect; UResourceBundle *res; UResourceBundle *subRes; UErrorCode status = U_ZERO_ERROR; /* test getISOLanguages*/ /*str=uloc_getISOLanguages(); */ log_verbose("Testing ISO Languages: \n"); /* use structLocale - this data is no longer in root */ res = ures_openDirect(loadTestData(&status), "structLocale", &status); subRes = ures_getByKey(res, "Languages", NULL, &status); if (U_FAILURE(status)) { log_data_err("There is an error in structLocale's ures_getByKey(\"Languages\"), status=%s\n", u_errorName(status)); return; } expect = ures_getSize(subRes); for(count = 0; *(str+count) != 0; count++) { key = NULL; test = *(str+count); status = U_ZERO_ERROR; do { /* Skip over language tags. This API only returns language codes. */ skipped += (key != NULL); ures_getNextString(subRes, NULL, &key, &status); } while (key != NULL && strchr(key, '_')); if(key == NULL) break; /* TODO: Consider removing sh, which is deprecated */ if(strcmp(key,"root") == 0 || strcmp(key,"Fallback") == 0 || strcmp(key,"sh") == 0) { ures_getNextString(subRes, NULL, &key, &status); skipped++; } #if U_CHARSET_FAMILY==U_ASCII_FAMILY /* This code only works on ASCII machines where the keys are stored in ASCII order */ if(strcmp(test,key)) { /* The first difference usually implies the place where things get out of sync */ log_err("FAIL Language diff at offset %d, \"%s\" != \"%s\"\n", count, test, key); } #endif if(!strcmp(test,"in")) log_err("FAIL getISOLanguages() has obsolete language code %s\n", test); if(!strcmp(test,"iw")) log_err("FAIL getISOLanguages() has obsolete language code %s\n", test); if(!strcmp(test,"ji")) log_err("FAIL getISOLanguages() has obsolete language code %s\n", test); if(!strcmp(test,"jw")) log_err("FAIL getISOLanguages() has obsolete language code %s\n", test); if(!strcmp(test,"sh")) log_err("FAIL getISOLanguages() has obsolete language code %s\n", test); } expect -= skipped; /* Ignore the skipped resources from structLocale */ if(count!=expect) { log_err("There is an error in getISOLanguages, got %d, expected %d (as per structLocale)\n", count, expect); } subRes = ures_getByKey(res, "Countries", subRes, &status); log_verbose("Testing ISO Countries"); skipped = 0; expect = ures_getSize(subRes) - 1; /* Skip ZZ */ for(count = 0; *(str1+count) != 0; count++) { key = NULL; test = *(str1+count); do { /* Skip over numeric UN tags. This API only returns ISO-3166 codes. */ skipped += (key != NULL); ures_getNextString(subRes, NULL, &key, &status); } while (key != NULL && strlen(key) != 2); if(key == NULL) break; /* TODO: Consider removing CS, which is deprecated */ while(strcmp(key,"QO") == 0 || strcmp(key,"QU") == 0 || strcmp(key,"CS") == 0) { ures_getNextString(subRes, NULL, &key, &status); skipped++; } #if U_CHARSET_FAMILY==U_ASCII_FAMILY /* This code only works on ASCII machines where the keys are stored in ASCII order */ if(strcmp(test,key)) { /* The first difference usually implies the place where things get out of sync */ log_err("FAIL Country diff at offset %d, \"%s\" != \"%s\"\n", count, test, key); } #endif if(!strcmp(test,"FX")) log_err("FAIL getISOCountries() has obsolete country code %s\n", test); if(!strcmp(test,"YU")) log_err("FAIL getISOCountries() has obsolete country code %s\n", test); if(!strcmp(test,"ZR")) log_err("FAIL getISOCountries() has obsolete country code %s\n", test); } ures_getNextString(subRes, NULL, &key, &status); if (strcmp(key, "ZZ") != 0) { log_err("ZZ was expected to be the last entry in structLocale, but got %s\n", key); } #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY /* On EBCDIC machines, the numbers are sorted last. Account for those in the skipped value too. */ key = NULL; do { /* Skip over numeric UN tags. uloc_getISOCountries only returns ISO-3166 codes. */ skipped += (key != NULL); ures_getNextString(subRes, NULL, &key, &status); } while (U_SUCCESS(status) && key != NULL && strlen(key) != 2); #endif expect -= skipped; /* Ignore the skipped resources from structLocale */ if(count!=expect) { log_err("There is an error in getISOCountries, got %d, expected %d \n", count, expect); } ures_close(subRes); ures_close(res); } #endif static void setUpDataTable() { int32_t i,j; dataTable = (UChar***)(calloc(sizeof(UChar**),LOCALE_INFO_SIZE)); for (i = 0; i < LOCALE_INFO_SIZE; i++) { dataTable[i] = (UChar**)(calloc(sizeof(UChar*),LOCALE_SIZE)); for (j = 0; j < LOCALE_SIZE; j++){ dataTable[i][j] = CharsToUChars(rawData2[i][j]); } } } static void cleanUpDataTable() { int32_t i,j; if(dataTable != NULL) { for (i=0; i status %s. Got %s instead\n", testCases[i].localeID, u_errorName(testCases[i].expectedStatus), u_errorName(status)); } status = U_ZERO_ERROR; if(keywords) { if((keyCount = uenum_count(keywords, &status)) != testCases[i].numKeywords) { log_err("Expected to get %i keywords, got %i\n", testCases[i].numKeywords, keyCount); } if(keyCount) { j = 0; while((keyword = uenum_next(keywords, &keywordLen, &status))) { if(strcmp(keyword, testCases[i].expectedKeywords[j]) != 0) { log_err("Expected to get keyword value %s, got %s\n", testCases[i].expectedKeywords[j], keyword); } j++; } j = 0; uenum_reset(keywords, &status); while((keyword = uenum_next(keywords, &keywordLen, &status))) { if(strcmp(keyword, testCases[i].expectedKeywords[j]) != 0) { log_err("Expected to get keyword value %s, got %s\n", testCases[i].expectedKeywords[j], keyword); } j++; } } uenum_close(keywords); } status = U_ZERO_ERROR; resultLen = uloc_getName(testCases[i].localeID, buffer, 256, &status); (void)resultLen; U_ASSERT(resultLen < 256); if (U_SUCCESS(status)) { if (testCases[i].expectedLocaleID == 0) { log_err("Expected uloc_getName(\"%s\") to fail; got \"%s\"\n", testCases[i].localeID, buffer); } else if (uprv_strcmp(testCases[i].expectedLocaleID, buffer) != 0) { log_err("Expected uloc_getName(\"%s\") => \"%s\"; got \"%s\"\n", testCases[i].localeID, testCases[i].expectedLocaleID, buffer); } } else { if (testCases[i].expectedLocaleID != 0) { log_err("Expected uloc_getName(\"%s\") => \"%s\"; but returned error: %s\n", testCases[i].localeID, testCases[i].expectedLocaleID, buffer, u_errorName(status)); } } status = U_ZERO_ERROR; resultLen = uloc_getBaseName(testCases[i].localeID, buffer, 256, &status); U_ASSERT(resultLen < 256); if (U_SUCCESS(status)) { if (testCases[i].expectedLocaleIDNoKeywords == 0) { log_err("Expected uloc_getBaseName(\"%s\") to fail; got \"%s\"\n", testCases[i].localeID, buffer); } else if (uprv_strcmp(testCases[i].expectedLocaleIDNoKeywords, buffer) != 0) { log_err("Expected uloc_getBaseName(\"%s\") => \"%s\"; got \"%s\"\n", testCases[i].localeID, testCases[i].expectedLocaleIDNoKeywords, buffer); } } else { if (testCases[i].expectedLocaleIDNoKeywords != 0) { log_err("Expected uloc_getBaseName(\"%s\") => \"%s\"; but returned error: %s\n", testCases[i].localeID, testCases[i].expectedLocaleIDNoKeywords, buffer, u_errorName(status)); } } status = U_ZERO_ERROR; resultLen = uloc_canonicalize(testCases[i].localeID, buffer, 256, &status); U_ASSERT(resultLen < 256); if (U_SUCCESS(status)) { if (testCases[i].expectedCanonicalID == 0) { log_err("Expected uloc_canonicalize(\"%s\") to fail; got \"%s\"\n", testCases[i].localeID, buffer); } else if (uprv_strcmp(testCases[i].expectedCanonicalID, buffer) != 0) { log_err("Expected uloc_canonicalize(\"%s\") => \"%s\"; got \"%s\"\n", testCases[i].localeID, testCases[i].expectedCanonicalID, buffer); } } else { if (testCases[i].expectedCanonicalID != 0) { log_err("Expected uloc_canonicalize(\"%s\") => \"%s\"; but returned error: %s\n", testCases[i].localeID, testCases[i].expectedCanonicalID, buffer, u_errorName(status)); } } } } static void TestKeywordVariantParsing(void) { static const struct { const char *localeID; const char *keyword; const char *expectedValue; /* NULL if failure is expected */ } testCases[] = { { "de_DE@ C o ll A t i o n = Phonebook ", "c o ll a t i o n", NULL }, /* malformed key name */ { "de_DE", "collation", ""}, { "de_DE@collation=PHONEBOOK", "collation", "PHONEBOOK" }, { "de_DE@currency = euro; CoLLaTion = PHONEBOOk", "collatiON", "PHONEBOOk" }, }; UErrorCode status; int32_t i = 0; int32_t resultLen = 0; char buffer[256]; for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { *buffer = 0; status = U_ZERO_ERROR; resultLen = uloc_getKeywordValue(testCases[i].localeID, testCases[i].keyword, buffer, 256, &status); (void)resultLen; /* Suppress set but not used warning. */ if (testCases[i].expectedValue) { /* expect success */ if (U_FAILURE(status)) { log_err("Expected to extract \"%s\" from \"%s\" for keyword \"%s\". Instead got status %s\n", testCases[i].expectedValue, testCases[i].localeID, testCases[i].keyword, u_errorName(status)); } else if (uprv_strcmp(testCases[i].expectedValue, buffer) != 0) { log_err("Expected to extract \"%s\" from \"%s\" for keyword \"%s\". Instead got \"%s\"\n", testCases[i].expectedValue, testCases[i].localeID, testCases[i].keyword, buffer); } } else if (U_SUCCESS(status)) { /* expect failure */ log_err("Expected failure but got success from \"%s\" for keyword \"%s\". Got \"%s\"\n", testCases[i].localeID, testCases[i].keyword, buffer); } } } static const struct { const char *l; /* locale */ const char *k; /* kw */ const char *v; /* value */ const char *x; /* expected */ } kwSetTestCases[] = { #if 1 { "en_US", "calendar", "japanese", "en_US@calendar=japanese" }, { "en_US@", "calendar", "japanese", "en_US@calendar=japanese" }, { "en_US@calendar=islamic", "calendar", "japanese", "en_US@calendar=japanese" }, { "en_US@calendar=slovakian", "calendar", "gregorian", "en_US@calendar=gregorian" }, /* don't know what this means, but it has the same # of chars as gregorian */ { "en_US@calendar=gregorian", "calendar", "japanese", "en_US@calendar=japanese" }, { "de", "Currency", "CHF", "de@currency=CHF" }, { "de", "Currency", "CHF", "de@currency=CHF" }, { "en_US@collation=phonebook", "calendar", "japanese", "en_US@calendar=japanese;collation=phonebook" }, { "en_US@calendar=japanese", "collation", "phonebook", "en_US@calendar=japanese;collation=phonebook" }, { "de@collation=phonebook", "Currency", "CHF", "de@collation=phonebook;currency=CHF" }, { "en_US@calendar=gregorian;collation=phonebook", "calendar", "japanese", "en_US@calendar=japanese;collation=phonebook" }, { "en_US@calendar=slovakian;collation=phonebook", "calendar", "gregorian", "en_US@calendar=gregorian;collation=phonebook" }, /* don't know what this means, but it has the same # of chars as gregorian */ { "en_US@calendar=slovakian;collation=videobook", "collation", "phonebook", "en_US@calendar=slovakian;collation=phonebook" }, /* don't know what this means, but it has the same # of chars as gregorian */ { "en_US@calendar=islamic;collation=phonebook", "calendar", "japanese", "en_US@calendar=japanese;collation=phonebook" }, { "de@collation=phonebook", "Currency", "CHF", "de@collation=phonebook;currency=CHF" }, #endif #if 1 { "mt@a=0;b=1;c=2;d=3", "c","j", "mt@a=0;b=1;c=j;d=3" }, { "mt@a=0;b=1;c=2;d=3", "x","j", "mt@a=0;b=1;c=2;d=3;x=j" }, { "mt@a=0;b=1;c=2;d=3", "a","f", "mt@a=f;b=1;c=2;d=3" }, { "mt@a=0;aa=1;aaa=3", "a","x", "mt@a=x;aa=1;aaa=3" }, { "mt@a=0;aa=1;aaa=3", "aa","x", "mt@a=0;aa=x;aaa=3" }, { "mt@a=0;aa=1;aaa=3", "aaa","x", "mt@a=0;aa=1;aaa=x" }, { "mt@a=0;aa=1;aaa=3", "a","yy", "mt@a=yy;aa=1;aaa=3" }, { "mt@a=0;aa=1;aaa=3", "aa","yy", "mt@a=0;aa=yy;aaa=3" }, { "mt@a=0;aa=1;aaa=3", "aaa","yy", "mt@a=0;aa=1;aaa=yy" }, #endif #if 1 /* removal tests */ /* 1. removal of item at end */ { "de@collation=phonebook;currency=CHF", "currency", "", "de@collation=phonebook" }, { "de@collation=phonebook;currency=CHF", "currency", NULL, "de@collation=phonebook" }, /* 2. removal of item at beginning */ { "de@collation=phonebook;currency=CHF", "collation", "", "de@currency=CHF" }, { "de@collation=phonebook;currency=CHF", "collation", NULL, "de@currency=CHF" }, /* 3. removal of an item not there */ { "de@collation=phonebook;currency=CHF", "calendar", NULL, "de@collation=phonebook;currency=CHF" }, /* 4. removal of only item */ { "de@collation=phonebook", "collation", NULL, "de" }, #endif { "de@collation=phonebook", "Currency", "CHF", "de@collation=phonebook;currency=CHF" }, /* cases with legal extra spacing */ /*31*/{ "en_US@ calendar = islamic", "calendar", "japanese", "en_US@calendar=japanese" }, /*32*/{ "en_US@ calendar = gregorian ; collation = phonebook", "calendar", "japanese", "en_US@calendar=japanese;collation=phonebook" }, /*33*/{ "en_US@ calendar = islamic", "currency", "CHF", "en_US@calendar=islamic;currency=CHF" }, /*34*/{ "en_US@ currency = CHF", "calendar", "japanese", "en_US@calendar=japanese;currency=CHF" }, /* cases in which setKeywordValue expected to fail (implied by NULL for expected); locale need not be canonical */ /*35*/{ "en_US@calendar=gregorian;", "calendar", "japanese", NULL }, /*36*/{ "en_US@calendar=gregorian;=", "calendar", "japanese", NULL }, /*37*/{ "en_US@calendar=gregorian;currency=", "calendar", "japanese", NULL }, /*38*/{ "en_US@=", "calendar", "japanese", NULL }, /*39*/{ "en_US@=;", "calendar", "japanese", NULL }, /*40*/{ "en_US@= ", "calendar", "japanese", NULL }, /*41*/{ "en_US@ =", "calendar", "japanese", NULL }, /*42*/{ "en_US@ = ", "calendar", "japanese", NULL }, /*43*/{ "en_US@=;calendar=gregorian", "calendar", "japanese", NULL }, /*44*/{ "en_US@= calen dar = gregorian", "calendar", "japanese", NULL }, /*45*/{ "en_US@= calendar = greg orian", "calendar", "japanese", NULL }, /*46*/{ "en_US@=;cal...endar=gregorian", "calendar", "japanese", NULL }, /*47*/{ "en_US@=;calendar=greg...orian", "calendar", "japanese", NULL }, /*48*/{ "en_US@calendar=gregorian", "cale ndar", "japanese", NULL }, /*49*/{ "en_US@calendar=gregorian", "calendar", "japa..nese", NULL }, /* cases in which getKeywordValue and setKeyword expected to fail (implied by NULL for value and expected) */ /*50*/{ "en_US@=", "calendar", NULL, NULL }, /*51*/{ "en_US@=;", "calendar", NULL, NULL }, /*52*/{ "en_US@= ", "calendar", NULL, NULL }, /*53*/{ "en_US@ =", "calendar", NULL, NULL }, /*54*/{ "en_US@ = ", "calendar", NULL, NULL }, /*55*/{ "en_US@=;calendar=gregorian", "calendar", NULL, NULL }, /*56*/{ "en_US@= calen dar = gregorian", "calendar", NULL, NULL }, /*57*/{ "en_US@= calendar = greg orian", "calendar", NULL, NULL }, /*58*/{ "en_US@=;cal...endar=gregorian", "calendar", NULL, NULL }, /*59*/{ "en_US@=;calendar=greg...orian", "calendar", NULL, NULL }, /*60*/{ "en_US@calendar=gregorian", "cale ndar", NULL, NULL }, }; static void TestKeywordSet(void) { int32_t i = 0; int32_t resultLen = 0; char buffer[1024]; char cbuffer[1024]; for(i = 0; i < UPRV_LENGTHOF(kwSetTestCases); i++) { UErrorCode status = U_ZERO_ERROR; memset(buffer,'%',1023); strcpy(buffer, kwSetTestCases[i].l); if (kwSetTestCases[i].x != NULL) { uloc_canonicalize(kwSetTestCases[i].l, cbuffer, 1023, &status); if(strcmp(buffer,cbuffer)) { log_verbose("note: [%d] wasn't canonical, should be: '%s' not '%s'. Won't check for canonicity in output.\n", i, cbuffer, buffer); } /* sanity check test case results for canonicity */ uloc_canonicalize(kwSetTestCases[i].x, cbuffer, 1023, &status); if(strcmp(kwSetTestCases[i].x,cbuffer)) { log_err("%s:%d: ERROR: kwSetTestCases[%d].x = '%s', should be %s (must be canonical)\n", __FILE__, __LINE__, i, kwSetTestCases[i].x, cbuffer); } status = U_ZERO_ERROR; resultLen = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, 1023, &status); if(U_FAILURE(status)) { log_err("Err on test case %d for setKeywordValue: got error %s\n", i, u_errorName(status)); } else if(strcmp(buffer,kwSetTestCases[i].x) || ((int32_t)strlen(buffer)!=resultLen)) { log_err("FAIL: #%d setKeywordValue: %s + [%s=%s] -> %s (%d) expected %s (%d)\n", i, kwSetTestCases[i].l, kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, resultLen, kwSetTestCases[i].x, strlen(buffer)); } else { log_verbose("pass: #%d: %s + [%s=%s] -> %s\n", i, kwSetTestCases[i].l, kwSetTestCases[i].k, kwSetTestCases[i].v,buffer); } if (kwSetTestCases[i].v != NULL && kwSetTestCases[i].v[0] != 0) { status = U_ZERO_ERROR; resultLen = uloc_getKeywordValue(kwSetTestCases[i].x, kwSetTestCases[i].k, buffer, 1023, &status); if(U_FAILURE(status)) { log_err("Err on test case %d for getKeywordValue: got error %s\n", i, u_errorName(status)); } else if (resultLen != (int32_t)uprv_strlen(kwSetTestCases[i].v) || uprv_strcmp(buffer, kwSetTestCases[i].v) != 0) { log_err("FAIL: #%d getKeywordValue: got %s (%d) expected %s (%d)\n", i, buffer, resultLen, kwSetTestCases[i].v, uprv_strlen(kwSetTestCases[i].v)); } } } else { /* test cases expected to result in error */ status = U_ZERO_ERROR; resultLen = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, 1023, &status); if(U_SUCCESS(status)) { log_err("Err on test case %d for setKeywordValue: expected to fail but succeeded, got %s (%d)\n", i, buffer, resultLen); } if (kwSetTestCases[i].v == NULL) { status = U_ZERO_ERROR; strcpy(cbuffer, kwSetTestCases[i].l); resultLen = uloc_getKeywordValue(cbuffer, kwSetTestCases[i].k, buffer, 1023, &status); if(U_SUCCESS(status)) { log_err("Err on test case %d for getKeywordValue: expected to fail but succeeded\n", i); } } } } } static void TestKeywordSetError(void) { char buffer[1024]; UErrorCode status; int32_t res; int32_t i; int32_t blen; /* 0-test whether an error condition modifies the buffer at all */ blen=0; i=0; memset(buffer,'%',1023); status = U_ZERO_ERROR; res = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, blen, &status); if(status != U_ILLEGAL_ARGUMENT_ERROR) { log_err("expected illegal err got %s\n", u_errorName(status)); return; } /* if(res!=strlen(kwSetTestCases[i].x)) { log_err("expected result %d got %d\n", strlen(kwSetTestCases[i].x), res); return; } */ if(buffer[blen]!='%') { log_err("Buffer byte %d was modified: now %c\n", blen, buffer[blen]); return; } log_verbose("0-buffer modify OK\n"); for(i=0;i<=2;i++) { /* 1- test a short buffer with growing text */ blen=(int32_t)strlen(kwSetTestCases[i].l)+1; memset(buffer,'%',1023); strcpy(buffer,kwSetTestCases[i].l); status = U_ZERO_ERROR; res = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, blen, &status); if(status != U_BUFFER_OVERFLOW_ERROR) { log_err("expected buffer overflow on buffer %d got %s, len %d (%s + [%s=%s])\n", blen, u_errorName(status), res, kwSetTestCases[i].l, kwSetTestCases[i].k, kwSetTestCases[i].v); return; } if(res!=(int32_t)strlen(kwSetTestCases[i].x)) { log_err("expected result %d got %d\n", strlen(kwSetTestCases[i].x), res); return; } if(buffer[blen]!='%') { log_err("Buffer byte %d was modified: now %c\n", blen, buffer[blen]); return; } log_verbose("1/%d-buffer modify OK\n",i); } for(i=3;i<=4;i++) { /* 2- test a short buffer - text the same size or shrinking */ blen=(int32_t)strlen(kwSetTestCases[i].l)+1; memset(buffer,'%',1023); strcpy(buffer,kwSetTestCases[i].l); status = U_ZERO_ERROR; res = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, blen, &status); if(status != U_ZERO_ERROR) { log_err("expected zero error got %s\n", u_errorName(status)); return; } if(buffer[blen+1]!='%') { log_err("Buffer byte %d was modified: now %c\n", blen+1, buffer[blen+1]); return; } if(res!=(int32_t)strlen(kwSetTestCases[i].x)) { log_err("expected result %d got %d\n", strlen(kwSetTestCases[i].x), res); return; } if(strcmp(buffer,kwSetTestCases[i].x) || ((int32_t)strlen(buffer)!=res)) { log_err("FAIL: #%d: %s + [%s=%s] -> %s (%d) expected %s (%d)\n", i, kwSetTestCases[i].l, kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, res, kwSetTestCases[i].x, strlen(buffer)); } else { log_verbose("pass: #%d: %s + [%s=%s] -> %s\n", i, kwSetTestCases[i].l, kwSetTestCases[i].k, kwSetTestCases[i].v, buffer); } log_verbose("2/%d-buffer modify OK\n",i); } } static int32_t _canonicalize(int32_t selector, /* 0==getName, 1==canonicalize */ const char* localeID, char* result, int32_t resultCapacity, UErrorCode* ec) { /* YOU can change this to use function pointers if you like */ switch (selector) { case 0: return uloc_getName(localeID, result, resultCapacity, ec); case 1: return uloc_canonicalize(localeID, result, resultCapacity, ec); default: return -1; } } static void TestCanonicalization(void) { static const struct { const char *localeID; /* input */ const char *getNameID; /* expected getName() result */ const char *canonicalID; /* expected canonicalize() result */ } testCases[] = { { "ca_ES-with-extra-stuff-that really doesn't make any sense-unless-you're trying to increase code coverage", "ca_ES_WITH_EXTRA_STUFF_THAT REALLY DOESN'T MAKE ANY SENSE_UNLESS_YOU'RE TRYING TO INCREASE CODE COVERAGE", "ca_ES_WITH_EXTRA_STUFF_THAT REALLY DOESN'T MAKE ANY SENSE_UNLESS_YOU'RE TRYING TO INCREASE CODE COVERAGE"}, { "zh@collation=pinyin", "zh@collation=pinyin", "zh@collation=pinyin" }, { "zh_CN@collation=pinyin", "zh_CN@collation=pinyin", "zh_CN@collation=pinyin" }, { "zh_CN_CA@collation=pinyin", "zh_CN_CA@collation=pinyin", "zh_CN_CA@collation=pinyin" }, { "en_US_POSIX", "en_US_POSIX", "en_US_POSIX" }, { "hy_AM_REVISED", "hy_AM_REVISED", "hy_AM_REVISED" }, { "no_NO_NY", "no_NO_NY", "no_NO_NY" /* not: "nn_NO" [alan ICU3.0] */ }, { "no@ny", "no@ny", "no__NY" /* not: "nn" [alan ICU3.0] */ }, /* POSIX ID */ { "no-no.utf32@B", "no_NO.utf32@B", "no_NO_B" /* not: "nb_NO_B" [alan ICU3.0] */ }, /* POSIX ID */ { "qz-qz@Euro", "qz_QZ@Euro", "qz_QZ_EURO" }, /* qz-qz uses private use iso codes */ { "en-BOONT", "en__BOONT", "en__BOONT" }, /* registered name */ { "de-1901", "de__1901", "de__1901" }, /* registered name */ { "de-1906", "de__1906", "de__1906" }, /* registered name */ /* posix behavior that used to be performed by getName */ { "mr.utf8", "mr.utf8", "mr" }, { "de-tv.koi8r", "de_TV.koi8r", "de_TV" }, { "x-piglatin_ML.MBE", "x-piglatin_ML.MBE", "x-piglatin_ML" }, { "i-cherokee_US.utf7", "i-cherokee_US.utf7", "i-cherokee_US" }, { "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA" }, { "no-no-ny.utf8@B", "no_NO_NY.utf8@B", "no_NO_NY_B" /* not: "nn_NO" [alan ICU3.0] */ }, /* @ ignored unless variant is empty */ /* fleshing out canonicalization */ /* trim space and sort keywords, ';' is separator so not present at end in canonical form */ { "en_Hant_IL_VALLEY_GIRL@ currency = EUR; calendar = Japanese ;", "en_Hant_IL_VALLEY_GIRL@calendar=Japanese;currency=EUR", "en_Hant_IL_VALLEY_GIRL@calendar=Japanese;currency=EUR" }, /* already-canonical ids are not changed */ { "en_Hant_IL_VALLEY_GIRL@calendar=Japanese;currency=EUR", "en_Hant_IL_VALLEY_GIRL@calendar=Japanese;currency=EUR", "en_Hant_IL_VALLEY_GIRL@calendar=Japanese;currency=EUR" }, /* norwegian is just too weird, if we handle things in their full generality */ { "no-Hant-GB_NY@currency=$$$", "no_Hant_GB_NY@currency=$$$", "no_Hant_GB_NY@currency=$$$" /* not: "nn_Hant_GB@currency=$$$" [alan ICU3.0] */ }, /* test cases reflecting internal resource bundle usage */ { "root@kw=foo", "root@kw=foo", "root@kw=foo" }, { "@calendar=gregorian", "@calendar=gregorian", "@calendar=gregorian" }, { "ja_JP@calendar=Japanese", "ja_JP@calendar=Japanese", "ja_JP@calendar=Japanese" }, { "ja_JP", "ja_JP", "ja_JP" }, /* test case for "i-default" */ { "i-default", "en@x=i-default", "en@x=i-default" }, // Before ICU 64, ICU locale canonicalization had some additional mappings. // They were removed for ICU-20187 "drop support for long-obsolete locale ID variants". // The following now use standard canonicalization. { "ca_ES_PREEURO", "ca_ES_PREEURO", "ca_ES_PREEURO" }, { "de_AT_PREEURO", "de_AT_PREEURO", "de_AT_PREEURO" }, { "de_DE_PREEURO", "de_DE_PREEURO", "de_DE_PREEURO" }, { "de_LU_PREEURO", "de_LU_PREEURO", "de_LU_PREEURO" }, { "el_GR_PREEURO", "el_GR_PREEURO", "el_GR_PREEURO" }, { "en_BE_PREEURO", "en_BE_PREEURO", "en_BE_PREEURO" }, { "en_IE_PREEURO", "en_IE_PREEURO", "en_IE_PREEURO" }, { "es_ES_PREEURO", "es_ES_PREEURO", "es_ES_PREEURO" }, { "eu_ES_PREEURO", "eu_ES_PREEURO", "eu_ES_PREEURO" }, { "fi_FI_PREEURO", "fi_FI_PREEURO", "fi_FI_PREEURO" }, { "fr_BE_PREEURO", "fr_BE_PREEURO", "fr_BE_PREEURO" }, { "fr_FR_PREEURO", "fr_FR_PREEURO", "fr_FR_PREEURO" }, { "fr_LU_PREEURO", "fr_LU_PREEURO", "fr_LU_PREEURO" }, { "ga_IE_PREEURO", "ga_IE_PREEURO", "ga_IE_PREEURO" }, { "gl_ES_PREEURO", "gl_ES_PREEURO", "gl_ES_PREEURO" }, { "it_IT_PREEURO", "it_IT_PREEURO", "it_IT_PREEURO" }, { "nl_BE_PREEURO", "nl_BE_PREEURO", "nl_BE_PREEURO" }, { "nl_NL_PREEURO", "nl_NL_PREEURO", "nl_NL_PREEURO" }, { "pt_PT_PREEURO", "pt_PT_PREEURO", "pt_PT_PREEURO" }, { "de__PHONEBOOK", "de__PHONEBOOK", "de__PHONEBOOK" }, { "en_GB_EURO", "en_GB_EURO", "en_GB_EURO" }, { "en_GB@EURO", "en_GB@EURO", "en_GB_EURO" }, /* POSIX ID */ { "es__TRADITIONAL", "es__TRADITIONAL", "es__TRADITIONAL" }, { "hi__DIRECT", "hi__DIRECT", "hi__DIRECT" }, { "ja_JP_TRADITIONAL", "ja_JP_TRADITIONAL", "ja_JP_TRADITIONAL" }, { "th_TH_TRADITIONAL", "th_TH_TRADITIONAL", "th_TH_TRADITIONAL" }, { "zh_TW_STROKE", "zh_TW_STROKE", "zh_TW_STROKE" }, { "zh__PINYIN", "zh__PINYIN", "zh__PINYIN" }, { "zh_CN_STROKE", "zh_CN_STROKE", "zh_CN_STROKE" }, { "sr-SP-Cyrl", "sr_SP_CYRL", "sr_SP_CYRL" }, /* .NET name */ { "sr-SP-Latn", "sr_SP_LATN", "sr_SP_LATN" }, /* .NET name */ { "sr_YU_CYRILLIC", "sr_YU_CYRILLIC", "sr_YU_CYRILLIC" }, /* Linux name */ { "uz-UZ-Cyrl", "uz_UZ_CYRL", "uz_UZ_CYRL" }, /* .NET name */ { "uz-UZ-Latn", "uz_UZ_LATN", "uz_UZ_LATN" }, /* .NET name */ { "zh-CHS", "zh_CHS", "zh_CHS" }, /* .NET name */ { "zh-CHT", "zh_CHT", "zh_CHT" }, /* .NET name This may change back to zh_Hant */ /* PRE_EURO and EURO conversions don't affect other keywords */ { "es_ES_PREEURO@CALendar=Japanese", "es_ES_PREEURO@calendar=Japanese", "es_ES_PREEURO@calendar=Japanese" }, { "es_ES_EURO@SHOUT=zipeedeedoodah", "es_ES_EURO@shout=zipeedeedoodah", "es_ES_EURO@shout=zipeedeedoodah" }, /* currency keyword overrides PRE_EURO and EURO currency */ { "es_ES_PREEURO@currency=EUR", "es_ES_PREEURO@currency=EUR", "es_ES_PREEURO@currency=EUR" }, { "es_ES_EURO@currency=ESP", "es_ES_EURO@currency=ESP", "es_ES_EURO@currency=ESP" }, }; static const char* label[] = { "getName", "canonicalize" }; UErrorCode status = U_ZERO_ERROR; int32_t i, j, resultLen = 0, origResultLen; char buffer[256]; for (i=0; i < UPRV_LENGTHOF(testCases); i++) { for (j=0; j<2; ++j) { const char* expected = (j==0) ? testCases[i].getNameID : testCases[i].canonicalID; *buffer = 0; status = U_ZERO_ERROR; if (expected == NULL) { expected = uloc_getDefault(); } /* log_verbose("testing %s -> %s\n", testCases[i], testCases[i].canonicalID); */ origResultLen = _canonicalize(j, testCases[i].localeID, NULL, 0, &status); if (status != U_BUFFER_OVERFLOW_ERROR) { log_err("FAIL: uloc_%s(%s) => %s, expected U_BUFFER_OVERFLOW_ERROR\n", label[j], testCases[i].localeID, u_errorName(status)); continue; } status = U_ZERO_ERROR; resultLen = _canonicalize(j, testCases[i].localeID, buffer, sizeof(buffer), &status); if (U_FAILURE(status)) { log_err("FAIL: uloc_%s(%s) => %s, expected U_ZERO_ERROR\n", label[j], testCases[i].localeID, u_errorName(status)); continue; } if(uprv_strcmp(expected, buffer) != 0) { log_err("FAIL: uloc_%s(%s) => \"%s\", expected \"%s\"\n", label[j], testCases[i].localeID, buffer, expected); } else { log_verbose("Ok: uloc_%s(%s) => \"%s\"\n", label[j], testCases[i].localeID, buffer); } if (resultLen != (int32_t)strlen(buffer)) { log_err("FAIL: uloc_%s(%s) => len %d, expected len %d\n", label[j], testCases[i].localeID, resultLen, strlen(buffer)); } if (origResultLen != resultLen) { log_err("FAIL: uloc_%s(%s) => preflight len %d != actual len %d\n", label[j], testCases[i].localeID, origResultLen, resultLen); } } } } static void TestCanonicalizationBuffer(void) { UErrorCode status = U_ZERO_ERROR; char buffer[256]; // ULOC_FULLNAME_CAPACITY == 157 (uloc.h) static const char name[] = "zh@x" "=foo-bar-baz-foo-bar-baz-foo-bar-baz-foo-bar-baz" "-foo-bar-baz-foo-bar-baz-foo-bar-baz-foo-bar-baz" "-foo-bar-baz-foo-bar-baz-foo-bar-baz-foo-bar-baz" "-foo-barz" ; static const size_t len = sizeof(name) - 1; // Without NUL terminator. int32_t reslen = uloc_canonicalize(name, buffer, (int32_t)len, &status); if (U_FAILURE(status)) { log_err("FAIL: uloc_canonicalize(%s) => %s, expected !U_FAILURE()\n", name, u_errorName(status)); return; } if (reslen != len) { log_err("FAIL: uloc_canonicalize(%s) => \"%i\", expected \"%u\"\n", name, reslen, len); return; } if (uprv_strncmp(name, buffer, len) != 0) { log_err("FAIL: uloc_canonicalize(%s) => \"%.*s\", expected \"%s\"\n", name, reslen, buffer, name); return; } } static void TestCanonicalization21749StackUseAfterScope(void) { UErrorCode status = U_ZERO_ERROR; char buffer[256]; const char* input = "- _"; uloc_canonicalize(input, buffer, -1, &status); if (U_SUCCESS(status)) { log_err("FAIL: uloc_canonicalize(%s) => %s, expected U_FAILURE()\n", input, u_errorName(status)); return; } } static void TestDisplayKeywords(void) { int32_t i; static const struct { const char *localeID; const char *displayLocale; UChar displayKeyword[200]; } testCases[] = { { "ca_ES@currency=ESP", "de_AT", {0x0057, 0x00e4, 0x0068, 0x0072, 0x0075, 0x006e, 0x0067, 0x0000}, }, { "ja_JP@calendar=japanese", "de", { 0x004b, 0x0061, 0x006c, 0x0065, 0x006e, 0x0064, 0x0065, 0x0072, 0x0000} }, { "de_DE@collation=traditional", "de_DE", {0x0053, 0x006f, 0x0072, 0x0074, 0x0069, 0x0065, 0x0072, 0x0075, 0x006e, 0x0067, 0x0000} }, }; for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { UErrorCode status = U_ZERO_ERROR; const char* keyword =NULL; int32_t keywordLen = 0; int32_t keywordCount = 0; UChar *displayKeyword=NULL; int32_t displayKeywordLen = 0; UEnumeration* keywordEnum = uloc_openKeywords(testCases[i].localeID, &status); for(keywordCount = uenum_count(keywordEnum, &status); keywordCount > 0 ; keywordCount--){ if(U_FAILURE(status)){ log_err("uloc_getKeywords failed for locale id: %s with error : %s \n", testCases[i].localeID, u_errorName(status)); break; } /* the uenum_next returns NUL terminated string */ keyword = uenum_next(keywordEnum, &keywordLen, &status); /* fetch the displayKeyword */ displayKeywordLen = uloc_getDisplayKeyword(keyword, testCases[i].displayLocale, displayKeyword, displayKeywordLen, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status = U_ZERO_ERROR; displayKeywordLen++; /* for null termination */ displayKeyword = (UChar*) malloc(displayKeywordLen * U_SIZEOF_UCHAR); displayKeywordLen = uloc_getDisplayKeyword(keyword, testCases[i].displayLocale, displayKeyword, displayKeywordLen, &status); if(U_FAILURE(status)){ log_err("uloc_getDisplayKeyword filed for keyword : %s in locale id: %s for display locale: %s \n", testCases[i].localeID, keyword, testCases[i].displayLocale, u_errorName(status)); free(displayKeyword); break; } if(u_strncmp(displayKeyword, testCases[i].displayKeyword, displayKeywordLen)!=0){ if (status == U_USING_DEFAULT_WARNING) { log_data_err("uloc_getDisplayKeyword did not get the expected value for keyword : %s in locale id: %s for display locale: %s . Got error: %s. Perhaps you are missing data?\n", testCases[i].localeID, keyword, testCases[i].displayLocale, u_errorName(status)); } else { log_err("uloc_getDisplayKeyword did not get the expected value for keyword : %s in locale id: %s for display locale: %s \n", testCases[i].localeID, keyword, testCases[i].displayLocale); } free(displayKeyword); break; } }else{ log_err("uloc_getDisplayKeyword did not return the expected error. Error: %s\n", u_errorName(status)); } free(displayKeyword); } uenum_close(keywordEnum); } } static void TestDisplayKeywordValues(void){ int32_t i; static const struct { const char *localeID; const char *displayLocale; UChar displayKeywordValue[500]; } testCases[] = { { "ca_ES@currency=ESP", "de_AT", {0x0053, 0x0070, 0x0061, 0x006e, 0x0069, 0x0073, 0x0063, 0x0068, 0x0065, 0x0020, 0x0050, 0x0065, 0x0073, 0x0065, 0x0074, 0x0061, 0x0000} }, { "de_AT@currency=ATS", "fr_FR", {0x0073, 0x0063, 0x0068, 0x0069, 0x006c, 0x006c, 0x0069, 0x006e, 0x0067, 0x0020, 0x0061, 0x0075, 0x0074, 0x0072, 0x0069, 0x0063, 0x0068, 0x0069, 0x0065, 0x006e, 0x0000} }, { "de_DE@currency=DEM", "it", {0x006d, 0x0061, 0x0072, 0x0063, 0x006f, 0x0020, 0x0074, 0x0065, 0x0064, 0x0065, 0x0073, 0x0063, 0x006f, 0x0000} }, { "el_GR@currency=GRD", "en", {0x0047, 0x0072, 0x0065, 0x0065, 0x006b, 0x0020, 0x0044, 0x0072, 0x0061, 0x0063, 0x0068, 0x006d, 0x0061, 0x0000} }, { "eu_ES@currency=ESP", "it_IT", {0x0070, 0x0065, 0x0073, 0x0065, 0x0074, 0x0061, 0x0020, 0x0073, 0x0070, 0x0061, 0x0067, 0x006e, 0x006f, 0x006c, 0x0061, 0x0000} }, { "de@collation=phonebook", "es", {0x006F, 0x0072, 0x0064, 0x0065, 0x006E, 0x0020, 0x0064, 0x0065, 0x0020, 0x006C, 0x0069, 0x0073, 0x0074, 0x00ED, 0x006E, 0x0020, 0x0074, 0x0065, 0x006C, 0x0065, 0x0066, 0x00F3, 0x006E, 0x0069, 0x0063, 0x006F, 0x0000} }, { "de_DE@collation=phonebook", "es", {0x006F, 0x0072, 0x0064, 0x0065, 0x006E, 0x0020, 0x0064, 0x0065, 0x0020, 0x006C, 0x0069, 0x0073, 0x0074, 0x00ED, 0x006E, 0x0020, 0x0074, 0x0065, 0x006C, 0x0065, 0x0066, 0x00F3, 0x006E, 0x0069, 0x0063, 0x006F, 0x0000} }, { "es_ES@collation=traditional","de", {0x0054, 0x0072, 0x0061, 0x0064, 0x0069, 0x0074, 0x0069, 0x006f, 0x006e, 0x0065, 0x006c, 0x006c, 0x0065, 0x0020, 0x0053, 0x006f, 0x0072, 0x0074, 0x0069, 0x0065, 0x0072, 0x0072, 0x0065, 0x0067, 0x0065, 0x006c, 0x006e, 0x0000} }, { "ja_JP@calendar=japanese", "de", {0x004a, 0x0061, 0x0070, 0x0061, 0x006e, 0x0069, 0x0073, 0x0063, 0x0068, 0x0065, 0x0072, 0x0020, 0x004b, 0x0061, 0x006c, 0x0065, 0x006e, 0x0064, 0x0065, 0x0072, 0x0000} }, }; for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { UErrorCode status = U_ZERO_ERROR; const char* keyword =NULL; int32_t keywordLen = 0; int32_t keywordCount = 0; UChar *displayKeywordValue = NULL; int32_t displayKeywordValueLen = 0; UEnumeration* keywordEnum = uloc_openKeywords(testCases[i].localeID, &status); for(keywordCount = uenum_count(keywordEnum, &status); keywordCount > 0 ; keywordCount--){ if(U_FAILURE(status)){ log_err("uloc_getKeywords failed for locale id: %s in display locale: % with error : %s \n", testCases[i].localeID, testCases[i].displayLocale, u_errorName(status)); break; } /* the uenum_next returns NUL terminated string */ keyword = uenum_next(keywordEnum, &keywordLen, &status); /* fetch the displayKeywordValue */ displayKeywordValueLen = uloc_getDisplayKeywordValue(testCases[i].localeID, keyword, testCases[i].displayLocale, displayKeywordValue, displayKeywordValueLen, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status = U_ZERO_ERROR; displayKeywordValueLen++; /* for null termination */ displayKeywordValue = (UChar*)malloc(displayKeywordValueLen * U_SIZEOF_UCHAR); displayKeywordValueLen = uloc_getDisplayKeywordValue(testCases[i].localeID, keyword, testCases[i].displayLocale, displayKeywordValue, displayKeywordValueLen, &status); if(U_FAILURE(status)){ log_err("uloc_getDisplayKeywordValue failed for keyword : %s in locale id: %s for display locale: %s with error : %s \n", testCases[i].localeID, keyword, testCases[i].displayLocale, u_errorName(status)); free(displayKeywordValue); break; } if(u_strncmp(displayKeywordValue, testCases[i].displayKeywordValue, displayKeywordValueLen)!=0){ if (status == U_USING_DEFAULT_WARNING) { log_data_err("uloc_getDisplayKeywordValue did not return the expected value keyword : %s in locale id: %s for display locale: %s with error : %s Perhaps you are missing data\n", testCases[i].localeID, keyword, testCases[i].displayLocale, u_errorName(status)); } else { log_err("uloc_getDisplayKeywordValue did not return the expected value keyword : %s in locale id: %s for display locale: %s with error : %s \n", testCases[i].localeID, keyword, testCases[i].displayLocale, u_errorName(status)); } free(displayKeywordValue); break; } }else{ log_err("uloc_getDisplayKeywordValue did not return the expected error. Error: %s\n", u_errorName(status)); } free(displayKeywordValue); } uenum_close(keywordEnum); } { /* test a multiple keywords */ UErrorCode status = U_ZERO_ERROR; const char* keyword =NULL; int32_t keywordLen = 0; int32_t keywordCount = 0; const char* localeID = "es@collation=phonebook;calendar=buddhist;currency=DEM"; const char* displayLocale = "de"; static const UChar expected[][50] = { {0x0042, 0x0075, 0x0064, 0x0064, 0x0068, 0x0069, 0x0073, 0x0074, 0x0069, 0x0073, 0x0063, 0x0068, 0x0065, 0x0072, 0x0020, 0x004b, 0x0061, 0x006c, 0x0065, 0x006e, 0x0064, 0x0065, 0x0072, 0x0000}, {0x0054, 0x0065, 0x006c, 0x0065, 0x0066, 0x006f, 0x006e, 0x0062, 0x0075, 0x0063, 0x0068, 0x002d, 0x0053, 0x006f, 0x0072, 0x0074, 0x0069, 0x0065, 0x0072, 0x0075, 0x006e, 0x0067, 0x0000}, {0x0044, 0x0065, 0x0075, 0x0074, 0x0073, 0x0063, 0x0068, 0x0065, 0x0020, 0x004d, 0x0061, 0x0072, 0x006b, 0x0000}, }; UEnumeration* keywordEnum = uloc_openKeywords(localeID, &status); for(keywordCount = 0; keywordCount < uenum_count(keywordEnum, &status) ; keywordCount++){ UChar *displayKeywordValue = NULL; int32_t displayKeywordValueLen = 0; if(U_FAILURE(status)){ log_err("uloc_getKeywords failed for locale id: %s in display locale: % with error : %s \n", localeID, displayLocale, u_errorName(status)); break; } /* the uenum_next returns NUL terminated string */ keyword = uenum_next(keywordEnum, &keywordLen, &status); /* fetch the displayKeywordValue */ displayKeywordValueLen = uloc_getDisplayKeywordValue(localeID, keyword, displayLocale, displayKeywordValue, displayKeywordValueLen, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status = U_ZERO_ERROR; displayKeywordValueLen++; /* for null termination */ displayKeywordValue = (UChar*)malloc(displayKeywordValueLen * U_SIZEOF_UCHAR); displayKeywordValueLen = uloc_getDisplayKeywordValue(localeID, keyword, displayLocale, displayKeywordValue, displayKeywordValueLen, &status); if(U_FAILURE(status)){ log_err("uloc_getDisplayKeywordValue failed for keyword : %s in locale id: %s for display locale: %s with error : %s \n", localeID, keyword, displayLocale, u_errorName(status)); free(displayKeywordValue); break; } if(u_strncmp(displayKeywordValue, expected[keywordCount], displayKeywordValueLen)!=0){ if (status == U_USING_DEFAULT_WARNING) { log_data_err("uloc_getDisplayKeywordValue did not return the expected value keyword : %s in locale id: %s for display locale: %s got error: %s. Perhaps you are missing data?\n", localeID, keyword, displayLocale, u_errorName(status)); } else { log_err("uloc_getDisplayKeywordValue did not return the expected value keyword : %s in locale id: %s for display locale: %s \n", localeID, keyword, displayLocale); } free(displayKeywordValue); break; } }else{ log_err("uloc_getDisplayKeywordValue did not return the expected error. Error: %s\n", u_errorName(status)); } free(displayKeywordValue); } uenum_close(keywordEnum); } { /* Test non existent keywords */ UErrorCode status = U_ZERO_ERROR; const char* localeID = "es"; const char* displayLocale = "de"; UChar *displayKeywordValue = NULL; int32_t displayKeywordValueLen = 0; /* fetch the displayKeywordValue */ displayKeywordValueLen = uloc_getDisplayKeywordValue(localeID, "calendar", displayLocale, displayKeywordValue, displayKeywordValueLen, &status); if(U_FAILURE(status)) { log_err("uloc_getDisplaykeywordValue returned error status %s\n", u_errorName(status)); } else if(displayKeywordValueLen != 0) { log_err("uloc_getDisplaykeywordValue returned %d should be 0 \n", displayKeywordValueLen); } } } static void TestGetBaseName(void) { static const struct { const char *localeID; const char *baseName; } testCases[] = { { "de_DE@ C o ll A t i o n = Phonebook ", "de_DE" }, { "de@currency = euro; CoLLaTion = PHONEBOOk", "de" }, { "ja@calendar = buddhist", "ja" } }; int32_t i = 0, baseNameLen = 0; char baseName[256]; UErrorCode status = U_ZERO_ERROR; for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { baseNameLen = uloc_getBaseName(testCases[i].localeID, baseName, 256, &status); (void)baseNameLen; /* Suppress set but not used warning. */ if(strcmp(testCases[i].baseName, baseName)) { log_err("For locale \"%s\" expected baseName \"%s\", but got \"%s\"\n", testCases[i].localeID, testCases[i].baseName, baseName); return; } } } static void TestTrailingNull(void) { const char* localeId = "zh_Hans"; UChar buffer[128]; /* sufficient for this test */ int32_t len; UErrorCode status = U_ZERO_ERROR; int i; len = uloc_getDisplayName(localeId, localeId, buffer, 128, &status); if (len > 128) { log_err("buffer too small"); return; } for (i = 0; i < len; ++i) { if (buffer[i] == 0) { log_err("name contained null"); return; } } } /* Jitterbug 4115 */ static void TestDisplayNameWarning(void) { UChar name[256]; int32_t size; UErrorCode status = U_ZERO_ERROR; size = uloc_getDisplayLanguage("qqq", "kl", name, UPRV_LENGTHOF(name), &status); (void)size; /* Suppress set but not used warning. */ if (status != U_USING_DEFAULT_WARNING) { log_err("For language \"qqq\" in locale \"kl\", expecting U_USING_DEFAULT_WARNING, but got %s\n", u_errorName(status)); } } /** * Compare two locale IDs. If they are equal, return 0. If `string' * starts with `prefix' plus an additional element, that is, string == * prefix + '_' + x, then return 1. Otherwise return a value < 0. */ static UBool _loccmp(const char* string, const char* prefix) { int32_t slen = (int32_t)uprv_strlen(string), plen = (int32_t)uprv_strlen(prefix); int32_t c = uprv_strncmp(string, prefix, plen); /* 'root' is less than everything */ if (uprv_strcmp(prefix, "root") == 0) { return (uprv_strcmp(string, "root") == 0) ? 0 : 1; } if (c) return -1; /* mismatch */ if (slen == plen) return 0; if (string[plen] == '_') return 1; return -2; /* false match, e.g. "en_USX" cmp "en_US" */ } static void _checklocs(const char* label, const char* req, const char* valid, const char* actual) { /* We want the valid to be strictly > the bogus requested locale, and the valid to be >= the actual. */ if (_loccmp(req, valid) > 0 && _loccmp(valid, actual) >= 0) { log_verbose("%s; req=%s, valid=%s, actual=%s\n", label, req, valid, actual); } else { log_err("FAIL: %s; req=%s, valid=%s, actual=%s\n", label, req, valid, actual); } } static void TestGetLocale(void) { UErrorCode ec = U_ZERO_ERROR; UParseError pe; UChar EMPTY[1] = {0}; /* === udat === */ #if !UCONFIG_NO_FORMATTING { UDateFormat *obj; const char *req = "en_US_REDWOODSHORES", *valid, *actual; obj = udat_open(UDAT_DEFAULT, UDAT_DEFAULT, req, NULL, 0, NULL, 0, &ec); if (U_FAILURE(ec)) { log_data_err("udat_open failed.Error %s\n", u_errorName(ec)); return; } valid = udat_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = udat_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("udat_getLocaleByType() failed\n"); return; } _checklocs("udat", req, valid, actual); udat_close(obj); } #endif /* === ucal === */ #if !UCONFIG_NO_FORMATTING { UCalendar *obj; const char *req = "fr_FR_PROVENCAL", *valid, *actual; obj = ucal_open(NULL, 0, req, UCAL_GREGORIAN, &ec); if (U_FAILURE(ec)) { log_err("ucal_open failed with error: %s\n", u_errorName(ec)); return; } valid = ucal_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = ucal_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("ucal_getLocaleByType() failed\n"); return; } _checklocs("ucal", req, valid, actual); ucal_close(obj); } #endif /* === unum === */ #if !UCONFIG_NO_FORMATTING { UNumberFormat *obj; const char *req = "zh_Hant_TW_TAINAN", *valid, *actual; obj = unum_open(UNUM_DECIMAL, NULL, 0, req, &pe, &ec); if (U_FAILURE(ec)) { log_err("unum_open failed\n"); return; } valid = unum_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = unum_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("unum_getLocaleByType() failed\n"); return; } _checklocs("unum", req, valid, actual); unum_close(obj); } #endif /* === umsg === */ #if 0 /* commented out by weiv 01/12/2005. umsg_getLocaleByType is to be removed */ #if !UCONFIG_NO_FORMATTING { UMessageFormat *obj; const char *req = "ja_JP_TAKAYAMA", *valid, *actual; UBool test; obj = umsg_open(EMPTY, 0, req, &pe, &ec); if (U_FAILURE(ec)) { log_err("umsg_open failed\n"); return; } valid = umsg_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = umsg_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("umsg_getLocaleByType() failed\n"); return; } /* We want the valid to be strictly > the bogus requested locale, and the valid to be >= the actual. */ /* TODO MessageFormat is currently just storing the locale it is given. As a result, it will return whatever it was given, even if the locale is invalid. */ test = (_cmpversion("3.2") <= 0) ? /* Here is the weakened test for 3.0: */ (_loccmp(req, valid) >= 0) : /* Here is what the test line SHOULD be: */ (_loccmp(req, valid) > 0); if (test && _loccmp(valid, actual) >= 0) { log_verbose("umsg; req=%s, valid=%s, actual=%s\n", req, valid, actual); } else { log_err("FAIL: umsg; req=%s, valid=%s, actual=%s\n", req, valid, actual); } umsg_close(obj); } #endif #endif /* === ubrk === */ #if !UCONFIG_NO_BREAK_ITERATION { UBreakIterator *obj; const char *req = "ar_KW_ABDALI", *valid, *actual; obj = ubrk_open(UBRK_WORD, req, EMPTY, 0, &ec); if (U_FAILURE(ec)) { log_err("ubrk_open failed. Error: %s \n", u_errorName(ec)); return; } valid = ubrk_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = ubrk_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("ubrk_getLocaleByType() failed\n"); return; } _checklocs("ubrk", req, valid, actual); ubrk_close(obj); } #endif /* === ucol === */ #if !UCONFIG_NO_COLLATION { UCollator *obj; const char *req = "es_AR_BUENOSAIRES", *valid, *actual; obj = ucol_open(req, &ec); if (U_FAILURE(ec)) { log_err("ucol_open failed - %s\n", u_errorName(ec)); return; } valid = ucol_getLocaleByType(obj, ULOC_VALID_LOCALE, &ec); actual = ucol_getLocaleByType(obj, ULOC_ACTUAL_LOCALE, &ec); if (U_FAILURE(ec)) { log_err("ucol_getLocaleByType() failed\n"); return; } _checklocs("ucol", req, valid, actual); ucol_close(obj); } #endif } static void TestEnglishExemplarCharacters(void) { UErrorCode status = U_ZERO_ERROR; int i; USet *exSet = NULL; UChar testChars[] = { 0x61, /* standard */ 0xE1, /* auxiliary */ 0x41, /* index */ 0x2D /* punctuation */ }; ULocaleData *uld = ulocdata_open("en", &status); if (U_FAILURE(status)) { log_data_err("ulocdata_open() failed : %s - (Are you missing data?)\n", u_errorName(status)); return; } for (i = 0; i < ULOCDATA_ES_COUNT; i++) { exSet = ulocdata_getExemplarSet(uld, exSet, 0, (ULocaleDataExemplarSetType)i, &status); if (U_FAILURE(status)) { log_err_status(status, "ulocdata_getExemplarSet() for type %d failed\n", i); status = U_ZERO_ERROR; continue; } if (!uset_contains(exSet, (UChar32)testChars[i])) { log_err("Character U+%04X is not included in exemplar type %d\n", testChars[i], i); } } uset_close(exSet); ulocdata_close(uld); } static void TestNonexistentLanguageExemplars(void) { /* JB 4068 - Nonexistent language */ UErrorCode ec = U_ZERO_ERROR; ULocaleData *uld = ulocdata_open("qqq",&ec); if (ec != U_USING_DEFAULT_WARNING) { log_err_status(ec, "Exemplar set for \"qqq\", expecting U_USING_DEFAULT_WARNING, but got %s\n", u_errorName(ec)); } uset_close(ulocdata_getExemplarSet(uld, NULL, 0, ULOCDATA_ES_STANDARD, &ec)); ulocdata_close(uld); } static void TestLocDataErrorCodeChaining(void) { UErrorCode ec = U_USELESS_COLLATOR_ERROR; ulocdata_open(NULL, &ec); ulocdata_getExemplarSet(NULL, NULL, 0, ULOCDATA_ES_STANDARD, &ec); ulocdata_getDelimiter(NULL, ULOCDATA_DELIMITER_COUNT, NULL, -1, &ec); ulocdata_getMeasurementSystem(NULL, &ec); ulocdata_getPaperSize(NULL, NULL, NULL, &ec); if (ec != U_USELESS_COLLATOR_ERROR) { log_err("ulocdata API changed the error code to %s\n", u_errorName(ec)); } } typedef struct { const char* locale; UMeasurementSystem measureSys; } LocToMeasureSys; static const LocToMeasureSys locToMeasures[] = { { "fr_FR", UMS_SI }, { "en", UMS_US }, { "en_GB", UMS_UK }, { "fr_FR@rg=GBZZZZ", UMS_UK }, { "en@rg=frzzzz", UMS_SI }, { "en_GB@rg=USZZZZ", UMS_US }, { NULL, (UMeasurementSystem)0 } /* terminator */ }; static void TestLocDataWithRgTag(void) { const LocToMeasureSys* locToMeasurePtr = locToMeasures; for (; locToMeasurePtr->locale != NULL; locToMeasurePtr++) { UErrorCode status = U_ZERO_ERROR; UMeasurementSystem measureSys = ulocdata_getMeasurementSystem(locToMeasurePtr->locale, &status); if (U_FAILURE(status)) { log_data_err("ulocdata_getMeasurementSystem(\"%s\", ...) failed: %s - Are you missing data?\n", locToMeasurePtr->locale, u_errorName(status)); } else if (measureSys != locToMeasurePtr->measureSys) { log_err("ulocdata_getMeasurementSystem(\"%s\", ...), expected %d, got %d\n", locToMeasurePtr->locale, (int) locToMeasurePtr->measureSys, (int)measureSys); } } } static void TestLanguageExemplarsFallbacks(void) { /* Test that en_US fallsback, but en doesn't fallback. */ UErrorCode ec = U_ZERO_ERROR; ULocaleData *uld = ulocdata_open("en_US",&ec); uset_close(ulocdata_getExemplarSet(uld, NULL, 0, ULOCDATA_ES_STANDARD, &ec)); if (ec != U_USING_FALLBACK_WARNING) { log_err_status(ec, "Exemplar set for \"en_US\", expecting U_USING_FALLBACK_WARNING, but got %s\n", u_errorName(ec)); } ulocdata_close(uld); ec = U_ZERO_ERROR; uld = ulocdata_open("en",&ec); uset_close(ulocdata_getExemplarSet(uld, NULL, 0, ULOCDATA_ES_STANDARD, &ec)); if (ec != U_ZERO_ERROR) { log_err_status(ec, "Exemplar set for \"en\", expecting U_ZERO_ERROR, but got %s\n", u_errorName(ec)); } ulocdata_close(uld); } static const char *acceptResult(UAcceptResult uar) { return udbg_enumName(UDBG_UAcceptResult, uar); } static void TestAcceptLanguage(void) { UErrorCode status = U_ZERO_ERROR; UAcceptResult outResult; UEnumeration *available; char tmp[200]; int i; int32_t rc = 0; struct { int32_t httpSet; /**< Which of http[] should be used? */ const char *icuSet; /**< ? */ const char *expect; /**< The expected locale result */ UAcceptResult res; /**< The expected error code */ UErrorCode expectStatus; /**< expected status */ } tests[] = { /*0*/{ 0, NULL, "mt_MT", ULOC_ACCEPT_VALID, U_ZERO_ERROR}, /*1*/{ 1, NULL, "en", ULOC_ACCEPT_VALID, U_ZERO_ERROR}, /*2*/{ 2, NULL, "en_GB", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /*3*/{ 3, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR}, /*4*/{ 4, NULL, "es", ULOC_ACCEPT_VALID, U_ZERO_ERROR}, /*5*/{ 5, NULL, "zh", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /* XF */ /*6*/{ 6, NULL, "ja", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /* XF */ /*7*/{ 7, NULL, "zh", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /* XF */ /*8*/{ 8, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR }, /* */ /*9*/{ 9, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR }, /* */ /*10*/{10, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR }, /* */ /*11*/{11, NULL, "", ULOC_ACCEPT_FAILED, U_ILLEGAL_ARGUMENT_ERROR }, /* */ }; const int32_t numTests = UPRV_LENGTHOF(tests); static const char *http[] = { /*0*/ "mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, " "iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, " "es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, " "nl-nl;q=0.55, nl;q=0.53, th-th-traditional;q=0.01", /*1*/ "ja;q=0.5, en;q=0.8, tlh", /*2*/ "en-wf, de-lx;q=0.8", /*3*/ "mga-ie;q=0.9, sux", /*4*/ "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, " "xxx-yyy;q=0.01, xxx-yyy;q=0.01, xxx-yyy;q=0.01, xx-yy;q=0.1, " "es", /*5*/ "zh-xx;q=0.9, en;q=0.6", /*6*/ "ja-JA", /*7*/ "zh-xx;q=0.9", /*08*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // 156 /*09*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB", // 157 (this hits U_STRING_NOT_TERMINATED_WARNING ) /*10*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABC", // 158 /*11*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // 163 bytes }; for(i=0;i0)&&uprv_strcmp(tmp, tests[i].expect)) { log_err_status(status, "FAIL: #%d: expected %s but got %s\n", i, tests[i].expect, tmp); log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n", i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect, acceptResult(tests[i].res)); } } } } static const char* LOCALE_ALIAS[][2] = { {"in", "id"}, {"in_ID", "id_ID"}, {"iw", "he"}, {"iw_IL", "he_IL"}, {"ji", "yi"}, {"en_BU", "en_MM"}, {"en_DY", "en_BJ"}, {"en_HV", "en_BF"}, {"en_NH", "en_VU"}, {"en_RH", "en_ZW"}, {"en_TP", "en_TL"}, {"en_ZR", "en_CD"} }; static UBool isLocaleAvailable(UResourceBundle* resIndex, const char* loc){ UErrorCode status = U_ZERO_ERROR; int32_t len = 0; ures_getStringByKey(resIndex, loc,&len, &status); if(U_FAILURE(status)){ return FALSE; } return TRUE; } static void TestCalendar() { #if !UCONFIG_NO_FORMATTING int i; UErrorCode status = U_ZERO_ERROR; UResourceBundle *resIndex = ures_open(NULL,"res_index", &status); if(U_FAILURE(status)){ log_err_status(status, "Could not open res_index.res. Exiting. Error: %s\n", u_errorName(status)); return; } for (i=0; iuerror == U_BUFFER_OVERFLOW_ERROR || data->uerror == U_STRING_NOT_TERMINATED_WARNING) { return (int32_t)strlen(data->expected); } else { return -1; } } static int32_t getBufferSize(const errorData* data, int32_t actualSize) { if (data->expected == NULL) { return actualSize; } else if (data->bufferSize < 0) { return (int32_t)strlen(data->expected) + 1; } else { return data->bufferSize; } } static void TestLikelySubtags() { char buffer[ULOC_FULLNAME_CAPACITY + ULOC_KEYWORD_AND_VALUES_CAPACITY + 1]; int32_t i = 0; for (; i < UPRV_LENGTHOF(basic_maximize_data); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const minimal = basic_maximize_data[i][0]; const char* const maximal = basic_maximize_data[i][1]; /* const int32_t length = */ uloc_addLikelySubtags( minimal, buffer, sizeof(buffer), &status); if (U_FAILURE(status)) { log_err_status(status, " unexpected failure of uloc_addLikelySubtags(), minimal \"%s\" status %s\n", minimal, u_errorName(status)); status = U_ZERO_ERROR; } else if (uprv_strlen(maximal) == 0) { if (uprv_stricmp(minimal, buffer) != 0) { log_err(" unexpected maximal value \"%s\" in uloc_addLikelySubtags(), minimal \"%s\" = \"%s\"\n", maximal, minimal, buffer); } } else if (uprv_stricmp(maximal, buffer) != 0) { log_err(" maximal doesn't match expected %s in uloc_addLikelySubtags(), minimal \"%s\" = %s\n", maximal, minimal, buffer); } } for (i = 0; i < UPRV_LENGTHOF(basic_minimize_data); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const maximal = basic_minimize_data[i][0]; const char* const minimal = basic_minimize_data[i][1]; /* const int32_t length = */ uloc_minimizeSubtags( maximal, buffer, sizeof(buffer), &status); if (U_FAILURE(status)) { log_err_status(status, " unexpected failure of uloc_MinimizeSubtags(), maximal \"%s\" status %s\n", maximal, u_errorName(status)); status = U_ZERO_ERROR; } else if (uprv_strlen(minimal) == 0) { if (uprv_stricmp(maximal, buffer) != 0) { log_err(" unexpected minimal value \"%s\" in uloc_minimizeSubtags(), maximal \"%s\" = \"%s\"\n", minimal, maximal, buffer); } } else if (uprv_stricmp(minimal, buffer) != 0) { log_err(" minimal doesn't match expected %s in uloc_MinimizeSubtags(), maximal \"%s\" = %s\n", minimal, maximal, buffer); } } for (i = 0; i < UPRV_LENGTHOF(full_data); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const minimal = full_data[i][0]; const char* const maximal = full_data[i][1]; /* const int32_t length = */ uloc_addLikelySubtags( minimal, buffer, sizeof(buffer), &status); if (U_FAILURE(status)) { log_err_status(status, " unexpected failure of uloc_addLikelySubtags(), minimal \"%s\" status \"%s\"\n", minimal, u_errorName(status)); status = U_ZERO_ERROR; } else if (uprv_strlen(maximal) == 0) { if (uprv_stricmp(minimal, buffer) != 0) { log_err(" unexpected maximal value \"%s\" in uloc_addLikelySubtags(), minimal \"%s\" = \"%s\"\n", maximal, minimal, buffer); } } else if (uprv_stricmp(maximal, buffer) != 0) { log_err(" maximal doesn't match expected \"%s\" in uloc_addLikelySubtags(), minimal \"%s\" = \"%s\"\n", maximal, minimal, buffer); } } for (i = 0; i < UPRV_LENGTHOF(full_data); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const maximal = full_data[i][1]; const char* const minimal = full_data[i][2]; if (strlen(maximal) > 0) { /* const int32_t length = */ uloc_minimizeSubtags( maximal, buffer, sizeof(buffer), &status); if (U_FAILURE(status)) { log_err_status(status, " unexpected failure of uloc_minimizeSubtags(), maximal \"%s\" status %s\n", maximal, u_errorName(status)); status = U_ZERO_ERROR; } else if (uprv_strlen(minimal) == 0) { if (uprv_stricmp(maximal, buffer) != 0) { log_err(" unexpected minimal value \"%s\" in uloc_minimizeSubtags(), maximal \"%s\" = \"%s\"\n", minimal, maximal, buffer); } } else if (uprv_stricmp(minimal, buffer) != 0) { log_err(" minimal doesn't match expected %s in uloc_MinimizeSubtags(), maximal \"%s\" = %s\n", minimal, maximal, buffer); } } } for (i = 0; i < UPRV_LENGTHOF(maximizeErrors); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const minimal = maximizeErrors[i].tag; const char* const maximal = maximizeErrors[i].expected; const UErrorCode expectedStatus = maximizeErrors[i].uerror; const int32_t expectedLength = getExpectedReturnValue(&maximizeErrors[i]); const int32_t bufferSize = getBufferSize(&maximizeErrors[i], sizeof(buffer)); const int32_t length = uloc_addLikelySubtags( minimal, buffer, bufferSize, &status); if (status == U_ZERO_ERROR) { log_err(" unexpected U_ZERO_ERROR for uloc_addLikelySubtags(), minimal \"%s\" expected status %s\n", minimal, u_errorName(expectedStatus)); status = U_ZERO_ERROR; } else if (status != expectedStatus) { log_err_status(status, " unexpected status for uloc_addLikelySubtags(), minimal \"%s\" expected status %s, but got %s\n", minimal, u_errorName(expectedStatus), u_errorName(status)); } else if (length != expectedLength) { log_err(" unexpected length for uloc_addLikelySubtags(), minimal \"%s\" expected length %d, but got %d\n", minimal, expectedLength, length); } else if (status == U_BUFFER_OVERFLOW_ERROR || status == U_STRING_NOT_TERMINATED_WARNING) { if (uprv_strnicmp(maximal, buffer, bufferSize) != 0) { log_err(" maximal doesn't match expected %s in uloc_addLikelySubtags(), minimal \"%s\" = %*s\n", maximal, minimal, (int)sizeof(buffer), buffer); } } } for (i = 0; i < UPRV_LENGTHOF(minimizeErrors); ++i) { UErrorCode status = U_ZERO_ERROR; const char* const maximal = minimizeErrors[i].tag; const char* const minimal = minimizeErrors[i].expected; const UErrorCode expectedStatus = minimizeErrors[i].uerror; const int32_t expectedLength = getExpectedReturnValue(&minimizeErrors[i]); const int32_t bufferSize = getBufferSize(&minimizeErrors[i], sizeof(buffer)); const int32_t length = uloc_minimizeSubtags( maximal, buffer, bufferSize, &status); if (status == U_ZERO_ERROR) { log_err(" unexpected U_ZERO_ERROR for uloc_minimizeSubtags(), maximal \"%s\" expected status %s\n", maximal, u_errorName(expectedStatus)); status = U_ZERO_ERROR; } else if (status != expectedStatus) { log_err_status(status, " unexpected status for uloc_minimizeSubtags(), maximal \"%s\" expected status %s, but got %s\n", maximal, u_errorName(expectedStatus), u_errorName(status)); } else if (length != expectedLength) { log_err(" unexpected length for uloc_minimizeSubtags(), maximal \"%s\" expected length %d, but got %d\n", maximal, expectedLength, length); } else if (status == U_BUFFER_OVERFLOW_ERROR || status == U_STRING_NOT_TERMINATED_WARNING) { if (uprv_strnicmp(minimal, buffer, bufferSize) != 0) { log_err(" minimal doesn't match expected \"%s\" in uloc_minimizeSubtags(), minimal \"%s\" = \"%*s\"\n", minimal, maximal, (int)sizeof(buffer), buffer); } } } } const char* const locale_to_langtag[][3] = { {"", "und", "und"}, {"en", "en", "en"}, {"en_US", "en-US", "en-US"}, {"iw_IL", "he-IL", "he-IL"}, {"sr_Latn_SR", "sr-Latn-SR", "sr-Latn-SR"}, {"en__POSIX", "en-u-va-posix", "en-u-va-posix"}, {"en_POSIX", "en-u-va-posix", "en-u-va-posix"}, {"en_US_POSIX_VAR", "en-US-posix-x-lvariant-var", NULL}, /* variant POSIX_VAR is processed as regular variant */ {"en_US_VAR_POSIX", "en-US-x-lvariant-var-posix", NULL}, /* variant VAR_POSIX is processed as regular variant */ {"en_US_POSIX@va=posix2", "en-US-u-va-posix2", "en-US-u-va-posix2"}, /* if keyword va=xxx already exists, variant POSIX is simply dropped */ {"en_US_POSIX@ca=japanese", "en-US-u-ca-japanese-va-posix", "en-US-u-ca-japanese-va-posix"}, {"und_555", "und-555", "und-555"}, {"123", "und", NULL}, {"%$#&", "und", NULL}, {"_Latn", "und-Latn", "und-Latn"}, {"_DE", "und-DE", "und-DE"}, {"und_FR", "und-FR", "und-FR"}, {"th_TH_TH", "th-TH-x-lvariant-th", NULL}, {"bogus", "bogus", "bogus"}, {"foooobarrr", "und", NULL}, {"aa_BB_CYRL", "aa-BB-x-lvariant-cyrl", NULL}, {"en_US_1234", "en-US-1234", "en-US-1234"}, {"en_US_VARIANTA_VARIANTB", "en-US-varianta-variantb", "en-US-varianta-variantb"}, {"en_US_VARIANTB_VARIANTA", "en-US-varianta-variantb", "en-US-varianta-variantb"}, /* ICU-20478 */ {"ja__9876_5432", "ja-5432-9876", "ja-5432-9876"}, /* ICU-20478 */ {"sl__ROZAJ_BISKE_1994", "sl-1994-biske-rozaj", "sl-1994-biske-rozaj"}, /* ICU-20478 */ {"en__SCOUSE_FONIPA", "en-fonipa-scouse", "en-fonipa-scouse"}, /* ICU-20478 */ {"zh_Hant__VAR", "zh-Hant-x-lvariant-var", NULL}, {"es__BADVARIANT_GOODVAR", "es-goodvar", NULL}, {"en@calendar=gregorian", "en-u-ca-gregory", "en-u-ca-gregory"}, {"de@collation=phonebook;calendar=gregorian", "de-u-ca-gregory-co-phonebk", "de-u-ca-gregory-co-phonebk"}, {"th@numbers=thai;z=extz;x=priv-use;a=exta", "th-a-exta-u-nu-thai-z-extz-x-priv-use", "th-a-exta-u-nu-thai-z-extz-x-priv-use"}, {"en@timezone=America/New_York;calendar=japanese", "en-u-ca-japanese-tz-usnyc", "en-u-ca-japanese-tz-usnyc"}, {"en@timezone=US/Eastern", "en-u-tz-usnyc", "en-u-tz-usnyc"}, {"en@x=x-y-z;a=a-b-c", "en-x-x-y-z", NULL}, {"it@collation=badcollationtype;colStrength=identical;cu=usd-eur", "it-u-cu-usd-eur-ks-identic", NULL}, {"en_US_POSIX", "en-US-u-va-posix", "en-US-u-va-posix"}, {"en_US_POSIX@calendar=japanese;currency=EUR","en-US-u-ca-japanese-cu-eur-va-posix", "en-US-u-ca-japanese-cu-eur-va-posix"}, {"@x=elmer", "und-x-elmer", "und-x-elmer"}, {"en@x=elmer", "en-x-elmer", "en-x-elmer"}, {"@x=elmer;a=exta", "und-a-exta-x-elmer", "und-a-exta-x-elmer"}, {"en_US@attribute=attr1-attr2;calendar=gregorian", "en-US-u-attr1-attr2-ca-gregory", "en-US-u-attr1-attr2-ca-gregory"}, /* #12671 */ {"en@a=bar;attribute=baz", "en-a-bar-u-baz", "en-a-bar-u-baz"}, {"en@a=bar;attribute=baz;x=u-foo", "en-a-bar-u-baz-x-u-foo", "en-a-bar-u-baz-x-u-foo"}, {"en@attribute=baz", "en-u-baz", "en-u-baz"}, {"en@attribute=baz;calendar=islamic-civil", "en-u-baz-ca-islamic-civil", "en-u-baz-ca-islamic-civil"}, {"en@a=bar;calendar=islamic-civil;x=u-foo", "en-a-bar-u-ca-islamic-civil-x-u-foo", "en-a-bar-u-ca-islamic-civil-x-u-foo"}, {"en@a=bar;attribute=baz;calendar=islamic-civil;x=u-foo", "en-a-bar-u-baz-ca-islamic-civil-x-u-foo", "en-a-bar-u-baz-ca-islamic-civil-x-u-foo"}, {"en@9=efg;a=baz", "en-9-efg-a-baz", "en-9-efg-a-baz"}, // Before ICU 64, ICU locale canonicalization had some additional mappings. // They were removed for ICU-20187 "drop support for long-obsolete locale ID variants". // The following now uses standard canonicalization. {"az_AZ_CYRL", "az-AZ-x-lvariant-cyrl", NULL}, /* ICU-20310 */ {"en-u-kn-true", "en-u-kn", "en-u-kn"}, {"en-u-kn", "en-u-kn", "en-u-kn"}, {"de-u-co-yes", "de-u-co", "de-u-co"}, {"de-u-co", "de-u-co", "de-u-co"}, {"de@collation=yes", "de-u-co", "de-u-co"}, {"cmn-hans-cn-u-ca-t-ca-x-t-u", "cmn-Hans-CN-t-ca-u-ca-x-t-u", "cmn-Hans-CN-t-ca-u-ca-x-t-u"}, {NULL, NULL, NULL} }; static void TestToLanguageTag(void) { char langtag[256]; int32_t i; UErrorCode status; int32_t len; const char *inloc; const char *expected; for (i = 0; locale_to_langtag[i][0] != NULL; i++) { inloc = locale_to_langtag[i][0]; /* testing non-strict mode */ status = U_ZERO_ERROR; langtag[0] = 0; expected = locale_to_langtag[i][1]; len = uloc_toLanguageTag(inloc, langtag, sizeof(langtag), FALSE, &status); (void)len; /* Suppress set but not used warning. */ if (U_FAILURE(status)) { if (expected != NULL) { log_err("Error returned by uloc_toLanguageTag for locale id [%s] - error: %s\n", inloc, u_errorName(status)); } } else { if (expected == NULL) { log_err("Error should be returned by uloc_toLanguageTag for locale id [%s], but [%s] is returned without errors\n", inloc, langtag); } else if (uprv_strcmp(langtag, expected) != 0) { log_data_err("uloc_toLanguageTag returned language tag [%s] for input locale [%s] - expected: [%s]. Are you missing data?\n", langtag, inloc, expected); } } /* testing strict mode */ status = U_ZERO_ERROR; langtag[0] = 0; expected = locale_to_langtag[i][2]; len = uloc_toLanguageTag(inloc, langtag, sizeof(langtag), TRUE, &status); if (U_FAILURE(status)) { if (expected != NULL) { log_data_err("Error returned by uloc_toLanguageTag {strict} for locale id [%s] - error: %s Are you missing data?\n", inloc, u_errorName(status)); } } else { if (expected == NULL) { log_err("Error should be returned by uloc_toLanguageTag {strict} for locale id [%s], but [%s] is returned without errors\n", inloc, langtag); } else if (uprv_strcmp(langtag, expected) != 0) { log_err("uloc_toLanguageTag {strict} returned language tag [%s] for input locale [%s] - expected: [%s]\n", langtag, inloc, expected); } } } } static void TestBug20132(void) { char langtag[256]; UErrorCode status; int32_t len; static const char inloc[] = "en-C"; static const char expected[] = "en-x-lvariant-c"; const int32_t expected_len = (int32_t)uprv_strlen(expected); /* Before ICU-20132 was fixed, calling uloc_toLanguageTag() with a too small * buffer would not immediately return the buffer size actually needed, but * instead require several iterations before getting the correct size. */ status = U_ZERO_ERROR; len = uloc_toLanguageTag(inloc, langtag, 1, FALSE, &status); if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) { log_data_err("Error returned by uloc_toLanguageTag for locale id [%s] - error: %s Are you missing data?\n", inloc, u_errorName(status)); } if (len != expected_len) { log_err("Bad length returned by uloc_toLanguageTag for locale id [%s]: %i != %i\n", inloc, len, expected_len); } status = U_ZERO_ERROR; len = uloc_toLanguageTag(inloc, langtag, expected_len, FALSE, &status); if (U_FAILURE(status)) { log_data_err("Error returned by uloc_toLanguageTag for locale id [%s] - error: %s Are you missing data?\n", inloc, u_errorName(status)); } if (len != expected_len) { log_err("Bad length returned by uloc_toLanguageTag for locale id [%s]: %i != %i\n", inloc, len, expected_len); } else if (uprv_strncmp(langtag, expected, expected_len) != 0) { log_data_err("uloc_toLanguageTag returned language tag [%.*s] for input locale [%s] - expected: [%s]. Are you missing data?\n", len, langtag, inloc, expected); } } #define FULL_LENGTH -1 static const struct { const char *bcpID; const char *locID; int32_t len; } langtag_to_locale[] = { {"en", "en", FULL_LENGTH}, {"en-us", "en_US", FULL_LENGTH}, {"und-US", "_US", FULL_LENGTH}, {"und-latn", "_Latn", FULL_LENGTH}, {"en-US-posix", "en_US_POSIX", FULL_LENGTH}, {"de-de_euro", "de", 2}, {"kok-IN", "kok_IN", FULL_LENGTH}, {"123", "", 0}, {"en_us", "", 0}, {"en-latn-x", "en_Latn", 7}, {"art-lojban", "jbo", FULL_LENGTH}, {"zh-hakka", "hak", FULL_LENGTH}, {"zh-cmn-CH", "cmn_CH", FULL_LENGTH}, {"zh-cmn-CH-u-co-pinyin", "cmn_CH@collation=pinyin", FULL_LENGTH}, {"xxx-yy", "xxx_YY", FULL_LENGTH}, {"fr-234", "fr_234", FULL_LENGTH}, {"i-default", "en@x=i-default", FULL_LENGTH}, {"i-test", "", 0}, {"ja-jp-jp", "ja_JP", 5}, {"bogus", "bogus", FULL_LENGTH}, {"boguslang", "", 0}, {"EN-lATN-us", "en_Latn_US", FULL_LENGTH}, {"und-variant-1234", "__1234_VARIANT", FULL_LENGTH}, /* ICU-20478 */ {"ja-9876-5432", "ja__5432_9876", FULL_LENGTH}, /* ICU-20478 */ {"en-US-varianta-variantb", "en_US_VARIANTA_VARIANTB", FULL_LENGTH}, /* ICU-20478 */ {"en-US-variantb-varianta", "en_US_VARIANTA_VARIANTB", FULL_LENGTH}, /* ICU-20478 */ {"sl-rozaj-1994-biske", "sl__1994_BISKE_ROZAJ", FULL_LENGTH}, /* ICU-20478 */ {"sl-biske-1994-rozaj", "sl__1994_BISKE_ROZAJ", FULL_LENGTH}, /* ICU-20478 */ {"sl-1994-rozaj-biske", "sl__1994_BISKE_ROZAJ", FULL_LENGTH}, /* ICU-20478 */ {"sl-rozaj-biske-1994", "sl__1994_BISKE_ROZAJ", FULL_LENGTH}, /* ICU-20478 */ {"en-fonipa-scouse", "en__FONIPA_SCOUSE", FULL_LENGTH}, /* ICU-20478 */ {"en-scouse-fonipa", "en__FONIPA_SCOUSE", FULL_LENGTH}, /* ICU-20478 */ {"und-varzero-var1-vartwo", "__VARZERO", 11}, {"en-u-ca-gregory", "en@calendar=gregorian", FULL_LENGTH}, {"en-U-cu-USD", "en@currency=usd", FULL_LENGTH}, {"en-US-u-va-posix", "en_US_POSIX", FULL_LENGTH}, {"en-us-u-ca-gregory-va-posix", "en_US_POSIX@calendar=gregorian", FULL_LENGTH}, {"en-us-posix-u-va-posix", "en_US_POSIX@va=posix", FULL_LENGTH}, {"en-us-u-va-posix2", "en_US@va=posix2", FULL_LENGTH}, {"en-us-vari1-u-va-posix", "en_US_VARI1@va=posix", FULL_LENGTH}, {"ar-x-1-2-3", "ar@x=1-2-3", FULL_LENGTH}, {"fr-u-nu-latn-cu-eur", "fr@currency=eur;numbers=latn", FULL_LENGTH}, {"de-k-kext-u-co-phonebk-nu-latn", "de@collation=phonebook;k=kext;numbers=latn", FULL_LENGTH}, {"ja-u-cu-jpy-ca-jp", "ja@calendar=yes;currency=jpy;jp=yes", FULL_LENGTH}, {"en-us-u-tz-usnyc", "en_US@timezone=America/New_York", FULL_LENGTH}, {"und-a-abc-def", "und@a=abc-def", FULL_LENGTH}, {"zh-u-ca-chinese-x-u-ca-chinese", "zh@calendar=chinese;x=u-ca-chinese", FULL_LENGTH}, {"x-elmer", "@x=elmer", FULL_LENGTH}, {"en-US-u-attr1-attr2-ca-gregory", "en_US@attribute=attr1-attr2;calendar=gregorian", FULL_LENGTH}, {"sr-u-kn", "sr@colnumeric=yes", FULL_LENGTH}, {"de-u-kn-co-phonebk", "de@collation=phonebook;colnumeric=yes", FULL_LENGTH}, {"en-u-attr2-attr1-kn-kb", "en@attribute=attr1-attr2;colbackwards=yes;colnumeric=yes", FULL_LENGTH}, {"ja-u-ijkl-efgh-abcd-ca-japanese-xx-yyy-zzz-kn", "ja@attribute=abcd-efgh-ijkl;calendar=japanese;colnumeric=yes;xx=yyy-zzz", FULL_LENGTH}, {"de-u-xc-xphonebk-co-phonebk-ca-buddhist-mo-very-lo-extensi-xd-that-de-should-vc-probably-xz-killthebuffer", "de@calendar=buddhist;collation=phonebook;de=should;lo=extensi;mo=very;vc=probably;xc=xphonebk;xd=that;xz=yes", 91}, {"de-1901-1901", "de__1901", 7}, {"de-DE-1901-1901", "de_DE_1901", 10}, {"en-a-bbb-a-ccc", "en@a=bbb", 8}, /* #12761 */ {"en-a-bar-u-baz", "en@a=bar;attribute=baz", FULL_LENGTH}, {"en-a-bar-u-baz-x-u-foo", "en@a=bar;attribute=baz;x=u-foo", FULL_LENGTH}, {"en-u-baz", "en@attribute=baz", FULL_LENGTH}, {"en-u-baz-ca-islamic-civil", "en@attribute=baz;calendar=islamic-civil", FULL_LENGTH}, {"en-a-bar-u-ca-islamic-civil-x-u-foo", "en@a=bar;calendar=islamic-civil;x=u-foo", FULL_LENGTH}, {"en-a-bar-u-baz-ca-islamic-civil-x-u-foo", "en@a=bar;attribute=baz;calendar=islamic-civil;x=u-foo", FULL_LENGTH}, {"und-Arab-u-em-emoji", "_Arab@em=emoji", FULL_LENGTH}, {"und-Latn-u-em-emoji", "_Latn@em=emoji", FULL_LENGTH}, {"und-Latn-DE-u-em-emoji", "_Latn_DE@em=emoji", FULL_LENGTH}, {"und-Zzzz-DE-u-em-emoji", "_Zzzz_DE@em=emoji", FULL_LENGTH}, {"und-DE-u-em-emoji", "_DE@em=emoji", FULL_LENGTH}, // #20098 {"hant-cmn-cn", "hant", 4}, {"zh-cmn-TW", "cmn_TW", FULL_LENGTH}, {"zh-x_t-ab", "zh", 2}, {"zh-hans-cn-u-ca-x_t-u", "zh_Hans_CN@calendar=yes", 15}, /* #20140 dupe keys in U-extension */ {"zh-u-ca-chinese-ca-gregory", "zh@calendar=chinese", FULL_LENGTH}, {"zh-u-ca-gregory-co-pinyin-ca-chinese", "zh@calendar=gregorian;collation=pinyin", FULL_LENGTH}, {"de-latn-DE-1901-u-co-phonebk-co-pinyin-ca-gregory", "de_Latn_DE_1901@calendar=gregorian;collation=phonebook", FULL_LENGTH}, {"th-u-kf-nu-thai-kf-false", "th@colcasefirst=yes;numbers=thai", FULL_LENGTH}, /* #9562 IANA language tag data update */ {"en-gb-oed", "en_GB_OXENDICT", FULL_LENGTH}, {"i-navajo", "nv", FULL_LENGTH}, {"i-navajo-a-foo", "nv@a=foo", FULL_LENGTH}, {"i-navajo-latn-us", "nv_Latn_US", FULL_LENGTH}, {"sgn-br", "bzs", FULL_LENGTH}, {"sgn-br-u-co-phonebk", "bzs@collation=phonebook", FULL_LENGTH}, {"ja-latn-hepburn-heploc", "ja_Latn__ALALC97", FULL_LENGTH}, {"ja-latn-hepburn-heploc-u-ca-japanese", "ja_Latn__ALALC97@calendar=japanese", FULL_LENGTH}, {"en-a-bcde-0-fgh", "en@0=fgh;a=bcde", FULL_LENGTH}, }; static void TestForLanguageTag(void) { char locale[256]; int32_t i; UErrorCode status; int32_t parsedLen; int32_t expParsedLen; for (i = 0; i < UPRV_LENGTHOF(langtag_to_locale); i++) { status = U_ZERO_ERROR; locale[0] = 0; expParsedLen = langtag_to_locale[i].len; if (expParsedLen == FULL_LENGTH) { expParsedLen = (int32_t)uprv_strlen(langtag_to_locale[i].bcpID); } uloc_forLanguageTag(langtag_to_locale[i].bcpID, locale, sizeof(locale), &parsedLen, &status); if (U_FAILURE(status)) { log_err_status(status, "Error returned by uloc_forLanguageTag for language tag [%s] - error: %s\n", langtag_to_locale[i].bcpID, u_errorName(status)); } else { if (uprv_strcmp(langtag_to_locale[i].locID, locale) != 0) { log_data_err("uloc_forLanguageTag returned locale [%s] for input language tag [%s] - expected: [%s]\n", locale, langtag_to_locale[i].bcpID, langtag_to_locale[i].locID); } if (parsedLen != expParsedLen) { log_err("uloc_forLanguageTag parsed length of %d for input language tag [%s] - expected parsed length: %d\n", parsedLen, langtag_to_locale[i].bcpID, expParsedLen); } } } } static const struct { const char *input; const char *canonical; } langtag_to_canonical[] = { {"de-DD", "de-DE"}, {"de-DD-u-co-phonebk", "de-DE-u-co-phonebk"}, {"jw-id", "jv-ID"}, {"jw-id-u-ca-islamic-civil", "jv-ID-u-ca-islamic-civil"}, {"mo-md", "ro-MD"}, {"my-bu-u-nu-mymr", "my-MM-u-nu-mymr"}, {"yuu-ru", "yug-RU"}, }; static void TestLangAndRegionCanonicalize(void) { char locale[256]; char canonical[256]; int32_t i; UErrorCode status; for (i = 0; i < UPRV_LENGTHOF(langtag_to_canonical); i++) { status = U_ZERO_ERROR; const char* input = langtag_to_canonical[i].input; uloc_forLanguageTag(input, locale, sizeof(locale), NULL, &status); uloc_toLanguageTag(locale, canonical, sizeof(canonical), TRUE, &status); if (U_FAILURE(status)) { log_err_status(status, "Error returned by uloc_forLanguageTag or uloc_toLanguageTag " "for language tag [%s] - error: %s\n", input, u_errorName(status)); } else { const char* expected_canonical = langtag_to_canonical[i].canonical; if (uprv_strcmp(expected_canonical, canonical) != 0) { log_data_err("input language tag [%s] is canonicalized to [%s] - expected: [%s]\n", input, canonical, expected_canonical); } } } } static void TestToUnicodeLocaleKey(void) { /* $IN specifies the result should be the input pointer itself */ static const char* DATA[][2] = { {"calendar", "ca"}, {"CALEndar", "ca"}, /* difference casing */ {"ca", "ca"}, /* bcp key itself */ {"kv", "kv"}, /* no difference between legacy and bcp */ {"foo", NULL}, /* unknown, bcp ill-formed */ {"ZZ", "$IN"}, /* unknown, bcp well-formed - */ {NULL, NULL} }; int32_t i; for (i = 0; DATA[i][0] != NULL; i++) { const char* keyword = DATA[i][0]; const char* expected = DATA[i][1]; const char* bcpKey = NULL; bcpKey = uloc_toUnicodeLocaleKey(keyword); if (expected == NULL) { if (bcpKey != NULL) { log_err("toUnicodeLocaleKey: keyword=%s => %s, expected=NULL\n", keyword, bcpKey); } } else if (bcpKey == NULL) { log_data_err("toUnicodeLocaleKey: keyword=%s => NULL, expected=%s\n", keyword, expected); } else if (uprv_strcmp(expected, "$IN") == 0) { if (bcpKey != keyword) { log_err("toUnicodeLocaleKey: keyword=%s => %s, expected=%s(input pointer)\n", keyword, bcpKey, keyword); } } else if (uprv_strcmp(bcpKey, expected) != 0) { log_err("toUnicodeLocaleKey: keyword=%s => %s, expected=%s\n", keyword, bcpKey, expected); } } } static void TestBug20321UnicodeLocaleKey(void) { // key = alphanum alpha ; static const char* invalid[] = { "a0", "00", "a@", "0@", "@a", "@a", "abc", "0bc", }; for (int i = 0; i < UPRV_LENGTHOF(invalid); i++) { const char* bcpKey = NULL; bcpKey = uloc_toUnicodeLocaleKey(invalid[i]); if (bcpKey != NULL) { log_err("toUnicodeLocaleKey: keyword=%s => %s, expected=NULL\n", invalid[i], bcpKey); } } static const char* valid[] = { "aa", "0a", }; for (int i = 0; i < UPRV_LENGTHOF(valid); i++) { const char* bcpKey = NULL; bcpKey = uloc_toUnicodeLocaleKey(valid[i]); if (bcpKey == NULL) { log_err("toUnicodeLocaleKey: keyword=%s => NULL, expected!=NULL\n", valid[i]); } } } static void TestToLegacyKey(void) { /* $IN specifies the result should be the input pointer itself */ static const char* DATA[][2] = { {"kb", "colbackwards"}, {"kB", "colbackwards"}, /* different casing */ {"Collation", "collation"}, /* keyword itself with different casing */ {"kv", "kv"}, /* no difference between legacy and bcp */ {"foo", "$IN"}, /* unknown, bcp ill-formed */ {"ZZ", "$IN"}, /* unknown, bcp well-formed */ {"e=mc2", NULL}, /* unknown, bcp/legacy ill-formed */ {NULL, NULL} }; int32_t i; for (i = 0; DATA[i][0] != NULL; i++) { const char* keyword = DATA[i][0]; const char* expected = DATA[i][1]; const char* legacyKey = NULL; legacyKey = uloc_toLegacyKey(keyword); if (expected == NULL) { if (legacyKey != NULL) { log_err("toLegacyKey: keyword=%s => %s, expected=NULL\n", keyword, legacyKey); } } else if (legacyKey == NULL) { log_err("toLegacyKey: keyword=%s => NULL, expected=%s\n", keyword, expected); } else if (uprv_strcmp(expected, "$IN") == 0) { if (legacyKey != keyword) { log_err("toLegacyKey: keyword=%s => %s, expected=%s(input pointer)\n", keyword, legacyKey, keyword); } } else if (uprv_strcmp(legacyKey, expected) != 0) { log_data_err("toUnicodeLocaleKey: keyword=%s, %s, expected=%s\n", keyword, legacyKey, expected); } } } static void TestToUnicodeLocaleType(void) { /* $IN specifies the result should be the input pointer itself */ static const char* DATA[][3] = { {"tz", "Asia/Kolkata", "inccu"}, {"calendar", "gregorian", "gregory"}, {"ca", "gregorian", "gregory"}, {"ca", "Gregorian", "gregory"}, {"ca", "buddhist", "buddhist"}, {"Calendar", "Japanese", "japanese"}, {"calendar", "Islamic-Civil", "islamic-civil"}, {"calendar", "islamicc", "islamic-civil"}, /* bcp type alias */ {"colalternate", "NON-IGNORABLE", "noignore"}, {"colcaselevel", "yes", "true"}, {"rg", "GBzzzz", "$IN"}, {"tz", "america/new_york", "usnyc"}, {"tz", "Asia/Kolkata", "inccu"}, {"timezone", "navajo", "usden"}, {"ca", "aaaa", "$IN"}, /* unknown type, well-formed type */ {"ca", "gregory-japanese-islamic", "$IN"}, /* unknown type, well-formed type */ {"zz", "gregorian", NULL}, /* unknown key, ill-formed type */ {"co", "foo-", NULL}, /* unknown type, ill-formed type */ {"variableTop", "00A0", "$IN"}, /* valid codepoints type */ {"variableTop", "wxyz", "$IN"}, /* invalid codepoints type - return as is for now */ {"kr", "space-punct", "space-punct"}, /* valid reordercode type */ {"kr", "digit-spacepunct", NULL}, /* invalid (bcp ill-formed) reordercode type */ {NULL, NULL, NULL} }; int32_t i; for (i = 0; DATA[i][0] != NULL; i++) { const char* keyword = DATA[i][0]; const char* value = DATA[i][1]; const char* expected = DATA[i][2]; const char* bcpType = NULL; bcpType = uloc_toUnicodeLocaleType(keyword, value); if (expected == NULL) { if (bcpType != NULL) { log_err("toUnicodeLocaleType: keyword=%s, value=%s => %s, expected=NULL\n", keyword, value, bcpType); } } else if (bcpType == NULL) { log_data_err("toUnicodeLocaleType: keyword=%s, value=%s => NULL, expected=%s\n", keyword, value, expected); } else if (uprv_strcmp(expected, "$IN") == 0) { if (bcpType != value) { log_err("toUnicodeLocaleType: keyword=%s, value=%s => %s, expected=%s(input pointer)\n", keyword, value, bcpType, value); } } else if (uprv_strcmp(bcpType, expected) != 0) { log_data_err("toUnicodeLocaleType: keyword=%s, value=%s => %s, expected=%s\n", keyword, value, bcpType, expected); } } } static void TestToLegacyType(void) { /* $IN specifies the result should be the input pointer itself */ static const char* DATA[][3] = { {"calendar", "gregory", "gregorian"}, {"ca", "gregory", "gregorian"}, {"ca", "Gregory", "gregorian"}, {"ca", "buddhist", "buddhist"}, {"Calendar", "Japanese", "japanese"}, {"calendar", "Islamic-Civil", "islamic-civil"}, {"calendar", "islamicc", "islamic-civil"}, /* bcp type alias */ {"colalternate", "noignore", "non-ignorable"}, {"colcaselevel", "true", "yes"}, {"rg", "gbzzzz", "gbzzzz"}, {"tz", "usnyc", "America/New_York"}, {"tz", "inccu", "Asia/Calcutta"}, {"timezone", "usden", "America/Denver"}, {"timezone", "usnavajo", "America/Denver"}, /* bcp type alias */ {"colstrength", "quarternary", "quaternary"}, /* type alias */ {"ca", "aaaa", "$IN"}, /* unknown type */ {"calendar", "gregory-japanese-islamic", "$IN"}, /* unknown type, well-formed type */ {"zz", "gregorian", "$IN"}, /* unknown key, bcp ill-formed type */ {"ca", "gregorian-calendar", "$IN"}, /* known key, bcp ill-formed type */ {"co", "e=mc2", NULL}, /* known key, ill-formed bcp/legacy type */ {"variableTop", "00A0", "$IN"}, /* valid codepoints type */ {"variableTop", "wxyz", "$IN"}, /* invalid codepoints type - return as is for now */ {"kr", "space-punct", "space-punct"}, /* valid reordercode type */ {"kr", "digit-spacepunct", "digit-spacepunct"}, /* invalid reordercode type, but ok for legacy syntax */ {NULL, NULL, NULL} }; int32_t i; for (i = 0; DATA[i][0] != NULL; i++) { const char* keyword = DATA[i][0]; const char* value = DATA[i][1]; const char* expected = DATA[i][2]; const char* legacyType = NULL; legacyType = uloc_toLegacyType(keyword, value); if (expected == NULL) { if (legacyType != NULL) { log_err("toLegacyType: keyword=%s, value=%s => %s, expected=NULL\n", keyword, value, legacyType); } } else if (legacyType == NULL) { log_err("toLegacyType: keyword=%s, value=%s => NULL, expected=%s\n", keyword, value, expected); } else if (uprv_strcmp(expected, "$IN") == 0) { if (legacyType != value) { log_err("toLegacyType: keyword=%s, value=%s => %s, expected=%s(input pointer)\n", keyword, value, legacyType, value); } } else if (uprv_strcmp(legacyType, expected) != 0) { log_data_err("toLegacyType: keyword=%s, value=%s => %s, expected=%s\n", keyword, value, legacyType, expected); } else { log_verbose("toLegacyType: keyword=%s, value=%s => %s\n", keyword, value, legacyType); } } } static void test_unicode_define(const char *namech, char ch, const char *nameu, UChar uch) { UChar asUch[1]; asUch[0]=0; log_verbose("Testing whether %s[\\x%02x,'%c'] == %s[U+%04X]\n", namech, ch,(int)ch, nameu, (int) uch); u_charsToUChars(&ch, asUch, 1); if(asUch[0] != uch) { log_err("FAIL: %s[\\x%02x,'%c'] maps to U+%04X, but %s = U+%04X\n", namech, ch, (int)ch, (int)asUch[0], nameu, (int)uch); } else { log_verbose(" .. OK, == U+%04X\n", (int)asUch[0]); } } static void checkTerminating(const char* locale, const char* inLocale) { UErrorCode status = U_ZERO_ERROR; int32_t preflight_length = uloc_getDisplayName( locale, inLocale, NULL, 0, &status); if (status != U_BUFFER_OVERFLOW_ERROR) { log_err("uloc_getDisplayName(%s, %s) preflight failed", locale, inLocale); } UChar buff[256]; const UChar sentinel1 = 0x6C38; // 永- a Han unicode as sentinel. const UChar sentinel2 = 0x92D2; // 鋒- a Han unicode as sentinel. // 1. Test when we set the maxResultSize to preflight_length + 1. // Set sentinel1 in the buff[preflight_length-1] to check it will be // replaced with display name. buff[preflight_length-1] = sentinel1; // Set sentinel2 in the buff[preflight_length] to check it will be // replaced by null. buff[preflight_length] = sentinel2; // It should be properly null terminated at buff[preflight_length]. status = U_ZERO_ERROR; int32_t length = uloc_getDisplayName( locale, inLocale, buff, preflight_length + 1, &status); const char* result = U_SUCCESS(status) ? aescstrdup(buff, length) : "(undefined when failure)"; if (length != preflight_length) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length+1 returns " "length %d different from preflight length %d. Returns '%s'\n", locale, inLocale, length, preflight_length, result); } if (U_ZERO_ERROR != status) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length+1 should " "set status to U_ZERO_ERROR but got %d %s. Returns %s\n", locale, inLocale, status, myErrorName(status), result); } if (buff[length-1] == sentinel1) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length+1 does " "not change memory in the end of buffer while it should. " "Returns %s\n", locale, inLocale, result); } if (buff[length] != 0x0000) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length+1 should " "null terminate at buff[length] but does not %x. Returns %s\n", locale, inLocale, buff[length], result); } // 2. Test when we only set the maxResultSize to preflight_length. // Set sentinel1 in the buff[preflight_length-1] to check it will be // replaced with display name. buff[preflight_length-1] = sentinel1; // Set sentinel2 in the buff[preflight_length] to check it won't be replaced // by null. buff[preflight_length] = sentinel2; status = U_ZERO_ERROR; length = uloc_getDisplayName( locale, inLocale, buff, preflight_length, &status); result = U_SUCCESS(status) ? aescstrdup(buff, length) : "(undefined when failure)"; if (length != preflight_length) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length return " "length %d different from preflight length %d. Returns '%s'\n", locale, inLocale, length, preflight_length, result); } if (U_STRING_NOT_TERMINATED_WARNING != status) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length should " "set status to U_STRING_NOT_TERMINATED_WARNING but got %d %s. " "Returns %s\n", locale, inLocale, status, myErrorName(status), result); } if (buff[length-1] == sentinel1) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length does not " "change memory in the end of buffer while it should. Returns " "'%s'\n", locale, inLocale, result); } if (buff[length] != sentinel2) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length change " "memory beyond maxResultSize to %x. Returns '%s'\n", locale, inLocale, buff[length], result); } if (buff[preflight_length - 1] == 0x0000) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length null " "terminated while it should not. Return '%s'\n", locale, inLocale, result); } // 3. Test when we only set the maxResultSize to preflight_length-1. // Set sentinel1 in the buff[preflight_length-1] to check it will not be // replaced with display name. buff[preflight_length-1] = sentinel1; // Set sentinel2 in the buff[preflight_length] to check it won't be replaced // by null. buff[preflight_length] = sentinel2; status = U_ZERO_ERROR; length = uloc_getDisplayName( locale, inLocale, buff, preflight_length - 1, &status); result = U_SUCCESS(status) ? aescstrdup(buff, length) : "(undefined when failure)"; if (length != preflight_length) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length-1 return " "length %d different from preflight length %d. Returns '%s'\n", locale, inLocale, length, preflight_length, result); } if (U_BUFFER_OVERFLOW_ERROR != status) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length-1 should " "set status to U_BUFFER_OVERFLOW_ERROR but got %d %s. " "Returns %s\n", locale, inLocale, status, myErrorName(status), result); } if (buff[length-1] != sentinel1) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length-1 should " "not change memory in beyond the maxResultSize. Returns '%s'\n", locale, inLocale, result); } if (buff[length] != sentinel2) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length-1 change " "memory beyond maxResultSize to %x. Returns '%s'\n", locale, inLocale, buff[length], result); } if (buff[preflight_length - 2] == 0x0000) { log_err("uloc_getDisplayName(%s, %s) w/ maxResultSize=length-1 null " "terminated while it should not. Return '%s'\n", locale, inLocale, result); } } static void Test21157CorrectTerminating(void) { checkTerminating("fr", "fr"); checkTerminating("fr_BE", "fr"); checkTerminating("fr_Latn_BE", "fr"); checkTerminating("fr_Latn", "fr"); checkTerminating("fr", "fr"); checkTerminating("fr-CN", "fr"); checkTerminating("fr-Hant-CN", "fr"); checkTerminating("fr-Hant", "fr"); checkTerminating("zh-u-co-pinyin", "fr"); } #define TEST_UNICODE_DEFINE(x,y) test_unicode_define(#x, (char)(x), #y, (UChar)(y)) static void TestUnicodeDefines(void) { TEST_UNICODE_DEFINE(ULOC_KEYWORD_SEPARATOR, ULOC_KEYWORD_SEPARATOR_UNICODE); TEST_UNICODE_DEFINE(ULOC_KEYWORD_ASSIGN, ULOC_KEYWORD_ASSIGN_UNICODE); TEST_UNICODE_DEFINE(ULOC_KEYWORD_ITEM_SEPARATOR, ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE); } static void TestIsRightToLeft() { // API test only. More test cases in intltest/LocaleTest. if(uloc_isRightToLeft("root") || !uloc_isRightToLeft("EN-HEBR")) { log_err("uloc_isRightToLeft() failed"); } } typedef struct { const char * badLocaleID; const char * displayLocale; const char * expectedName; UErrorCode expectedStatus; } BadLocaleItem; static const BadLocaleItem badLocaleItems[] = { { "-9223372036854775808", "en", "Unknown language (9223372036854775808)", U_USING_DEFAULT_WARNING }, /* add more in the future */ { NULL, NULL, NULL, U_ZERO_ERROR } /* terminator */ }; enum { kUBufDispNameMax = 128, kBBufDispNameMax = 256 }; static void TestBadLocaleIDs() { const BadLocaleItem* itemPtr; for (itemPtr = badLocaleItems; itemPtr->badLocaleID != NULL; itemPtr++) { UChar ubufExpect[kUBufDispNameMax], ubufGet[kUBufDispNameMax]; UErrorCode status = U_ZERO_ERROR; int32_t ulenExpect = u_unescape(itemPtr->expectedName, ubufExpect, kUBufDispNameMax); int32_t ulenGet = uloc_getDisplayName(itemPtr->badLocaleID, itemPtr->displayLocale, ubufGet, kUBufDispNameMax, &status); if (status != itemPtr->expectedStatus || (U_SUCCESS(status) && (ulenGet != ulenExpect || u_strncmp(ubufGet, ubufExpect, ulenExpect) != 0))) { char bbufExpect[kBBufDispNameMax], bbufGet[kBBufDispNameMax]; u_austrncpy(bbufExpect, ubufExpect, ulenExpect); u_austrncpy(bbufGet, ubufGet, ulenGet); log_err("FAIL: For localeID %s, displayLocale %s, calling uloc_getDisplayName:\n" " expected status %-26s, name (len %2d): %s\n" " got status %-26s, name (len %2d): %s\n", itemPtr->badLocaleID, itemPtr->displayLocale, u_errorName(itemPtr->expectedStatus), ulenExpect, bbufExpect, u_errorName(status), ulenGet, bbufGet ); } } } // Test case for ICU-20370. // The issue shows as an Address Sanitizer failure. static void TestBug20370() { const char *localeID = "x-privatebutreallylongtagfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar"; uint32_t lcid = uloc_getLCID(localeID); if (lcid != 0) { log_err("FAIL: Expected LCID value of 0 for invalid localeID input."); } } // Test case for ICU-20149 // Handle the duplicate U extension attribute static void TestBug20149() { const char *localeID = "zh-u-foo-foo-co-pinyin"; char locale[256]; UErrorCode status = U_ZERO_ERROR; int32_t parsedLen; locale[0] = '\0'; uloc_forLanguageTag(localeID, locale, sizeof(locale), &parsedLen, &status); if (U_FAILURE(status) || 0 !=strcmp("zh@attribute=foo;collation=pinyin", locale)) { log_err("ERROR: in uloc_forLanguageTag %s return %s\n", myErrorName(status), locale); } } #if !UCONFIG_NO_FORMATTING typedef enum UldnNameType { TEST_ULDN_LOCALE, TEST_ULDN_LANGUAGE, TEST_ULDN_SCRIPT, TEST_ULDN_REGION, TEST_ULOC_LOCALE, // only valid with optStdMidLong TEST_ULOC_LANGUAGE, // only valid with optStdMidLong TEST_ULOC_SCRIPT, // only valid with optStdMidLong TEST_ULOC_REGION, // only valid with optStdMidLong } UldnNameType; typedef struct { const char * localeToName; // NULL to terminate a list of these UldnNameType nameType; const UChar * expectResult; } UldnItem; typedef struct { const char * displayLocale; const UDisplayContext * displayOptions; // set of 3 UDisplayContext items const UldnItem * testItems; int32_t countItems; } UldnLocAndOpts; static const UDisplayContext optStdMidLong[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, UDISPCTX_LENGTH_FULL}; static const UDisplayContext optStdMidShrt[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, UDISPCTX_LENGTH_SHORT}; static const UDisplayContext optDiaMidLong[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, UDISPCTX_LENGTH_FULL}; static const UDisplayContext optDiaMidShrt[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, UDISPCTX_LENGTH_SHORT}; static const UldnItem en_StdMidLong[] = { { "en_US", TEST_ULDN_LOCALE, u"English (United States)" }, { "en", TEST_ULDN_LANGUAGE, u"English" }, { "en_US", TEST_ULOC_LOCALE, u"English (United States)" }, { "en_US", TEST_ULOC_LANGUAGE, u"English" }, { "en", TEST_ULOC_LANGUAGE, u"English" }, // https://unicode-org.atlassian.net/browse/ICU-20870 { "fa_AF", TEST_ULDN_LOCALE, u"Persian (Afghanistan)" }, { "prs", TEST_ULDN_LOCALE, u"Dari" }, { "prs_AF", TEST_ULDN_LOCALE, u"Dari (Afghanistan)" }, { "prs_TJ", TEST_ULDN_LOCALE, u"Dari (Tajikistan)" }, { "prs", TEST_ULDN_LANGUAGE, u"Dari" }, { "prs", TEST_ULOC_LANGUAGE, u"Dari" }, // https://unicode-org.atlassian.net/browse/ICU-21742 { "ji", TEST_ULDN_LOCALE, u"Yiddish" }, { "ji_US", TEST_ULDN_LOCALE, u"Yiddish (United States)" }, { "ji", TEST_ULDN_LANGUAGE, u"Yiddish" }, { "ji_US", TEST_ULOC_LOCALE, u"Yiddish (United States)" }, { "ji", TEST_ULOC_LANGUAGE, u"Yiddish" }, // https://unicode-org.atlassian.net/browse/ICU-11563 { "mo", TEST_ULDN_LOCALE, u"Romanian" }, { "mo_MD", TEST_ULDN_LOCALE, u"Romanian (Moldova)" }, { "mo", TEST_ULDN_LANGUAGE, u"Romanian" }, { "mo_MD", TEST_ULOC_LOCALE, u"Romanian (Moldova)" }, { "mo", TEST_ULOC_LANGUAGE, u"Romanian" }, }; static const UldnItem en_StdMidShrt[] = { { "en_US", TEST_ULDN_LOCALE, u"English (US)" }, { "en", TEST_ULDN_LANGUAGE, u"English" }, }; static const UldnItem en_DiaMidLong[] = { { "en_US", TEST_ULDN_LOCALE, u"American English" }, { "fa_AF", TEST_ULDN_LOCALE, u"Dari" }, { "prs", TEST_ULDN_LOCALE, u"Dari" }, { "prs_AF", TEST_ULDN_LOCALE, u"Dari (Afghanistan)" }, { "prs_TJ", TEST_ULDN_LOCALE, u"Dari (Tajikistan)" }, { "prs", TEST_ULDN_LANGUAGE, u"Dari" }, { "mo", TEST_ULDN_LOCALE, u"Romanian" }, { "mo", TEST_ULDN_LANGUAGE, u"Romanian" }, }; static const UldnItem en_DiaMidShrt[] = { { "en_US", TEST_ULDN_LOCALE, u"US English" }, }; static const UldnItem ro_StdMidLong[] = { // https://unicode-org.atlassian.net/browse/ICU-11563 { "mo", TEST_ULDN_LOCALE, u"română" }, { "mo_MD", TEST_ULDN_LOCALE, u"română (Republica Moldova)" }, { "mo", TEST_ULDN_LANGUAGE, u"română" }, { "mo_MD", TEST_ULOC_LOCALE, u"română (Republica Moldova)" }, { "mo", TEST_ULOC_LANGUAGE, u"română" }, }; static const UldnItem yi_StdMidLong[] = { // https://unicode-org.atlassian.net/browse/ICU-21742 { "ji", TEST_ULDN_LOCALE, u"ייִדיש" }, { "ji_US", TEST_ULDN_LOCALE, u"ייִדיש (פֿאַראייניגטע שטאַטן)" }, { "ji", TEST_ULDN_LANGUAGE, u"ייִדיש" }, { "ji_US", TEST_ULOC_LOCALE, u"ייִדיש (פֿאַראייניגטע שטאַטן)" }, { "ji", TEST_ULOC_LANGUAGE, u"ייִדיש" }, }; static const UldnLocAndOpts uldnLocAndOpts[] = { { "en", optStdMidLong, en_StdMidLong, UPRV_LENGTHOF(en_StdMidLong) }, { "en", optStdMidShrt, en_StdMidShrt, UPRV_LENGTHOF(en_StdMidShrt) }, { "en", optDiaMidLong, en_DiaMidLong, UPRV_LENGTHOF(en_DiaMidLong) }, { "en", optDiaMidShrt, en_DiaMidShrt, UPRV_LENGTHOF(en_DiaMidShrt) }, { "ro", optStdMidLong, ro_StdMidLong, UPRV_LENGTHOF(ro_StdMidLong) }, { "yi", optStdMidLong, yi_StdMidLong, UPRV_LENGTHOF(yi_StdMidLong) }, { NULL, NULL, NULL, 0 } }; enum { kUNameBuf = 128, kBNameBuf = 256 }; static void TestUldnNameVariants() { const UldnLocAndOpts * uloPtr; for (uloPtr = uldnLocAndOpts; uloPtr->displayLocale != NULL; uloPtr++) { UErrorCode status = U_ZERO_ERROR; ULocaleDisplayNames * uldn = uldn_openForContext(uloPtr->displayLocale, (UDisplayContext*)uloPtr->displayOptions, 3, &status); if (U_FAILURE(status)) { log_data_err("uldn_openForContext fails, displayLocale %s, contexts %03X %03X %03X: %s - Are you missing data?\n", uloPtr->displayLocale, uloPtr->displayOptions[0], uloPtr->displayOptions[1], uloPtr->displayOptions[2], u_errorName(status) ); continue; } const UldnItem * itemPtr = uloPtr->testItems; int32_t itemCount = uloPtr->countItems; for (; itemCount-- > 0; itemPtr++) { UChar uget[kUNameBuf]; int32_t ulenget, ulenexp; const char* typeString; status = U_ZERO_ERROR; switch (itemPtr->nameType) { case TEST_ULDN_LOCALE: ulenget = uldn_localeDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); typeString = "uldn_localeDisplayName"; break; case TEST_ULDN_LANGUAGE: ulenget = uldn_languageDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); typeString = "uldn_languageDisplayName"; break; case TEST_ULDN_SCRIPT: ulenget = uldn_scriptDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); typeString = "uldn_scriptDisplayName"; break; case TEST_ULDN_REGION: ulenget = uldn_regionDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); typeString = "uldn_regionDisplayName"; break; case TEST_ULOC_LOCALE: ulenget = uloc_getDisplayName(itemPtr->localeToName, uloPtr->displayLocale, uget, kUNameBuf, &status); typeString = "uloc_getDisplayName"; break; case TEST_ULOC_LANGUAGE: ulenget = uloc_getDisplayLanguage(itemPtr->localeToName, uloPtr->displayLocale, uget, kUNameBuf, &status); typeString = "uloc_getDisplayLanguage"; break; case TEST_ULOC_SCRIPT: ulenget = uloc_getDisplayScript(itemPtr->localeToName, uloPtr->displayLocale, uget, kUNameBuf, &status); typeString = "uloc_getDisplayScript"; break; case TEST_ULOC_REGION: ulenget = uloc_getDisplayCountry(itemPtr->localeToName, uloPtr->displayLocale, uget, kUNameBuf, &status); typeString = "uloc_getDisplayCountry"; break; default: continue; } if (U_FAILURE(status)) { log_data_err("%s fails, displayLocale %s, contexts %03X %03X %03X, localeToName %s: %s\n", typeString, uloPtr->displayLocale, uloPtr->displayOptions[0], uloPtr->displayOptions[1], uloPtr->displayOptions[2], itemPtr->localeToName, u_errorName(status) ); continue; } ulenexp = u_strlen(itemPtr->expectResult); if (ulenget != ulenexp || u_strncmp(uget, itemPtr->expectResult, ulenexp) != 0) { char bexp[kBNameBuf], bget[kBNameBuf]; u_strToUTF8(bexp, kBNameBuf, NULL, itemPtr->expectResult, ulenexp, &status); u_strToUTF8(bget, kBNameBuf, NULL, uget, ulenget, &status); log_data_err("%s fails, displayLocale %s, contexts %03X %03X %03X, localeToName %s:\n expect %2d: %s\n get %2d: %s\n", typeString, uloPtr->displayLocale, uloPtr->displayOptions[0], uloPtr->displayOptions[1], uloPtr->displayOptions[2], itemPtr->localeToName, ulenexp, bexp, ulenget, bget ); } } uldn_close(uldn); } } #endif static void TestUsingDefaultWarning() { UChar buff[256]; char errorOutputBuff[256]; UErrorCode status = U_ZERO_ERROR; const char* language = "jJj"; int32_t length = uloc_getDisplayLanguage(language, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"jjj") != 0 || length != 3) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayLanguage %s return len:%d %s with status %d %s\n", language, length, errorOutputBuff, status, myErrorName(status)); } status = U_ZERO_ERROR; const char* script = "und-lALA"; length = uloc_getDisplayScript(script, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"Lala") != 0 || length != 4) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayScript %s return len:%d %s with status %d %s\n", script, length, errorOutputBuff, status, myErrorName(status)); } status = U_ZERO_ERROR; const char* region = "und-wt"; length = uloc_getDisplayCountry(region, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"WT") != 0 || length != 2) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayCountry %s return len:%d %s with status %d %s\n", region, length, errorOutputBuff, status, myErrorName(status)); } status = U_ZERO_ERROR; const char* variant = "und-abcde"; length = uloc_getDisplayVariant(variant, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"ABCDE") != 0 || length != 5) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayVariant %s return len:%d %s with status %d %s\n", variant, length, errorOutputBuff, status, myErrorName(status)); } status = U_ZERO_ERROR; const char* keyword = "postCODE"; length = uloc_getDisplayKeyword(keyword, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"postCODE") != 0 || length != 8) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayKeyword %s return len:%d %s with status %d %s\n", keyword, length, errorOutputBuff, status, myErrorName(status)); } status = U_ZERO_ERROR; const char* keyword_value = "de_DE@postCode=fOObAR"; length = uloc_getDisplayKeywordValue(keyword_value, keyword, "de", buff, 256, &status); if (status != U_USING_DEFAULT_WARNING || u_strcmp(buff, u"fOObAR") != 0 || length != 6) { u_UCharsToChars(buff, errorOutputBuff, length+1); log_err("ERROR: in uloc_getDisplayKeywordValue %s %s return len:%d %s with status %d %s\n", keyword_value, keyword, length, errorOutputBuff, status, myErrorName(status)); } } // Test case for ICU-20575 // This test checks if the environment variable LANG is set, // and if so ensures that both C and C.UTF-8 cause ICU's default locale to be en_US_POSIX. static void TestCDefaultLocale() { const char *defaultLocale = uloc_getDefault(); char *env_var = getenv("LANG"); if (env_var == NULL) { log_verbose("Skipping TestCDefaultLocale test, as the LANG variable is not set."); return; } if ((strcmp(env_var, "C") == 0 || strcmp(env_var, "C.UTF-8") == 0) && strcmp(defaultLocale, "en_US_POSIX") != 0) { log_err("The default locale for LANG=%s should be en_US_POSIX, not %s\n", env_var, defaultLocale); } } // Test case for ICU-21449 static void TestBug21449InfiniteLoop() { UErrorCode status = U_ZERO_ERROR; const char* invalidLocaleId = RES_PATH_SEPARATOR_S; // The issue causes an infinite loop to occur when looking up a non-existent resource for the invalid locale ID, // so the test is considered passed if the call to the API below returns anything at all. uloc_getDisplayLanguage(invalidLocaleId, invalidLocaleId, NULL, 0, &status); } // rdar://79296849 and https://unicode-org.atlassian.net/browse/ICU-21639 static void TestExcessivelyLongIDs(void) { const char* reallyLongID = "de-u-cu-eur-em-default-hc-h23-ks-level1-lb-strict-lw-normal-ms-metric" "-nu-latn-rg-atzzzz-sd-atat1-ss-none-tz-atvie-va-posix"; char minimizedID[ULOC_FULLNAME_CAPACITY]; char maximizedID[ULOC_FULLNAME_CAPACITY]; int32_t actualMinimizedLength = 0; int32_t actualMaximizedLength = 0; UErrorCode err = U_ZERO_ERROR; actualMinimizedLength = uloc_minimizeSubtags(reallyLongID, minimizedID, ULOC_FULLNAME_CAPACITY, &err); assertTrue("uloc_minimizeSubtags() with too-small buffer didn't fail as expected", U_FAILURE(err) && actualMinimizedLength > ULOC_FULLNAME_CAPACITY); err = U_ZERO_ERROR; actualMaximizedLength = uloc_addLikelySubtags(reallyLongID, maximizedID, ULOC_FULLNAME_CAPACITY, &err); assertTrue("uloc_addLikelySubtags() with too-small buffer didn't fail as expected", U_FAILURE(err) && actualMaximizedLength > ULOC_FULLNAME_CAPACITY); err = U_ZERO_ERROR; char* realMinimizedID = (char*)uprv_malloc(actualMinimizedLength + 1); uloc_minimizeSubtags(reallyLongID, realMinimizedID, actualMinimizedLength + 1, &err); if (assertSuccess("uloc_minimizeSubtags() failed", &err)) { assertEquals("Wrong result from uloc_minimizeSubtags()", "de__POSIX@colstrength=primary;currency=eur;em=default;hours=h23;lb=strict;" "lw=normal;measure=metric;numbers=latn;rg=atzzzz;sd=atat1;ss=none;timezone=Europe/Vienna", realMinimizedID); } uprv_free(realMinimizedID); char* realMaximizedID = (char*)uprv_malloc(actualMaximizedLength + 1); uloc_addLikelySubtags(reallyLongID, realMaximizedID, actualMaximizedLength + 1, &err); if (assertSuccess("uloc_addLikelySubtags() failed", &err)) { assertEquals("Wrong result from uloc_addLikelySubtags()", "de_Latn_DE_POSIX@colstrength=primary;currency=eur;em=default;hours=h23;lb=strict;" "lw=normal;measure=metric;numbers=latn;rg=atzzzz;sd=atat1;ss=none;timezone=Europe/Vienna", realMaximizedID); } uprv_free(realMaximizedID); }