X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b75a7d8f3b4adbae880cab104ce2c6a50eee4db2..2ca993e82fb37b597a3c73ecd1586a139a6579c5:/icuSources/test/cintltst/cloctst.c diff --git a/icuSources/test/cintltst/cloctst.c b/icuSources/test/cintltst/cloctst.c index baa1da1c..281aa1eb 100644 --- a/icuSources/test/cintltst/cloctst.c +++ b/icuSources/test/cintltst/cloctst.c @@ -1,116 +1,168 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2003, International Business Machines Corporation and + * 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 "unicode/utypes.h" -#include "unicode/putil.h" -#include "cloctst.h" -#include "unicode/uloc.h" -#include "unicode/uscript.h" -#include "unicode/uchar.h" -#include "unicode/ustring.h" -#include "unicode/uset.h" #include "cintltst.h" +#include "cmemory.h" #include "cstring.h" +#include "uparse.h" +#include "uresimp.h" +#include "cmemory.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" +#include "unicode/ualoc.h" /* Apple-specific */ + +static void TestNullDefault(void); +static void TestNonexistentLanguageExemplars(void); +static void TestLocDataErrorCodeChaining(void); +static void TestLocDataWithRgTag(void); +static void TestLanguageExemplarsFallbacks(void); +static void TestDisplayNameBrackets(void); -#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) +static void TestUnicodeDefines(void); -#ifdef WIN32 -#include "locmap.h" -#endif +static void TestIsRightToLeft(void); +static void TestUldnNameVariants(void); + +static void TestGetLanguagesForRegion(void); +static void TestGetAppleParent(void); +static void TestAppleLocalizationsToUse(void); -static void TestNullDefault(void); -static void VerifyTranslation(void); void PrintDataTable(); /*--------------------------------------------------- table of valid data --------------------------------------------------- */ -#define LOCALE_SIZE 5 -#define LOCALE_INFO_SIZE 23 +#define LOCALE_SIZE 9 +#define LOCALE_INFO_SIZE 28 -static const char* rawData2[LOCALE_INFO_SIZE][LOCALE_SIZE] = { +static const char* const rawData2[LOCALE_INFO_SIZE][LOCALE_SIZE] = { /* language code */ - { "en", "fr", "hr", "el", "no" }, + { "en", "fr", "ca", "el", "no", "zh", "de", "es", "ja" }, + /* script code */ + { "", "", "", "", "", "", "", "", "" }, /* country code */ - { "US", "FR", "HR", "GR", "NO" }, + { "US", "FR", "ES", "GR", "NO", "CN", "DE", "", "JP" }, /* variant code */ - { "", "", "", "", "NY" }, + { "", "", "", "", "NY", "", "", "", "" }, /* full name */ - { "en_US", "fr_FR", "hr_HR", "el_GR", "no_NO_NY" }, + { "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", "hrv", "ell", "nor" }, + { "eng", "fra", "cat", "ell", "nor", "zho", "deu", "spa", "jpn" }, /* ISO-3 country */ - { "USA", "FRA", "HRV", "GRC", "NOR" }, - /* LCID (not currently public) */ - { "409", "40c", "41a", "408", "814" }, + { "USA", "FRA", "ESP", "GRC", "NOR", "CHN", "DEU", "", "JPN" }, + /* LCID */ + { "409", "40c", "403", "408", "814", "804", "10407", "40a", "411" }, /* display language (English) */ - { "English", "French", "Croatian", "Greek", "Norwegian" }, + { "English", "French", "Catalan", "Greek", "Norwegian", "Chinese", "German", "Spanish", "Japanese" }, + /* display script code (English) */ + { "", "", "", "", "", "Simplified Han", "", "", "" }, /* display country (English) */ - { "United States", "France", "Croatia", "Greece", "Norway" }, + { "United States", "France", "Spain", "Greece", "Norway", "China", "Germany", "", "Japan" }, /* display variant (English) */ - { "", "", "", "", "Nynorsk" }, + { "", "", "", "", "NY", "", "", "", "" }, /* display name (English) */ - { "English (United States)", "French (France)", "Croatian (Croatia)", "Greek (Greece)", "Norwegian (Norway, Nynorsk)" }, + { "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", "croate", "grec", "norv\\u00E9gien" }, + { "anglais", "fran\\u00E7ais", "catalan", "grec", "norv\\u00E9gien", "chinois", "allemand", "espagnol", "japonais" }, + /* display script code (French) */ + { "", "", "", "", "", "sinogrammes simplifi\\u00e9s", "", "", "" }, /* display country (French) */ - { "\\u00C9tats-Unis", "France", "Croatie", "Gr\\u00E8ce", "Norv\\u00E8ge" }, + { "\\u00C9tats-Unis", "France", "Espagne", "Gr\\u00E8ce", "Norv\\u00E8ge", "Chine", "Allemagne", "", "Japon" }, /* display variant (French) */ - { "", "", "", "", "Nynorsk" }, + { "", "", "", "", "NY", "", "", "", "" }, /* display name (French) */ - { "anglais (\\u00C9tats-Unis)", "fran\\u00E7ais (France)", "croate (Croatie)", "grec (Gr\\u00E8ce)", "norv\\u00E9gien (Norv\\u00E8ge, Nynorsk)" }, - - /* display language (Croatian) */ - { "", "", "hrvatski", "", "" }, - /* display country (Croatian) */ - { "", "", "Hrvatska", "", "" }, - /* display variant (Croatian) */ - { "", "", "", "", "Nynorsk" }, - /* display name (Croatian) */ - { "", "", "hrvatski (Hrvatska)", "", "" }, + { "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\\u03c1\\u03bf\\u03b1\\u03c4\\u03b9\\u03ba\\u03ac", - "\\u03b5\\u03bb\\u03bb\\u03b7\\u03bd\\u03b9\\u03ba\\u03ac", - "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\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 \\u0391\\u03bc\\u03b5\\u03c1\\u03b9\\u03ba\\u03ae\\u03c2", + "\\u0397\\u03BD\\u03C9\\u03BC\\u03AD\\u03BD\\u03B5\\u03C2 \\u03A0\\u03BF\\u03BB\\u03B9\\u03C4\\u03B5\\u03AF\\u03B5\\u03C2", "\\u0393\\u03b1\\u03bb\\u03bb\\u03af\\u03b1", - "\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03af\\u03b1", + "\\u0399\\u03c3\\u03c0\\u03b1\\u03bd\\u03af\\u03b1", "\\u0395\\u03bb\\u03bb\\u03ac\\u03b4\\u03b1", - "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03af\\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) */ - { "", "", "", "", "Nynorsk" }, + { "", "", "", "", "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 \\u0391\\u03bc\\u03b5\\u03c1\\u03b9\\u03ba\\u03ae\\u03c2)", + "\\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\\u03c1\\u03bf\\u03b1\\u03c4\\u03b9\\u03ba\\u03ac (\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03af\\u03b1)", - "\\u03b5\\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, Nynorsk)" + "\\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)" } }; @@ -118,57 +170,104 @@ static UChar*** dataTable=0; enum { ENGLISH = 0, FRENCH = 1, - CROATIAN = 2, + CATALAN = 2, GREEK = 3, NORWEGIAN = 4 }; enum { LANG = 0, - CTRY = 1, - VAR = 2, - NAME = 3, - LANG3 = 4, - CTRY3 = 5, - LCID = 6, - DLANG_EN = 7, - DCTRY_EN = 8, - DVAR_EN = 9, - DNAME_EN = 10, - DLANG_FR = 11, - DCTRY_FR = 12, - DVAR_FR = 13, - DNAME_FR = 14, - DLANG_HR = 15, - DCTRY_HR = 16, - DVAR_HR = 17, - DNAME_HR = 18, - DLANG_EL = 19, - DCTRY_EL = 20, - DVAR_EL = 21, - DNAME_EL = 22 + 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) { - addTest(root, &TestObsoleteNames, "tsutil/cloctst/TestObsoleteNames"); /* srl- move */ - addTest(root, &TestBasicGetters, "tsutil/cloctst/TestBasicGetters"); - addTest(root, &TestNullDefault, "tsutil/cloctst/TestNullDefault"); - addTest(root, &TestPrefixes, "tsutil/cloctst/TestPrefixes"); - addTest(root, &TestSimpleResourceInfo, "tsutil/cloctst/TestSimpleResourceInfo"); - addTest(root, &TestDisplayNames, "tsutil/cloctst/TestDisplayNames"); - addTest(root, &TestGetAvailableLocales, "tsutil/cloctst/TestGetAvailableLocales"); - addTest(root, &TestDataDirectory, "tsutil/cloctst/TestDataDirectory"); - addTest(root, &TestISOFunctions, "tsutil/cloctst/TestISOFunctions"); - addTest(root, &TestISO3Fallback, "tsutil/cloctst/TestISO3Fallback"); - addTest(root, &TestUninstalledISO3Names, "tsutil/cloctst/TestUninstalledISO3Names"); - addTest(root, &TestSimpleDisplayNames, "tsutil/cloctst/TestSimpleDisplayNames"); - addTest(root, &TestVariantParsing, "tsutil/cloctst/TestVariantParsing"); - addTest(root, &TestLocaleStructure, "tsutil/cloctst/TestLocaleStructure"); - addTest(root, &TestConsistentCountryInfo,"tsutil/cloctst/TestConsistentCountryInfo"); - addTest(root, &VerifyTranslation, "tsutil/cloctst/VerifyTranslation"); + TESTCASE(TestObsoleteNames); /* srl- move */ + TESTCASE(TestBasicGetters); + TESTCASE(TestNullDefault); + TESTCASE(TestPrefixes); + TESTCASE(TestSimpleResourceInfo); + TESTCASE(TestDisplayNames); + TESTCASE(TestGetAvailableLocales); + 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(TestKeywordSet); + TESTCASE(TestKeywordSetError); + TESTCASE(TestDisplayKeywords); + TESTCASE(TestDisplayKeywordValues); + TESTCASE(TestGetBaseName); +#if !UCONFIG_NO_FILE_IO + TESTCASE(TestGetLocale); +#endif + TESTCASE(TestDisplayNameWarning); + 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(TestForLanguageTag); + TESTCASE(TestTrailingNull); + TESTCASE(TestUnicodeDefines); + TESTCASE(TestEnglishExemplarCharacters); + TESTCASE(TestDisplayNameBrackets); + TESTCASE(TestIsRightToLeft); + TESTCASE(TestToUnicodeLocaleKey); + TESTCASE(TestToLegacyKey); + TESTCASE(TestToUnicodeLocaleType); + TESTCASE(TestToLegacyType); + TESTCASE(TestUldnNameVariants); + TESTCASE(TestGetLanguagesForRegion); + TESTCASE(TestGetAppleParent); + TESTCASE(TestAppleLocalizationsToUse); } @@ -241,7 +340,6 @@ static void TestBasicGetters() { log_err(" Mismatch in getName: %s versus %s\n", name, rawData2[NAME][i]); } - free(temp); free(name); @@ -262,6 +360,47 @@ static void TestNullDefault() { 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)); + } + + } + } /* Test the i- and x- and @ and . functionality */ @@ -269,103 +408,135 @@ static void TestNullDefault() { #define PREFIXBUFSIZ 128 static void TestPrefixes() { - int row = 0; - int n; - const char *loc; - - const char *testData[][5] = - { - {"sv", "FI", "AL", "sv-fi-al", "sv_FI_AL" }, - {"en", "GB", "", "en-gb", "en_GB" }, - {"i-hakka", "MT", "XEMXIJA", "i-hakka_MT_XEMXIJA", "i-hakka_MT_XEMXIJA"}, - {"i-hakka", "CN", "", "i-hakka_CN", "i-hakka_CN"}, - {"i-hakka", "MX", "", "I-hakka_MX", "i-hakka_MX"}, - {"x-klingon", "US", "SANJOSE", "X-KLINGON_us_SANJOSE", "x-klingon_US_SANJOSE"}, - - {"mr", "", "", "mr.utf8", "mr"}, - {"de", "TV", "", "de-tv.koi8r", "de_TV"}, - {"x-piglatin", "ML", "", "x-piglatin_ML.MBE", "x-piglatin_ML"}, /* Multibyte English */ - {"i-cherokee","US", "", "i-Cherokee_US.utf7", "i-cherokee_US"}, - {"x-filfli", "MT", "FILFLA", "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA"}, - {"no", "NO", "NY", "no-no-ny.utf32@B", "no_NO_NY"}, /* @ ignored unless variant is empty */ - {"no", "NO", "B", "no-no.utf32@B", "no_NO_B" }, - {"no", "", "NY", "no__ny", "no__NY" }, - {"no", "", "NY", "no@ny", "no__NY" }, - - { "","","","",""} - }; - - const char *testTitles[] = { "uloc_getLanguage()", "uloc_getCountry()", "uloc_getVariant()", "name", "uloc_getName()", "country3", "lcid" }; - - char buf[PREFIXBUFSIZ]; - int32_t len; - UErrorCode err; - - - for(row=0;testData[row][0][0] != 0;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+1);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]); - } - - if(strcmp(buf, testData[row][n])) { - log_err("#%d: %s on %s: -> [%s] (expected '%s'!)\n", - row, testTitles[n], loc, buf, testData[row][n]); - + 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}, + + {"zh", "Hans", "", "PINYIN", "zh-Hans-pinyin", "zh_Hans__PINYIN", "zh_Hans@collation=pinyin"}, + {"hy", "", "", "AREVMDA", "hy_AREVMDA", "hy__AREVMDA", NULL}, + + {"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}, + {"zh", "Hant", "TW", "STROKE", "zh-hant_TW_STROKE", "zh_Hant_TW_STROKE", "zh_Hant_TW@collation=stroke"}, + {"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 */ + + {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); + + } + } } - } } - } } @@ -374,46 +545,120 @@ 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(); + + 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])); + 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])); + austrdup(dataTable[CTRY3][i])); } - sprintf(temp2, "%x", uloc_getLCID(testLocale)); + 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; } - free(expected); - free(testLocale); -cleanUpDataTable(); + 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); + } + } } @@ -537,12 +885,14 @@ static void doTestDisplayNames(const char* displayLocale, int32_t compareIndex) UChar *testLang = 0; + UChar *testScript = 0; UChar *testCtry = 0; UChar *testVar = 0; UChar *testName = 0; UChar* expectedLang = 0; + UChar* expectedScript = 0; UChar* expectedCtry = 0; UChar* expectedVar = 0; UChar* expectedName = 0; @@ -571,6 +921,22 @@ setUpDataTable(); log_err("Error in getDisplayLanguage() %s\n", myErrorName(status)); } + maxresultsize=0; + maxresultsize=uloc_getDisplayScript(testLocale, displayLocale, NULL, maxresultsize, &status); + if(status==U_BUFFER_OVERFLOW_ERROR) + { + status=U_ZERO_ERROR; + testScript=(UChar*)malloc(sizeof(UChar) * (maxresultsize+1)); + uloc_getDisplayScript(testLocale, displayLocale, testScript, maxresultsize + 1, &status); + } + else + { + testScript=&_NUL; + } + if(U_FAILURE(status)){ + log_err("Error in getDisplayScript() %s\n", myErrorName(status)); + } + maxresultsize=0; maxresultsize=uloc_getDisplayCountry(testLocale, displayLocale, NULL, maxresultsize, &status); if(status==U_BUFFER_OVERFLOW_ERROR) @@ -623,32 +989,40 @@ setUpDataTable(); if(u_strlen(expectedLang)== 0) expectedLang=dataTable[DLANG_EN][i]; - expectedCtry=dataTable[compareIndex + 1][i]; + expectedScript=dataTable[compareIndex + 1][i]; + if(u_strlen(expectedScript)== 0) + expectedScript=dataTable[DSCRIPT_EN][i]; + + expectedCtry=dataTable[compareIndex + 2][i]; if(u_strlen(expectedCtry)== 0) expectedCtry=dataTable[DCTRY_EN][i]; - expectedVar=dataTable[compareIndex + 2][i]; - if(u_strlen(expectedCtry)== 0) + expectedVar=dataTable[compareIndex + 3][i]; + if(u_strlen(expectedVar)== 0) expectedVar=dataTable[DVAR_EN][i]; - expectedName=dataTable[compareIndex + 3][i]; + expectedName=dataTable[compareIndex + 4][i]; if(u_strlen(expectedName) == 0) expectedName=dataTable[DNAME_EN][i]; if (0 !=u_strcmp(testLang,expectedLang)) { - log_data_err(" Display Language mismatch: got %s expected %s displayLocale=%s\n", austrdup(testLang), austrdup(expectedLang), displayLocale); + log_data_err(" Display Language mismatch: got %s expected %s displayLocale=%s (Are you missing data?)\n", austrdup(testLang), austrdup(expectedLang), displayLocale); + } + + if (0 != u_strcmp(testScript,expectedScript)) { + log_data_err(" Display Script mismatch: got %s expected %s displayLocale=%s (Are you missing data?)\n", austrdup(testScript), austrdup(expectedScript), displayLocale); } if (0 != u_strcmp(testCtry,expectedCtry)) { - log_data_err(" Display Country mismatch: got %s expected %s displayLocale=%s\n", austrdup(testCtry), austrdup(expectedCtry), displayLocale); + log_data_err(" Display Country mismatch: got %s expected %s displayLocale=%s (Are you missing data?)\n", austrdup(testCtry), austrdup(expectedCtry), displayLocale); } if (0 != u_strcmp(testVar,expectedVar)) { - log_data_err(" Display Variant mismatch: got %s expected %s displayLocale=%s\n", austrdup(testVar), austrdup(expectedVar), displayLocale); + log_data_err(" Display Variant mismatch: got %s expected %s displayLocale=%s (Are you missing data?)\n", austrdup(testVar), austrdup(expectedVar), displayLocale); } if(0 != u_strcmp(testName, expectedName)) { - log_data_err(" Display Name mismatch: got %s expected %s displayLocale=%s\n", austrdup(testName), austrdup(expectedName), displayLocale); + log_data_err(" Display Name mismatch: got %s expected %s displayLocale=%s (Are you missing data?)\n", austrdup(testName), austrdup(expectedName), displayLocale); } if(testName!=&_NUL) { @@ -657,6 +1031,9 @@ setUpDataTable(); if(testLang!=&_NUL) { free(testLang); } + if(testScript!=&_NUL) { + free(testScript); + } if(testCtry!=&_NUL) { free(testCtry); } @@ -667,82 +1044,244 @@ setUpDataTable(); cleanUpDataTable(); } +/*------------------------------ + * TestDisplayNameBrackets + */ + +typedef struct { + const char * displayLocale; + const char * namedRegion; + const char * namedLocale; + const char * regionName; + const char * ulocLocaleName; + const char * uldnLocaleName; +} DisplayNameBracketsItem; + +static const DisplayNameBracketsItem displayNameBracketsItems[] = { + { "en", "CC", "en_CC", "Cocos (Keeling) Islands", "English (Cocos [Keeling] Islands)", "English (Cocos [Keeling] Islands)" }, + { "en", "MM", "my_MM", "Myanmar (Burma)", "Burmese (Myanmar [Burma])", "Burmese (Myanmar)" }, + { "en", "MM", "my_Mymr_MM", "Myanmar (Burma)", "Burmese (Myanmar, Myanmar [Burma])", "Burmese (Myanmar, Myanmar)" }, + { "zh", "CC", "en_CC", "\\u79D1\\u79D1\\u65AF\\uFF08\\u57FA\\u6797\\uFF09\\u7FA4\\u5C9B", + "\\u82F1\\u6587\\uFF08\\u79D1\\u79D1\\u65AF\\uFF3B\\u57FA\\u6797\\uFF3D\\u7FA4\\u5C9B\\uFF09", + "\\u82F1\\u6587\\uFF08\\u79D1\\u79D1\\u65AF\\uFF3B\\u57FA\\u6797\\uFF3D\\u7FA4\\u5C9B\\uFF09" }, + { "zh", "CG", "fr_CG", "\\u521A\\u679C\\uFF08\\u5E03\\uFF09", + "\\u6CD5\\u6587\\uFF08\\u521A\\u679C\\uFF3B\\u5E03\\uFF3D\\uFF09", + "\\u6CD5\\u6587\\uFF08\\u521A\\u679C\\uFF3B\\u5E03\\uFF3D\\uFF09" }, + { NULL, NULL, NULL, NULL, NULL, NULL } +}; + +enum { kDisplayNameBracketsMax = 128 }; + +static void TestDisplayNameBrackets() +{ + const DisplayNameBracketsItem * itemPtr = displayNameBracketsItems; + for (; itemPtr->displayLocale != NULL; itemPtr++) { + ULocaleDisplayNames * uldn; + UErrorCode status; + UChar expectRegionName[kDisplayNameBracketsMax]; + UChar expectUlocLocaleName[kDisplayNameBracketsMax]; + UChar expectUldnLocaleName[kDisplayNameBracketsMax]; + UChar getName[kDisplayNameBracketsMax]; + int32_t ulen; + + (void) u_unescape(itemPtr->regionName, expectRegionName, kDisplayNameBracketsMax); + (void) u_unescape(itemPtr->ulocLocaleName, expectUlocLocaleName, kDisplayNameBracketsMax); + (void) u_unescape(itemPtr->uldnLocaleName, expectUldnLocaleName, 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, expectUlocLocaleName) != 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 ( U_FAILURE(status) ) { + log_data_err("uloc_getDisplayName for displayLocale %s and namedLocale %-10s returns unexpected status %s\n", itemPtr->displayLocale, itemPtr->namedLocale, myErrorName(status)); + } else if ( u_strcmp(getName, expectUlocLocaleName) != 0 ) { + char bbuf[128]; + u_strToUTF8(bbuf, 128, NULL, getName, ulen, &status); + log_data_err("uloc_getDisplayName for displayLocale %s and namedLocale %-10s returns unexpected name (len %d): \"%s\"\n", itemPtr->displayLocale, itemPtr->namedLocale, ulen, bbuf); + } + +#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) ) { + log_data_err("uldn_localeDisplayName for displayLocale %s and namedLocale %-10s returns unexpected status %s\n", itemPtr->displayLocale, itemPtr->namedLocale, myErrorName(status)); + } else if ( u_strcmp(getName, expectUldnLocaleName) != 0 ) { + char bbuf[128]; + u_strToUTF8(bbuf, 128, NULL, getName, ulen, &status); + log_data_err("uldn_localeDisplayName for displayLocale %s and namedLocale %-10s returns unexpected name (len %d): \"%s\"\n", itemPtr->displayLocale, itemPtr->namedLocale, ulen, bbuf); + } + + 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 */ + } +} + +/*------------------------------ + * 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; - int32_t count = 0; - UBool done = FALSE; + 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"); - while(!done) + /* 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++) { - if(*(str+count++) == 0) - { - done = TRUE; + 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); } - else - { - test = *(str+count-1); - 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); - } - } - count--; - expect = 450; + 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\n", 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"); - count=0; - done=FALSE; - while(!done) + skipped = 0; + expect = ures_getSize(subRes) - 1; /* Skip ZZ */ + for(count = 0; *(str1+count) != 0; count++) { - if(*(str1 + count++)==0) - { - done=TRUE; + 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); } - else - { - test = *(str1+count-1); - if(!strcmp(test,"FX")) - 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); + 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); } - count--; - expect=239; + + 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 < 23; i++) { + for (i = 0; i < LOCALE_INFO_SIZE; i++) { dataTable[i] = (UChar**)(calloc(sizeof(UChar*),LOCALE_SIZE)); - for (j = 0; j < 5; j++){ + for (j = 0; j < LOCALE_SIZE; j++){ dataTable[i][j] = CharsToUChars(rawData2[i][j]); } } @@ -796,21 +1335,28 @@ static void TestSimpleDisplayNames() names, and other stuff like that. This test just checks specific language and country codes to make sure we have the correct names for them. */ - char languageCodes[] [4] = { "he", "id", "iu", "ug", "yi", "za" }; - const char* languageNames [] = { "Hebrew", "Indonesian", "Inukitut", "Uighur", "Yiddish", - "Zhuang" }; + char languageCodes[] [4] = { "he", "id", "iu", "ug", "yi", "za", "419" }; + const char* languageNames [] = { "Hebrew", "Indonesian", "Inuktitut", "Uyghur", "Yiddish", + "Zhuang", "419" }; + const char* inLocale [] = { "en_US", "zh_Hant"}; UErrorCode status=U_ZERO_ERROR; int32_t i; - for (i = 0; i < 6; i++) { + int32_t localeIndex = 0; + for (i = 0; i < 7; i++) { UChar *testLang=0; UChar *expectedLang=0; int size=0; - size=uloc_getDisplayLanguage(languageCodes[i], "en_US", NULL, size, &status); + + if (i == 6) { + localeIndex = 1; /* Use the second locale for the rest of the test. */ + } + + size=uloc_getDisplayLanguage(languageCodes[i], inLocale[localeIndex], NULL, size, &status); if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; testLang=(UChar*)malloc(sizeof(UChar) * (size + 1)); - uloc_getDisplayLanguage(languageCodes[i], "en_US", testLang, size + 1, &status); + uloc_getDisplayLanguage(languageCodes[i], inLocale[localeIndex], testLang, size + 1, &status); } expectedLang=(UChar*)malloc(sizeof(UChar) * (strlen(languageNames[i])+1)); u_uastrcpy(expectedLang, languageNames[i]); @@ -830,13 +1376,13 @@ static void TestUninstalledISO3Names() { /* This test checks to make sure getISO3Language and getISO3Country work right even for locales that are not installed. */ - const char iso2Languages [][4] = { "am", "ba", "fy", "mr", "rn", + static const char iso2Languages [][4] = { "am", "ba", "fy", "mr", "rn", "ss", "tw", "zu" }; - const char iso3Languages [][5] = { "amh", "bak", "fry", "mar", "run", + static const char iso3Languages [][5] = { "amh", "bak", "fry", "mar", "run", "ssw", "twi", "zul" }; - char iso2Countries [][6] = { "am_AF", "ba_BW", "fy_KZ", "mr_MO", "rn_MN", + static const char iso2Countries [][6] = { "am_AF", "ba_BW", "fy_KZ", "mr_MO", "rn_MN", "ss_SB", "tw_TC", "zu_ZW" }; - char iso3Countries [][4] = { "AFG", "BWA", "KAZ", "MAC", "MNG", + static const char iso3Countries [][4] = { "AFG", "BWA", "KAZ", "MAC", "MNG", "SLB", "TCA", "ZWE" }; int32_t i; @@ -861,13 +1407,13 @@ static void TestUninstalledISO3Names() static void TestVariantParsing() { - const char* en_US_custom="en_US_De Anza_Cupertino_California_United States_Earth"; - const char* dispName="English (United States, DE ANZA_CUPERTINO_CALIFORNIA_UNITED STATES_EARTH)"; - const char* dispVar="DE ANZA_CUPERTINO_CALIFORNIA_UNITED STATES_EARTH"; - const char* shortVariant="fr_FR_foo"; - const char* bogusVariant="fr_FR__foo"; - const char* bogusVariant2="fr_FR_foo_"; - const char* bogusVariant3="fr_FR__foo_"; + static const char* en_US_custom="en_US_De Anza_Cupertino_California_United States_Earth"; + static const char* dispName="English (United States, DE ANZA_CUPERTINO_CALIFORNIA_UNITED STATES_EARTH)"; + static const char* dispVar="DE ANZA_CUPERTINO_CALIFORNIA_UNITED STATES_EARTH"; + static const char* shortVariant="fr_FR_foo"; + static const char* bogusVariant="fr_FR__foo"; + static const char* bogusVariant2="fr_FR_foo_"; + static const char* bogusVariant3="fr_FR__foo_"; UChar displayVar[100]; @@ -900,7 +1446,11 @@ static void TestVariantParsing() } u_uastrcpy(displayName, dispName); if(u_strcmp(got,displayName)!=0) { - log_err("FAIL: getDisplayName() Wanted %s, got %s\n", dispName, austrdup(got)); + if (status == U_USING_DEFAULT_WARNING) { + log_data_err("FAIL: getDisplayName() got %s. Perhaps you are missing data?\n", u_errorName(status)); + } else { + log_err("FAIL: getDisplayName() Wanted %s, got %s\n", dispName, austrdup(got)); + } } size=0; @@ -969,13 +1519,13 @@ static void TestObsoleteNames(void) UErrorCode status = U_ZERO_ERROR; char buff[256]; - struct + static const struct { char locale[9]; - char lang3[6]; - char lang[6]; - char ctry3[6]; - char ctry[6]; + char lang3[4]; + char lang[4]; + char ctry3[4]; + char ctry[4]; } tests[] = { { "eng_USA", "eng", "en", "USA", "US" }, @@ -983,6 +1533,7 @@ static void TestObsoleteNames(void) { "in", "ind", "in", "", "" }, { "id", "ind", "id", "", "" }, /* NO aliasing */ { "sh", "srp", "sh", "", "" }, + { "zz_CS", "", "zz", "SCG", "CS" }, { "zz_FX", "", "zz", "FXX", "FX" }, { "zz_RO", "", "zz", "ROU", "RO" }, { "zz_TP", "", "zz", "TMP", "TP" }, @@ -994,6 +1545,7 @@ static void TestObsoleteNames(void) { "zz_ZAR", "", "zz", "ZAR", "ZR" }, { "zz_TMP", "", "zz", "TMP", "TP" }, { "zz_TLS", "", "zz", "TLS", "TL" }, + { "zz_YUG", "", "zz", "YUG", "YU" }, { "mlt_PSE", "mlt", "mt", "PSE", "PS" }, { "iw", "heb", "iw", "", "" }, { "ji", "yid", "ji", "", "" }, @@ -1081,6 +1633,14 @@ static void TestObsoleteNames(void) } } + if (uloc_getLCID("iw_IL") != uloc_getLCID("he_IL")) { + log_err("he,iw LCID mismatch: %X versus %X\n", uloc_getLCID("iw_IL"), uloc_getLCID("he_IL")); + } + + if (uloc_getLCID("iw") != uloc_getLCID("he")) { + log_err("he,iw LCID mismatch: %X versus %X\n", uloc_getLCID("iw"), uloc_getLCID("he")); + } + #if 0 i = uloc_getLanguage("kok",NULL,0,&icu_err); @@ -1142,837 +1702,5587 @@ static void TestObsoleteNames(void) } -static void -TestKeyInRootRecursive(UResourceBundle *root, const char *rootName, - UResourceBundle *currentBundle, const char *locale) { - UErrorCode errorCode = U_ZERO_ERROR; - UResourceBundle *subRootBundle = NULL, *subBundle = NULL; - - ures_resetIterator(root); - ures_resetIterator(currentBundle); - while (ures_hasNext(currentBundle)) { - const char *subBundleKey = NULL; - const char *currentBundleKey = NULL; - - errorCode = U_ZERO_ERROR; - subBundle = ures_getNextResource(currentBundle, NULL, &errorCode); - if (U_FAILURE(errorCode)) { - log_err("Can't open a resource for locale %s\n", locale); - continue; +static void TestKeywordVariants(void) +{ + static const struct { + const char *localeID; + const char *expectedLocaleID; /* uloc_getName */ + const char *expectedLocaleIDNoKeywords; /* uloc_getBaseName */ + const char *expectedCanonicalID; /* uloc_canonicalize */ + const char *expectedKeywords[10]; + int32_t numKeywords; + UErrorCode expectedStatus; /* from uloc_openKeywords */ + } testCases[] = { + { + "de_DE@ currency = euro; C o ll A t i o n = Phonebook ; C alen dar = buddhist ", + "de_DE@calendar=buddhist;collation=Phonebook;currency=euro", + "de_DE", + "de_DE@calendar=buddhist;collation=Phonebook;currency=euro", + {"calendar", "collation", "currency"}, + 3, + U_ZERO_ERROR + }, + { + "de_DE@euro", + "de_DE@euro", + "de_DE@euro", /* we probably should strip off the POSIX style variant @euro see #11690 */ + "de_DE@currency=EUR", + {"","","","","","",""}, + 0, + U_INVALID_FORMAT_ERROR /* must have '=' after '@' */ + }, + { + "de_DE@euro;collation=phonebook", /* The POSIX style variant @euro cannot be combined with key=value? */ + "de_DE", /* getName returns de_DE - should be INVALID_FORMAT_ERROR? */ + "de_DE", /* getBaseName returns de_DE - should be INVALID_FORMAT_ERROR? see #11690 */ + "de_DE", /* canonicalize returns de_DE - should be INVALID_FORMAT_ERROR? */ + {"","","","","","",""}, + 0, + U_INVALID_FORMAT_ERROR + }, + { + "de_DE@collation=", + 0, /* expected getName to fail */ + "de_DE", /* getBaseName returns de_DE - should be INVALID_FORMAT_ERROR? see #11690 */ + 0, /* expected canonicalize to fail */ + {"","","","","","",""}, + 0, + U_INVALID_FORMAT_ERROR /* must have '=' after '@' */ } - subBundleKey = ures_getKey(subBundle); - currentBundleKey = ures_getKey(currentBundle); - - subRootBundle = ures_getByKey(root, subBundleKey, NULL, &errorCode); - if (U_FAILURE(errorCode)) { -/* if (ures_hasNext(root)) { - errorCode = U_ZERO_ERROR; - subRootBundle = ures_getNextResource(root, NULL, &errorCode); - } - if (errorCode!=U_ZERO_ERROR) { - if (ures_getKey(currentBundle) != 0 && strcmp(ures_getKey(currentBundle), "zoneStrings") == 0) { - break; - } - else {*/ - if (subBundleKey == NULL - || (strcmp(subBundleKey, "TransliterateLATIN") != 0 /* Ignore these special cases */ - && strcmp(subBundleKey, "BreakDictionaryData") != 0)) - { - UBool isRoot = strcmp(rootName, "root") == 0; - UBool isSpecial = FALSE; - if (currentBundleKey) { - isSpecial = strcmp(currentBundleKey, "Currencies") == 0 - || strcmp(currentBundleKey, "Languages") == 0 - || strcmp(currentBundleKey, "Countries") == 0; - } - - if ((isRoot && !isSpecial) - || (!isRoot && isSpecial)) - { - log_err("Can't open a resource with key \"%s\" in \"%s\" from %s for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - rootName, - locale); - } - } - ures_close(subBundle); - continue; -/* } - }*/ - } - if (ures_getType(subRootBundle) != ures_getType(subBundle)) { - log_err("key \"%s\" in \"%s\" has a different type from root for locale \"%s\"\n" - "\troot=%d, locale=%d\n", - subBundleKey, - ures_getKey(currentBundle), - locale, - ures_getType(subRootBundle), - ures_getType(subBundle)); - continue; + }; + UErrorCode status = U_ZERO_ERROR; + + int32_t i = 0, j = 0; + int32_t resultLen = 0; + char buffer[256]; + UEnumeration *keywords; + int32_t keyCount = 0; + const char *keyword = NULL; + int32_t keywordLen = 0; + + for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { + status = U_ZERO_ERROR; + *buffer = 0; + keywords = uloc_openKeywords(testCases[i].localeID, &status); + + if(status != testCases[i].expectedStatus) { + log_err("Expected to uloc_openKeywords(\"%s\") => status %s. Got %s instead\n", + testCases[i].localeID, + u_errorName(testCases[i].expectedStatus), u_errorName(status)); } - else if (ures_getType(subBundle) == URES_INT_VECTOR) { - int32_t minSize; - int32_t subBundleSize; - int32_t idx; - UBool sameArray = TRUE; - const int32_t *subRootBundleArr = ures_getIntVector(subRootBundle, &minSize, &errorCode); - const int32_t *subBundleArr = ures_getIntVector(subBundle, &subBundleSize, &errorCode); - - if (minSize > subBundleSize) { - minSize = subBundleSize; - log_err("Arrays are different size with key \"%s\" in \"%s\" from root for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); + 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); } - - for (idx = 0; idx < minSize && sameArray; idx++) { - if (subRootBundleArr[idx] != subBundleArr[idx]) { - sameArray = FALSE; + 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++; } - if (strcmp(subBundleKey, "DateTimeElements") == 0 - && (subBundleArr[idx] < 1 || 7 < subBundleArr[idx])) - { - log_err("Value out of range with key \"%s\" at index %d in \"%s\" for locale \"%s\"\n", - subBundleKey, - idx, - ures_getKey(currentBundle), - locale); + 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++; } } - /* Special exception es_US and DateTimeElements */ - if (sameArray - && !(strcmp(locale, "es_US") == 0 && strcmp(subBundleKey, "DateTimeElements") == 0)) - { - log_err("Integer vectors are the same with key \"%s\" in \"%s\" from root for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); - } + uenum_close(keywords); } - else if (ures_getType(subBundle) == URES_ARRAY) { - UResourceBundle *subSubBundle = ures_getByIndex(subBundle, 0, NULL, &errorCode); - UResourceBundle *subSubRootBundle = ures_getByIndex(subRootBundle, 0, NULL, &errorCode); - if (U_SUCCESS(errorCode) - && (ures_getType(subSubBundle) == URES_ARRAY || ures_getType(subSubRootBundle) == URES_ARRAY)) - { - /* TODO: Properly check for 2D arrays and zoneStrings */ - if (subBundleKey != NULL && strcmp(subBundleKey, "zoneStrings") == 0) { -/* int32_t minSize = ures_getSize(subBundle); - int32_t idx; - - for (idx = 0; idx < minSize; idx++) { - UResourceBundle *subSubBundleAtIndex = ures_getByIndex(subBundle, idx, NULL, &errorCode); - if (ures_getSize(subSubBundleAtIndex) != 6) { - log_err("zoneStrings at index %d has wrong size for locale \"%s\". array size=%d\n", - idx, - locale, - ures_getSize(subSubBundleAtIndex)); - } - ures_close(subSubBundleAtIndex); - }*/ - } - else { - /* Here is one of the recursive parts */ - TestKeyInRootRecursive(subRootBundle, rootName, subBundle, locale); - } + status = U_ZERO_ERROR; + resultLen = uloc_getName(testCases[i].localeID, buffer, 256, &status); + 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 { - int32_t minSize = ures_getSize(subRootBundle); - int32_t idx; - UBool sameArray = TRUE; - - if (minSize > ures_getSize(subBundle)) { - minSize = ures_getSize(subBundle); - } - - if ((subBundleKey == NULL - || (subBundleKey != NULL && strcmp(subBundleKey, "LocaleScript") != 0)) - && ures_getSize(subRootBundle) != ures_getSize(subBundle)) - { - log_err("Different size array with key \"%s\" in \"%s\" from root for locale \"%s\"\n" - "\troot array size=%d, locale array size=%d\n", - subBundleKey, - ures_getKey(currentBundle), - locale, - ures_getSize(subRootBundle), - ures_getSize(subBundle)); - } - - for (idx = 0; idx < minSize; idx++) { - int32_t rootStrLen, localeStrLen; - const UChar *rootStr = ures_getStringByIndex(subRootBundle,idx,&rootStrLen,&errorCode); - const UChar *localeStr = ures_getStringByIndex(subBundle,idx,&localeStrLen,&errorCode); - if (rootStr && localeStr && U_SUCCESS(errorCode)) { - if (u_strcmp(rootStr, localeStr) != 0) { - sameArray = FALSE; - } - } - else { - log_err("Got a NULL string with key \"%s\" in \"%s\" at index %d for root or locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - idx, - locale); - continue; - } - if (localeStr[0] == (UChar)0x20) { - log_err("key \"%s\" at index %d in \"%s\" starts with a space in locale \"%s\"\n", - subBundleKey, - idx, - ures_getKey(currentBundle), - locale); - } - else if (localeStr[localeStrLen - 1] == (UChar)0x20) { - log_err("key \"%s\" at index %d in \"%s\" ends with a space in locale \"%s\"\n", - subBundleKey, - idx, - ures_getKey(currentBundle), - locale); - } - else if (subBundleKey != NULL - && strcmp(subBundleKey, "DateTimePatterns") == 0) - { - int32_t quoted = 0; - const UChar *localeStrItr = localeStr; - while (*localeStrItr) { - if (*localeStrItr == (UChar)0x27 /* ' */) { - quoted++; - } - else if ((quoted % 2) == 0) { - /* Search for unquoted characters */ - if (4 <= idx && idx <= 7 - && (*localeStrItr == (UChar)0x6B /* k */ - || *localeStrItr == (UChar)0x48 /* H */ - || *localeStrItr == (UChar)0x6D /* m */ - || *localeStrItr == (UChar)0x73 /* s */ - || *localeStrItr == (UChar)0x53 /* S */ - || *localeStrItr == (UChar)0x61 /* a */ - || *localeStrItr == (UChar)0x68 /* h */ - || *localeStrItr == (UChar)0x7A /* z */)) - { - log_err("key \"%s\" at index %d has time pattern chars in date for locale \"%s\"\n", - subBundleKey, - idx, - locale); - } - else if (0 <= idx && idx <= 3 - && (*localeStrItr == (UChar)0x47 /* G */ - || *localeStrItr == (UChar)0x79 /* y */ - || *localeStrItr == (UChar)0x4D /* M */ - || *localeStrItr == (UChar)0x64 /* d */ - || *localeStrItr == (UChar)0x45 /* E */ - || *localeStrItr == (UChar)0x44 /* D */ - || *localeStrItr == (UChar)0x46 /* F */ - || *localeStrItr == (UChar)0x77 /* w */ - || *localeStrItr == (UChar)0x57 /* W */)) - { - log_err("key \"%s\" at index %d has date pattern chars in time for locale \"%s\"\n", - subBundleKey, - idx, - locale); - } - } - localeStrItr++; - } - } - else if (idx == 4 && subBundleKey != NULL - && strcmp(subBundleKey, "NumberElements") == 0 - && u_charDigitValue(localeStr[0]) != 0) - { - log_err("key \"%s\" at index %d has a non-zero based number for locale \"%s\"\n", - subBundleKey, - idx, - locale); - } - } - if (sameArray && strcmp(rootName, "root") == 0) { - log_err("Arrays are the same with key \"%s\" in \"%s\" from root for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); - } + } 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)); } - ures_close(subSubBundle); - ures_close(subSubRootBundle); - } - else if (ures_getType(subBundle) == URES_STRING) { - int32_t len = 0; - const UChar *string = ures_getString(subBundle, &len, &errorCode); - if (U_FAILURE(errorCode) || string == NULL) { - log_err("Can't open a string with key \"%s\" in \"%s\" for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); - } else if (string[0] == (UChar)0x20) { - log_err("key \"%s\" in \"%s\" starts with a space in locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); - } else if (string[len - 1] == (UChar)0x20) { - log_err("key \"%s\" in \"%s\" ends with a space in locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); - } else if (strcmp(subBundleKey, "localPatternChars") == 0 && len != 20) { - log_err("key \"%s\" has the wrong number of characters in locale \"%s\"\n", - subBundleKey, - locale); + } + + status = U_ZERO_ERROR; + resultLen = uloc_getBaseName(testCases[i].localeID, buffer, 256, &status); + 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); } - /* No fallback was done. Check for duplicate data */ - /* The ures_* API does not do fallback of sub-resource bundles, - So we can't do this now. */ - else if (strcmp(locale, "root") != 0 && errorCode == U_ZERO_ERROR) { - - const UChar *rootString = ures_getString(subRootBundle, &len, &errorCode); - if (U_FAILURE(errorCode) || rootString == NULL) { - log_err("Can't open a string with key \"%s\" in \"%s\" in root\n", - ures_getKey(subRootBundle), - ures_getKey(currentBundle)); - continue; - } else if (u_strcmp(string, rootString) == 0) { - if (strcmp(locale, "de_CH") != 0 && strcmp(subBundleKey, "Countries") != 0) { - log_err("Found duplicate data with key \"%s\" in \"%s\" in locale \"%s\"\n", - ures_getKey(subRootBundle), - ures_getKey(currentBundle), - locale); - } - else { - /* Ignore for now. */ - /* Can be fixed if fallback through de locale was done. */ - log_verbose("Skipping key %s in %s\n", subBundleKey, locale); - } - } + } 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)); } } - else if (ures_getType(subBundle) == URES_TABLE) { - /* Here is one of the recursive parts */ - TestKeyInRootRecursive(subRootBundle, rootName, subBundle, locale); - } - else if (ures_getType(subBundle) == URES_BINARY || ures_getType(subBundle) == URES_INT) { - /* Can't do anything to check it */ - /* We'll assume it's all correct */ - if (strcmp(subBundleKey, "LocaleID") != 0) { - log_verbose("Skipping key \"%s\" in \"%s\" for locale \"%s\"\n", - subBundleKey, - ures_getKey(currentBundle), - locale); + + status = U_ZERO_ERROR; + resultLen = uloc_canonicalize(testCases[i].localeID, buffer, 256, &status); + 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)); } - /* Testing for LocaleID is done in testLCID */ - } - else { - log_err("Type %d for key \"%s\" in \"%s\" is unknown for locale \"%s\"\n", - ures_getType(subBundle), - subBundleKey, - ures_getKey(currentBundle), - locale); } - ures_close(subRootBundle); - ures_close(subBundle); } } - -#ifdef WIN32 - -static void -testLCID(UResourceBundle *currentBundle, - const char *localeName) +static void TestKeywordVariantParsing(void) { + static const struct { + const char *localeID; + const char *keyword; + const char *expectedValue; + } testCases[] = { + { "de_DE@ C o ll A t i o n = Phonebook ", "c o ll a t i o n", "Phonebook" }, + { "de_DE", "collation", ""}, + { "de_DE@collation=PHONEBOOK", "collation", "PHONEBOOK" }, + { "de_DE@currency = euro; CoLLaTion = PHONEBOOk", "collatiON", "PHONEBOOk" }, + }; + UErrorCode status = U_ZERO_ERROR; - uint32_t lcid; - uint32_t expectedLCID; - char lcidStringC[64] = {0}; - int32_t lcidStringLen = 0; - const UChar *lcidString = NULL; - UResourceBundle *localeID = ures_getByKey(currentBundle, "LocaleID", NULL, &status); - - expectedLCID = ures_getInt(localeID, &status); - ures_close(localeID); - - if (U_FAILURE(status)) { - log_err("ERROR: %s does not have a LocaleID (%s)\n", - localeName, u_errorName(status)); - return; + + int32_t i = 0; + int32_t resultLen = 0; + char buffer[256]; + + for(i = 0; i < UPRV_LENGTHOF(testCases); i++) { + *buffer = 0; + resultLen = uloc_getKeywordValue(testCases[i].localeID, testCases[i].keyword, buffer, 256, &status); + (void)resultLen; /* Suppress set but not used warning. */ + if(uprv_strcmp(testCases[i].expectedValue, buffer) != 0) { + log_err("Expected to extract \"%s\" from \"%s\" for keyword \"%s\". Got \"%s\" instead\n", + testCases[i].expectedValue, testCases[i].localeID, testCases[i].keyword, buffer); + } } +} - lcid = uprv_convertToLCID(localeName, &status); - if (U_FAILURE(status)) { - if (expectedLCID == 0) { - log_verbose("INFO: %-5s does not have any LCID mapping\n", - localeName); +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" } +}; + + +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); + + 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); } - else { - log_err("ERROR: %-5s does not have an LCID mapping to 0x%.4X\n", - localeName, expectedLCID); + /* 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); + } + + resultLen = uloc_setKeywordValue(kwSetTestCases[i].k, kwSetTestCases[i].v, buffer, 1023, &status); + if(U_FAILURE(status)) { + log_err("Err on test case %d: got error %s\n", i, u_errorName(status)); + continue; + } + if(strcmp(buffer,kwSetTestCases[i].x) || ((int32_t)strlen(buffer)!=resultLen)) { + log_err("FAIL: #%d: %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); } - return; } +} + +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; - uprv_strcpy(lcidStringC, uprv_convertToPosix(expectedLCID, &status)); - if (U_FAILURE(status)) { - log_err("ERROR: %.4x does not have a POSIX mapping due to %s\n", - expectedLCID, u_errorName(status)); + 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"); - if(lcid != expectedLCID) { - log_err("ERROR: %-5s wrongfully has 0x%.4x instead of 0x%.4x for LCID\n", - localeName, expectedLCID, lcid); + 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); } - if(strcmp(localeName, lcidStringC) != 0) { - char langName[1024]; - char langLCID[1024]; - uloc_getLanguage(localeName, langName, sizeof(langName), &status); - uloc_getLanguage(lcidStringC, langLCID, sizeof(langLCID), &status); - if (expectedLCID == lcid && strcmp(langName, langLCID) == 0) { - log_verbose("WARNING: %-5s resolves to %s (0x%.4x)\n", - localeName, lcidStringC, lcid); + 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; } - else if (expectedLCID == lcid) { - log_err("ERROR: %-5s has 0x%.4x and the number resolves wrongfully to %s\n", - localeName, expectedLCID, lcidStringC); + if(res!=(int32_t)strlen(kwSetTestCases[i].x)) { + log_err("expected result %d got %d\n", strlen(kwSetTestCases[i].x), res); + return; } - else { - log_err("ERROR: %-5s has 0x%.4x and the number resolves wrongfully to %s. It should be 0x%x.\n", - localeName, expectedLCID, lcidStringC, lcid); + 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); } } -#endif +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 -TestLocaleStructure(void) { - UResourceBundle *root, *completeLoc, *currentLocale, *subtable, *completeSubtable; - int32_t locCount = uloc_countAvailable(); - int32_t locIndex; - UErrorCode errorCode = U_ZERO_ERROR; - const char *currLoc; +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_PREEURO-with-extra-stuff-that really doesn't make any sense-unless-you're trying to increase code coverage", + "ca_ES_PREEURO_WITH_EXTRA_STUFF_THAT REALLY DOESN'T MAKE ANY SENSE_UNLESS_YOU'RE TRYING TO INCREASE CODE COVERAGE", + "ca_ES_PREEURO_WITH_EXTRA_STUFF_THAT REALLY DOESN'T MAKE ANY SENSE_UNLESS_YOU'RE TRYING TO INCREASE CODE COVERAGE"}, + { "ca_ES_PREEURO", "ca_ES_PREEURO", "ca_ES@currency=ESP" }, + { "de_AT_PREEURO", "de_AT_PREEURO", "de_AT@currency=ATS" }, + { "de_DE_PREEURO", "de_DE_PREEURO", "de_DE@currency=DEM" }, + { "de_LU_PREEURO", "de_LU_PREEURO", "de_LU@currency=LUF" }, + { "el_GR_PREEURO", "el_GR_PREEURO", "el_GR@currency=GRD" }, + { "en_BE_PREEURO", "en_BE_PREEURO", "en_BE@currency=BEF" }, + { "en_IE_PREEURO", "en_IE_PREEURO", "en_IE@currency=IEP" }, + { "es_ES_PREEURO", "es_ES_PREEURO", "es_ES@currency=ESP" }, + { "eu_ES_PREEURO", "eu_ES_PREEURO", "eu_ES@currency=ESP" }, + { "fi_FI_PREEURO", "fi_FI_PREEURO", "fi_FI@currency=FIM" }, + { "fr_BE_PREEURO", "fr_BE_PREEURO", "fr_BE@currency=BEF" }, + { "fr_FR_PREEURO", "fr_FR_PREEURO", "fr_FR@currency=FRF" }, + { "fr_LU_PREEURO", "fr_LU_PREEURO", "fr_LU@currency=LUF" }, + { "ga_IE_PREEURO", "ga_IE_PREEURO", "ga_IE@currency=IEP" }, + { "gl_ES_PREEURO", "gl_ES_PREEURO", "gl_ES@currency=ESP" }, + { "it_IT_PREEURO", "it_IT_PREEURO", "it_IT@currency=ITL" }, + { "nl_BE_PREEURO", "nl_BE_PREEURO", "nl_BE@currency=BEF" }, + { "nl_NL_PREEURO", "nl_NL_PREEURO", "nl_NL@currency=NLG" }, + { "pt_PT_PREEURO", "pt_PT_PREEURO", "pt_PT@currency=PTE" }, + { "de__PHONEBOOK", "de__PHONEBOOK", "de@collation=phonebook" }, + { "en_GB_EURO", "en_GB_EURO", "en_GB@currency=EUR" }, + { "en_GB@EURO", "en_GB@EURO", "en_GB@currency=EUR" }, /* POSIX ID */ + { "es__TRADITIONAL", "es__TRADITIONAL", "es@collation=traditional" }, + { "hi__DIRECT", "hi__DIRECT", "hi@collation=direct" }, + { "ja_JP_TRADITIONAL", "ja_JP_TRADITIONAL", "ja_JP@calendar=japanese" }, + { "th_TH_TRADITIONAL", "th_TH_TRADITIONAL", "th_TH@calendar=buddhist" }, + { "zh_TW_STROKE", "zh_TW_STROKE", "zh_TW@collation=stroke" }, + { "zh__PINYIN", "zh__PINYIN", "zh@collation=pinyin" }, + { "zh@collation=pinyin", "zh@collation=pinyin", "zh@collation=pinyin" }, + { "zh_CN@collation=pinyin", "zh_CN@collation=pinyin", "zh_CN@collation=pinyin" }, + { "zh_CN_STROKE", "zh_CN_STROKE", "zh_CN@collation=stroke" }, + { "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@currency=EUR" }, /* 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 */ + { "sr-SP-Cyrl", "sr_SP_CYRL", "sr_Cyrl_RS" }, /* .NET name */ + { "sr-SP-Latn", "sr_SP_LATN", "sr_Latn_RS" }, /* .NET name */ + { "sr_YU_CYRILLIC", "sr_YU_CYRILLIC", "sr_Cyrl_RS" }, /* Linux name */ + { "uz-UZ-Cyrl", "uz_UZ_CYRL", "uz_Cyrl_UZ" }, /* .NET name */ + { "uz-UZ-Latn", "uz_UZ_LATN", "uz_Latn_UZ" }, /* .NET name */ + { "zh-CHS", "zh_CHS", "zh_Hans" }, /* .NET name */ + { "zh-CHT", "zh_CHT", "zh_Hant" }, /* .NET name This may change back to zh_Hant */ + + /* 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" }, + /* PRE_EURO and EURO conversions don't affect other keywords */ + { "es_ES_PREEURO@CALendar=Japanese", "es_ES_PREEURO@calendar=Japanese", "es_ES@calendar=Japanese;currency=ESP" }, + { "es_ES_EURO@SHOUT=zipeedeedoodah", "es_ES_EURO@shout=zipeedeedoodah", "es_ES@currency=EUR;shout=zipeedeedoodah" }, + /* currency keyword overrides PRE_EURO and EURO currency */ + { "es_ES_PREEURO@currency=EUR", "es_ES_PREEURO@currency=EUR", "es_ES@currency=EUR" }, + { "es_ES_EURO@currency=ESP", "es_ES_EURO@currency=ESP", "es_ES@currency=ESP" }, + /* 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" } + }; + + static const char* label[] = { "getName", "canonicalize" }; - /* TODO: Compare against parent's data too. This code can't handle fallbacks that some tools do already. */ -/* char locName[ULOC_FULLNAME_CAPACITY]; - char *locNamePtr; + 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(); + } - for (locIndex = 0; locIndex < locCount; locIndex++) { - errorCode=U_ZERO_ERROR; - strcpy(locName, uloc_getAvailable(locIndex)); - locNamePtr = strrchr(locName, '_'); - if (locNamePtr) { - *locNamePtr = 0; - } - else { - strcpy(locName, "root"); + /* 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 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)); + 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); + } + break; + } + }else{ + log_err("uloc_getDisplayKeyword did not return the expected error. Error: %s\n", u_errorName(status)); + } + + free(displayKeyword); - root = ures_openDirect(NULL, locName, &errorCode); - if(U_FAILURE(errorCode)) { - log_err("Can't open %s\n", locName); - continue; } -*/ - if (locCount <= 1) { - log_data_err("At least root needs to be installed\n"); + uenum_close(keywordEnum); } +} - root = ures_openDirect(NULL, "root", &errorCode); - if(U_FAILURE(errorCode)) { - log_data_err("Can't open root\n"); - return; - } - completeLoc = ures_openDirect(NULL, "en", &errorCode); - if(U_FAILURE(errorCode)) { - log_data_err("Can't open en\n"); - return; +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)); + 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)); + } + break; + } + }else{ + log_err("uloc_getDisplayKeywordValue did not return the expected error. Error: %s\n", u_errorName(status)); + } + free(displayKeywordValue); + } + uenum_close(keywordEnum); } - for (locIndex = 0; locIndex < locCount; locIndex++) { - errorCode=U_ZERO_ERROR; - currLoc = uloc_getAvailable(locIndex); - currentLocale = ures_open(NULL, currLoc, &errorCode); - if(errorCode != U_ZERO_ERROR) { - if(U_SUCCESS(errorCode)) { - /* It's installed, but there is no data. - It's installed for the g18n white paper [grhoten] */ - log_err("ERROR: Locale %-5s not installed, and it should be!\n", - uloc_getAvailable(locIndex)); - } else { - log_err("%%%%%%% Unexpected error %d in %s %%%%%%%", - u_errorName(errorCode), - uloc_getAvailable(locIndex)); - } - ures_close(currentLocale); - continue; + { + /* 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)); + 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); + } + break; + } + }else{ + log_err("uloc_getDisplayKeywordValue did not return the expected error. Error: %s\n", u_errorName(status)); + } + free(displayKeywordValue); } - ures_getStringByKey(currentLocale, "Version", NULL, &errorCode); - if(errorCode != U_ZERO_ERROR) { - log_err("No version information is available for locale %s, and it should be!\n", - currLoc); + 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); } - else if (ures_getStringByKey(currentLocale, "Version", NULL, &errorCode)[0] == (UChar)(0x78)) { - log_verbose("WARNING: The locale %s is experimental! It shouldn't be listed as an installed locale.\n", - currLoc); + } +} + + +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; } - TestKeyInRootRecursive(root, "root", currentLocale, currLoc); + } +} - completeSubtable = ures_getByKey(completeLoc, "Currencies", NULL, &errorCode); - subtable = ures_getByKey(currentLocale, "Currencies", NULL, &errorCode); - TestKeyInRootRecursive(completeSubtable, "en", subtable, currLoc); +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; -#ifdef WIN32 - testLCID(currentLocale, currLoc); -#endif + len = uloc_getDisplayName(localeId, localeId, buffer, 128, &status); + if (len > 128) { + log_err("buffer too small"); + return; + } - ures_close(completeSubtable); - ures_close(subtable); - ures_close(currentLocale); + for (i = 0; i < len; ++i) { + if (buffer[i] == 0) { + log_err("name contained null"); + return; } + } +} - ures_close(root); - ures_close(completeLoc); +/* 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)); + } } -static void -compareArrays(const char *keyName, - UResourceBundle *fromArray, const char *fromLocale, - UResourceBundle *toArray, const char *toLocale, - int32_t start, int32_t end) -{ - int32_t fromSize = ures_getSize(fromArray); - int32_t toSize = ures_getSize(fromArray); - int32_t idx; - UErrorCode errorCode = U_ZERO_ERROR; - if (fromSize > toSize) { - fromSize = toSize; - log_err("Arrays are different size from \"%s\" to \"%s\"\n", - fromLocale, - toLocale); +/** + * 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" */ +} - for (idx = start; idx <= end; idx++) { - const UChar *fromBundleStr = ures_getStringByIndex(fromArray, idx, NULL, &errorCode); - const UChar *toBundleStr = ures_getStringByIndex(toArray, idx, NULL, &errorCode); - if (fromBundleStr && toBundleStr && u_strcmp(fromBundleStr, toBundleStr) != 0) - { - log_err("Difference for %s at index %d from %s= \"%s\" to %s= \"%s\"\n", - keyName, - idx, - fromLocale, - austrdup(fromBundleStr), - toLocale, - austrdup(toBundleStr)); - } +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 -compareConsistentCountryInfo(const char *fromLocale, const char *toLocale) { - UErrorCode errorCode = U_ZERO_ERROR; - UResourceBundle *fromDateTimeElements, *toDateTimeElements; - UResourceBundle *fromArray, *toArray; - UResourceBundle *fromLocaleBund = ures_open(NULL, fromLocale, &errorCode); - UResourceBundle *toLocaleBund = ures_open(NULL, toLocale, &errorCode); +static void TestGetLocale(void) { + UErrorCode ec = U_ZERO_ERROR; + UParseError pe; + UChar EMPTY[1] = {0}; - if(U_FAILURE(errorCode)) { - log_err("Can't open resource bundle %s or %s - %s\n", fromLocale, toLocale, u_errorName(errorCode)); - return; + /* === 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 - fromDateTimeElements = ures_getByKey(fromLocaleBund, "DateTimeElements", NULL, &errorCode); - toDateTimeElements = ures_getByKey(toLocaleBund, "DateTimeElements", NULL, &errorCode); - if (strcmp(fromLocale, "ar_IN") != 0) + /* === ucal === */ +#if !UCONFIG_NO_FORMATTING { - int32_t fromSize; - int32_t toSize; - int32_t idx; - const int32_t *fromBundleArr = ures_getIntVector(fromDateTimeElements, &fromSize, &errorCode); - const int32_t *toBundleArr = ures_getIntVector(toDateTimeElements, &toSize, &errorCode); - - if (fromSize > toSize) { - fromSize = toSize; - log_err("Arrays are different size with key \"DateTimeElements\" from \"%s\" to \"%s\"\n", - fromLocale, - toLocale); - } - - for (idx = 0; idx < fromSize; idx++) { - if (fromBundleArr[idx] != toBundleArr[idx]) { - log_err("Difference with key \"DateTimeElements\" at index %d from \"%s\" to \"%s\"\n", - idx, - fromLocale, - toLocale); - } + 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); } - ures_close(fromDateTimeElements); - ures_close(toDateTimeElements); +#endif - fromArray = ures_getByKey(fromLocaleBund, "CurrencyElements", NULL, &errorCode); - toArray = ures_getByKey(toLocaleBund, "CurrencyElements", NULL, &errorCode); - if (strcmp(fromLocale, "en_CA") != 0) + /* === unum === */ +#if !UCONFIG_NO_FORMATTING { - /* The first one is probably localized. */ - compareArrays("CurrencyElements", fromArray, fromLocale, toArray, toLocale, 1, 2); + 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); } - ures_close(fromArray); - ures_close(toArray); +#endif - fromArray = ures_getByKey(fromLocaleBund, "NumberPatterns", NULL, &errorCode); - toArray = ures_getByKey(toLocaleBund, "NumberPatterns", NULL, &errorCode); - if (strcmp(fromLocale, "en_CA") != 0) + /* === umsg === */ +#if 0 + /* commented out by weiv 01/12/2005. umsg_getLocaleByType is to be removed */ +#if !UCONFIG_NO_FORMATTING { - compareArrays("NumberPatterns", fromArray, fromLocale, toArray, toLocale, 0, 3); + 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); } - ures_close(fromArray); - ures_close(toArray); +#endif +#endif - /* Difficult to test properly */ -/* - fromArray = ures_getByKey(fromLocaleBund, "DateTimePatterns", NULL, &errorCode); - toArray = ures_getByKey(toLocaleBund, "DateTimePatterns", NULL, &errorCode); + /* === ubrk === */ +#if !UCONFIG_NO_BREAK_ITERATION { - compareArrays("DateTimePatterns", fromArray, fromLocale, toArray, toLocale); + 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); } - ures_close(fromArray); - ures_close(toArray);*/ +#endif - fromArray = ures_getByKey(fromLocaleBund, "NumberElements", NULL, &errorCode); - toArray = ures_getByKey(toLocaleBund, "NumberElements", NULL, &errorCode); - if (strcmp(fromLocale, "en_CA") != 0) + /* === ucol === */ +#if !UCONFIG_NO_COLLATION { - compareArrays("NumberElements", fromArray, fromLocale, toArray, toLocale, 0, 3); - /* Index 4 is a script based 0 */ - compareArrays("NumberElements", fromArray, fromLocale, toArray, toLocale, 5, 10); + 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); + } } - ures_close(fromArray); - ures_close(toArray); - ures_close(fromLocaleBund); - ures_close(toLocaleBund); + uset_close(exSet); + ulocdata_close(uld); } -static void -TestConsistentCountryInfo(void) { -/* UResourceBundle *fromLocale, *toLocale;*/ - int32_t locCount = uloc_countAvailable(); - int32_t fromLocIndex, toLocIndex; +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); +} - int32_t fromCountryLen, toCountryLen; - char fromCountry[ULOC_FULLNAME_CAPACITY], toCountry[ULOC_FULLNAME_CAPACITY]; +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)); + } +} - int32_t fromVariantLen, toVariantLen; - char fromVariant[ULOC_FULLNAME_CAPACITY], toVariant[ULOC_FULLNAME_CAPACITY]; +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 */ +}; - UErrorCode errorCode = U_ZERO_ERROR; +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); + } + } +} - for (fromLocIndex = 0; fromLocIndex < locCount; fromLocIndex++) { - const char *fromLocale = uloc_getAvailable(fromLocIndex); +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); +} - errorCode=U_ZERO_ERROR; - fromCountryLen = uloc_getCountry(fromLocale, fromCountry, ULOC_FULLNAME_CAPACITY, &errorCode); - if (fromCountryLen <= 0) { - /* Ignore countryless locales */ - continue; +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 */ + } tests[] = { + /*0*/{ 0, NULL, "mt_MT", ULOC_ACCEPT_VALID }, + /*1*/{ 1, NULL, "en", ULOC_ACCEPT_VALID }, + /*2*/{ 2, NULL, "en", ULOC_ACCEPT_FALLBACK }, + /*3*/{ 3, NULL, "", ULOC_ACCEPT_FAILED }, + /*4*/{ 4, NULL, "es", ULOC_ACCEPT_VALID }, + + /*5*/{ 5, NULL, "en", ULOC_ACCEPT_VALID }, /* XF */ + /*6*/{ 6, NULL, "ja", ULOC_ACCEPT_FALLBACK }, /* XF */ + /*7*/{ 7, NULL, "zh", ULOC_ACCEPT_FALLBACK }, /* XF */ + }; + 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=.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, tlh", + /*4*/ "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, " + "xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xx-yy;q=.1, " + "es", + + /*5*/ "zh-xx;q=0.9, en;q=0.6", + /*6*/ "ja-JA", + /*7*/ "zh-xx;q=0.9", + }; + + 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)); } - fromVariantLen = uloc_getVariant(fromLocale, fromVariant, ULOC_FULLNAME_CAPACITY, &errorCode); - if (fromVariantLen > 0) { - /* Most variants are ignorable like PREEURO, or collation variants. */ + } +} + +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; i 0) { - /* Most variants are ignorable like PREEURO, or collation variants. */ - /* They're a variant for a reason. */ - continue; - } - if (strcmp(fromCountry, toCountry) == 0) { - log_verbose("comparing fromLocale=%s toLocale=%s\n", - fromLocale, toLocale); - compareConsistentCountryInfo(fromLocale, toLocale); - } +static void TestDateFormat() { +#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; i 0x39))) { - return strIdx; - } + status = U_ZERO_ERROR; + if(!isLocaleAvailable(resIndex, newLoc)){ + continue; } + if(U_FAILURE(status)){ + log_err("Creation of collators failed %s\n", u_errorName(status)); + return; + } + c1 = ucol_open(oldLoc, &status); + c2 = ucol_open(newLoc, &status); + l1 = ucol_getLocaleByType(c1, ULOC_VALID_LOCALE, &status); + l2 = ucol_getLocaleByType(c2, ULOC_VALID_LOCALE, &status); + if(U_FAILURE(status)){ + log_err("Fetching the locale names failed failed %s\n", u_errorName(status)); + } + if (strcmp(newLoc,l1)!=0 || strcmp(l1,l2)!=0) { + log_err("The locales are not equal!.Old: %s, New: %s \n", oldLoc, newLoc); + } + log_verbose("ucol_getLocaleByType old:%s new:%s\n", l1, l2); + ucol_close(c1); + ucol_close(c2); } - uset_close(exemplarSet); - return -1; + ures_close(resIndex); +#endif } -static void VerifyTranslation(void) { - UResourceBundle *root, *currentLocale; - int32_t locCount = uloc_countAvailable(); - int32_t locIndex; - UErrorCode errorCode = U_ZERO_ERROR; - int32_t exemplarLen; - const UChar *exemplarCharacters; - const char *currLoc; - UScriptCode scripts[USCRIPT_CODE_LIMIT]; - int32_t numScripts; - int32_t idx; - int32_t end; - UResourceBundle *resArray; +typedef struct OrientationStructTag { + const char* localeId; + ULayoutType character; + ULayoutType line; +} OrientationStruct; + +static const char* ULayoutTypeToString(ULayoutType type) +{ + switch(type) + { + case ULOC_LAYOUT_LTR: + return "ULOC_LAYOUT_LTR"; + break; + case ULOC_LAYOUT_RTL: + return "ULOC_LAYOUT_RTL"; + break; + case ULOC_LAYOUT_TTB: + return "ULOC_LAYOUT_TTB"; + break; + case ULOC_LAYOUT_BTT: + return "ULOC_LAYOUT_BTT"; + break; + case ULOC_LAYOUT_UNKNOWN: + break; + } + + return "Unknown enum value for ULayoutType!"; +} + +static void TestOrientation() +{ + static const OrientationStruct toTest [] = { + { "ar", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "aR", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "ar_Arab", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "fa", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "Fa", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "he", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "ps", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "ur", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "UR", ULOC_LAYOUT_RTL, ULOC_LAYOUT_TTB }, + { "en", ULOC_LAYOUT_LTR, ULOC_LAYOUT_TTB } + }; - if (locCount <= 1) { - log_data_err("At least root needs to be installed\n"); + size_t i = 0; + for (; i < UPRV_LENGTHOF(toTest); ++i) { + UErrorCode statusCO = U_ZERO_ERROR; + UErrorCode statusLO = U_ZERO_ERROR; + const char* const localeId = toTest[i].localeId; + const ULayoutType co = uloc_getCharacterOrientation(localeId, &statusCO); + const ULayoutType expectedCO = toTest[i].character; + const ULayoutType lo = uloc_getLineOrientation(localeId, &statusLO); + const ULayoutType expectedLO = toTest[i].line; + if (U_FAILURE(statusCO)) { + log_err_status(statusCO, + " unexpected failure for uloc_getCharacterOrientation(), with localId \"%s\" and status %s\n", + localeId, + u_errorName(statusCO)); + } + else if (co != expectedCO) { + log_err( + " unexpected result for uloc_getCharacterOrientation(), with localeId \"%s\". Expected %s but got result %s\n", + localeId, + ULayoutTypeToString(expectedCO), + ULayoutTypeToString(co)); + } + if (U_FAILURE(statusLO)) { + log_err_status(statusLO, + " unexpected failure for uloc_getLineOrientation(), with localId \"%s\" and status %s\n", + localeId, + u_errorName(statusLO)); + } + else if (lo != expectedLO) { + log_err( + " unexpected result for uloc_getLineOrientation(), with localeId \"%s\". Expected %s but got result %s\n", + localeId, + ULayoutTypeToString(expectedLO), + ULayoutTypeToString(lo)); + } } +} - root = ures_openDirect(NULL, "root", &errorCode); - if(U_FAILURE(errorCode)) { - log_data_err("Can't open root\n"); +static void TestULocale() { + 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 (locIndex = 0; locIndex < locCount; locIndex++) { - errorCode=U_ZERO_ERROR; - currLoc = uloc_getAvailable(locIndex); - currentLocale = ures_open(NULL, currLoc, &errorCode); - if(errorCode != U_ZERO_ERROR) { - if(U_SUCCESS(errorCode)) { - /* It's installed, but there is no data. - It's installed for the g18n white paper [grhoten] */ - log_err("ERROR: Locale %-5s not installed, and it should be!\n", - uloc_getAvailable(locIndex)); - } else { - log_err("%%%%%%% Unexpected error %d in %s %%%%%%%", - u_errorName(errorCode), - uloc_getAvailable(locIndex)); - } - ures_close(currentLocale); + for (i=0; i 2048) { - log_verbose("skipping test for %s\n", currLoc); + uloc_getDisplayName(oldLoc, ULOC_US, name1, capacity, &status); + if(U_FAILURE(status)){ + log_err("uloc_getDisplayName(%s) failed %s\n", oldLoc, u_errorName(status)); } - else { - UChar langBuffer[128]; - int32_t langSize; - int32_t strIdx; - langSize = uloc_getDisplayLanguage(currLoc, currLoc, langBuffer, sizeof(langBuffer)/sizeof(langBuffer[0]), &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error uloc_getDisplayLanguage returned %s\n", u_errorName(errorCode)); - } - else { - strIdx = findStringSetMismatch(langBuffer, langSize, exemplarCharacters, exemplarLen, FALSE); - if (strIdx >= 0) { - log_err("getDisplayLanguage(%s) at index %d returned characters not in the exemplar characters.\n", - currLoc, strIdx); - } - } - langSize = uloc_getDisplayCountry(currLoc, currLoc, langBuffer, sizeof(langBuffer)/sizeof(langBuffer[0]), &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error uloc_getDisplayCountry returned %s\n", u_errorName(errorCode)); - } - else { - strIdx = findStringSetMismatch(langBuffer, langSize, exemplarCharacters, exemplarLen, FALSE); - if (strIdx >= 0) { - log_err("getDisplayCountry(%s) at index %d returned characters not in the exemplar characters.\n", - currLoc, strIdx); - } - } - - resArray = ures_getByKey(currentLocale, "DayNames", NULL, &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error ures_getByKey returned %s\n", u_errorName(errorCode)); - } - if (QUICK) { - end = 1; - } - else { - end = ures_getSize(resArray); - } + uloc_getDisplayName(newLoc, ULOC_US, name2, capacity, &status); + if(U_FAILURE(status)){ + log_err("uloc_getDisplayName(%s) failed %s\n", newLoc, u_errorName(status)); + } - for (idx = 0; idx < end; idx++) { - const UChar *fromBundleStr = ures_getStringByIndex(resArray, idx, &langSize, &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error ures_getStringByIndex(%d) returned %s\n", idx, u_errorName(errorCode)); - continue; - } - strIdx = findStringSetMismatch(fromBundleStr, langSize, exemplarCharacters, exemplarLen, TRUE); - if (strIdx >= 0) { - log_err("getDayNames(%s, %d) at index %d returned characters not in the exemplar characters.\n", - currLoc, idx, strIdx); - } - } - ures_close(resArray); + if (u_strcmp(name1, name2)!=0) { + log_err("The locales are not equal!.Old: %s, New: %s \n", oldLoc, newLoc); + } + u_austrcpy(names1, name1); + u_austrcpy(names2, name2); + log_verbose("uloc_getDisplayName old:%s new:%s\n", names1, names2); + } + ures_close(resIndex); - resArray = ures_getByKey(currentLocale, "MonthNames", NULL, &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error ures_getByKey returned %s\n", u_errorName(errorCode)); - } - if (QUICK) { - end = 1; - } - else { - end = ures_getSize(resArray); - } +} - for (idx = 0; idx < end; idx++) { - const UChar *fromBundleStr = ures_getStringByIndex(resArray, idx, &langSize, &errorCode); - if (U_FAILURE(errorCode)) { - log_err("error ures_getStringByIndex(%d) returned %s\n", idx, u_errorName(errorCode)); - continue; - } - strIdx = findStringSetMismatch(fromBundleStr, langSize, exemplarCharacters, exemplarLen, TRUE); - if (strIdx >= 0) { - log_err("getMonthNames(%s, %d) at index %d returned characters not in the exemplar characters.\n", - currLoc, idx, strIdx); - } - } - ures_close(resArray); +static void TestUResourceBundle() { + const char* us1; + const char* us2; - errorCode = U_ZERO_ERROR; - numScripts = uscript_getCode(currLoc, scripts, sizeof(scripts)/sizeof(scripts[0]), &errorCode); - if (numScripts == 0) { - log_err("uscript_getCode(%s) doesn't work.\n", currLoc); - } - /* TODO: test that the scripts are a superset of exemplar characters. */ - } - ures_close(currentLocale); + UResourceBundle* rb1 = NULL; + UResourceBundle* rb2 = NULL; + UErrorCode status = U_ZERO_ERROR; + int i; + UResourceBundle *resIndex = NULL; + if(U_FAILURE(status)){ + log_err("Could not open res_index.res. Exiting. Error: %s\n", u_errorName(status)); + return; } + resIndex = ures_open(NULL,"res_index", &status); + for (i=0; iuerror == U_BUFFER_OVERFLOW_ERROR || + data->uerror == U_STRING_NOT_TERMINATED_WARNING) + { + return 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 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}, + {"az_AZ_CYRL", "az-Cyrl-AZ", "az-Cyrl-AZ"}, + {"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"}, + {"ja__9876_5432", "ja-9876-5432", "ja-9876-5432"}, + {"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", "x-elmer", "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"}, + {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); + } + } + } +} + +#define FULL_LENGTH -1 +static const struct { + const char *bcpID; + const char *locID; + int32_t len; +} langtag_to_locale[] = { + {"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}, + {"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}, + {"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", "__VARIANT_1234", FULL_LENGTH}, + {"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}, + {NULL, NULL, 0} +}; + +static void TestForLanguageTag(void) { + char locale[256]; + int32_t i; + UErrorCode status; + int32_t parsedLen; + int32_t expParsedLen; + + for (i = 0; langtag_to_locale[i].bcpID != NULL; i++) { + status = U_ZERO_ERROR; + locale[0] = 0; + expParsedLen = langtag_to_locale[i].len; + if (expParsedLen == FULL_LENGTH) { + expParsedLen = 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 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 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); + } + } +} + + + +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]); + } +} + +#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 enum UldnNameType { + TEST_ULDN_LOCALE, + TEST_ULDN_LANGUAGE, + TEST_ULDN_SCRIPT, + TEST_ULDN_REGION, +} UldnNameType; + +typedef struct { + const char * localeToName; // NULL to terminate a list of these + UldnNameType nameType; + const char * 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 UDisplayContext optStdBegLong[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, UDISPCTX_LENGTH_FULL}; +static const UDisplayContext optStdBegShrt[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, UDISPCTX_LENGTH_SHORT}; +static const UDisplayContext optDiaBegLong[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, UDISPCTX_LENGTH_FULL}; +static const UDisplayContext optDiaBegShrt[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, UDISPCTX_LENGTH_SHORT}; + +static const UDisplayContext optStdLstLong[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, UDISPCTX_LENGTH_FULL}; +static const UDisplayContext optStdLstShrt[3] = {UDISPCTX_STANDARD_NAMES, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, UDISPCTX_LENGTH_SHORT}; +static const UDisplayContext optDiaLstLong[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, UDISPCTX_LENGTH_FULL}; +static const UDisplayContext optDiaLstShrt[3] = {UDISPCTX_DIALECT_NAMES, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, UDISPCTX_LENGTH_SHORT}; + +static const UldnItem en_StdMidLong[] = { + { "en_US", TEST_ULDN_LOCALE, "English (US)" }, + { "en_US_POSIX", TEST_ULDN_LOCALE, "English (US, Computer)" }, + { "en_US@calendar=chinese", TEST_ULDN_LOCALE, "English (US, Chinese Calendar)" }, + { "en_CA", TEST_ULDN_LOCALE, "English (Canada)" }, + { "pt", TEST_ULDN_LOCALE, "Portuguese" }, + { "pt_BR", TEST_ULDN_LOCALE, "Portuguese (Brazil)" }, + { "pt_PT", TEST_ULDN_LOCALE, "Portuguese (Portugal)" }, + { "zh_Hans", TEST_ULDN_LOCALE, "Chinese (Simplified)" }, + { "zh_Hant_HK", TEST_ULDN_LOCALE, "Chinese (Traditional, Hong Kong)" }, + { "zh_HK", TEST_ULDN_LOCALE, "Chinese (Hong Kong)" }, + { "Latn", TEST_ULDN_SCRIPT, "Latin" }, + { "Hans", TEST_ULDN_SCRIPT, "Simplified Han" }, + { "Hant", TEST_ULDN_SCRIPT, "Traditional Han" }, + { "US", TEST_ULDN_REGION, "United States" }, + { "CA", TEST_ULDN_REGION, "Canada" }, + { "GB", TEST_ULDN_REGION, "United Kingdom" }, + { "HK", TEST_ULDN_REGION, "Hong Kong (China)" }, +}; + +static const UldnItem en_StdMidShrt[] = { + { "en_US", TEST_ULDN_LOCALE, "English (US)" }, + { "en_US_POSIX", TEST_ULDN_LOCALE, "English (US, Computer)" }, + { "en_US@calendar=chinese", TEST_ULDN_LOCALE, "English (US, Calendar: chinese)" }, + { "en_CA", TEST_ULDN_LOCALE, "English (Canada)" }, + { "pt", TEST_ULDN_LOCALE, "Portuguese" }, + { "pt_BR", TEST_ULDN_LOCALE, "Portuguese (Brazil)" }, + { "pt_PT", TEST_ULDN_LOCALE, "Portuguese (Portugal)" }, + { "zh_Hans", TEST_ULDN_LOCALE, "Chinese (Simplified)" }, + { "zh_Hant_HK", TEST_ULDN_LOCALE, "Chinese (Traditional, Hong Kong)" }, + { "zh_HK", TEST_ULDN_LOCALE, "Chinese (Hong Kong)" }, + { "Latn", TEST_ULDN_SCRIPT, "Latin" }, + { "Hans", TEST_ULDN_SCRIPT, "Simplified Han" }, + { "Hant", TEST_ULDN_SCRIPT, "Traditional Han" }, + { "US", TEST_ULDN_REGION, "US" }, + { "CA", TEST_ULDN_REGION, "Canada" }, + { "GB", TEST_ULDN_REGION, "UK" }, + { "HK", TEST_ULDN_REGION, "Hong Kong" }, +}; + +static const UldnItem en_DiaMidLong[] = { + { "en_US", TEST_ULDN_LOCALE, "American English" }, + { "en_US_POSIX", TEST_ULDN_LOCALE, "American English (Computer)" }, + { "en_US@calendar=chinese", TEST_ULDN_LOCALE, "American English (Chinese Calendar)" }, + { "en_CA", TEST_ULDN_LOCALE, "Canadian English" }, + { "pt", TEST_ULDN_LOCALE, "Portuguese" }, + { "pt_BR", TEST_ULDN_LOCALE, "Brazilian Portuguese" }, + { "pt_PT", TEST_ULDN_LOCALE, "European Portuguese" }, + { "zh_Hans", TEST_ULDN_LOCALE, "Simplified Chinese" }, + { "zh_Hant_HK", TEST_ULDN_LOCALE, "Traditional Chinese (Hong Kong)" }, + { "zh_HK", TEST_ULDN_LOCALE, "Chinese (Hong Kong)" }, + { "Latn", TEST_ULDN_SCRIPT, "Latin" }, + { "Hans", TEST_ULDN_SCRIPT, "Simplified Han" }, + { "Hant", TEST_ULDN_SCRIPT, "Traditional Han" }, + { "US", TEST_ULDN_REGION, "United States" }, + { "CA", TEST_ULDN_REGION, "Canada" }, + { "GB", TEST_ULDN_REGION, "United Kingdom" }, + { "HK", TEST_ULDN_REGION, "Hong Kong (China)" }, +}; + +static const UldnItem en_DiaMidShrt[] = { + { "en_US", TEST_ULDN_LOCALE, "US English" }, + { "en_US_POSIX", TEST_ULDN_LOCALE, "US English (Computer)" }, + { "en_US@calendar=chinese", TEST_ULDN_LOCALE, "US English (Calendar: chinese)" }, + { "en_CA", TEST_ULDN_LOCALE, "Canadian English" }, + { "pt", TEST_ULDN_LOCALE, "Portuguese" }, + { "pt_BR", TEST_ULDN_LOCALE, "Brazilian Portuguese" }, + { "pt_PT", TEST_ULDN_LOCALE, "European Portuguese" }, + { "zh_Hans", TEST_ULDN_LOCALE, "Simplified Chinese" }, + { "zh_Hant_HK", TEST_ULDN_LOCALE, "Traditional Chinese (Hong Kong)" }, + { "zh_HK", TEST_ULDN_LOCALE, "Chinese (Hong Kong)" }, + { "Latn", TEST_ULDN_SCRIPT, "Latin" }, + { "Hans", TEST_ULDN_SCRIPT, "Simplified Han" }, + { "Hant", TEST_ULDN_SCRIPT, "Traditional Han" }, + { "US", TEST_ULDN_REGION, "US" }, + { "CA", TEST_ULDN_REGION, "Canada" }, + { "GB", TEST_ULDN_REGION, "UK" }, + { "HK", TEST_ULDN_REGION, "Hong Kong" }, +}; + +static const UldnItem fr_StdMidLong[] = { + { "en_US", TEST_ULDN_LOCALE, "anglais (\\u00C9.-U.)" }, + { "US", TEST_ULDN_REGION, "\\u00C9tats-Unis" }, + { "HK", TEST_ULDN_REGION, "R.A.S. chinoise de Hong Kong" }, +}; + +static const UldnItem fr_StdMidShrt[] = { + { "en_US", TEST_ULDN_LOCALE, "anglais (\\u00C9.-U.)" }, + { "US", TEST_ULDN_REGION, "\\u00C9.-U." }, + { "HK", TEST_ULDN_REGION, "Hong Kong" }, +}; + +static const UldnItem fr_StdBegLong[] = { + { "en_US", TEST_ULDN_LOCALE, "Anglais (\\u00C9.-U.)" }, +}; + +static const UldnItem fr_StdLstLong[] = { + { "en_US", TEST_ULDN_LOCALE, "Anglais (\\u00C9.-U.)" }, +}; + +static const UldnItem fr_DiaMidLong[] = { + { "en_US", TEST_ULDN_LOCALE, "anglais am\\u00E9ricain" }, +}; + +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) }, + { "fr", optStdMidLong, fr_StdMidLong, UPRV_LENGTHOF(fr_StdMidLong) }, + { "fr", optStdMidShrt, fr_StdMidShrt, UPRV_LENGTHOF(fr_StdMidShrt) }, + { "fr", optStdBegLong, fr_StdBegLong, UPRV_LENGTHOF(fr_StdBegLong) }, + { "fr", optStdLstLong, fr_StdLstLong, UPRV_LENGTHOF(fr_StdLstLong) }, + { "fr", optDiaMidLong, fr_DiaMidLong, UPRV_LENGTHOF(fr_DiaMidLong) }, + { 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], uexp[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 = "locale"; + break; + case TEST_ULDN_LANGUAGE: + ulenget = uldn_languageDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); + typeString = "language"; + break; + case TEST_ULDN_SCRIPT: + ulenget = uldn_scriptDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); + typeString = "script"; + break; + case TEST_ULDN_REGION: + ulenget = uldn_regionDisplayName(uldn, itemPtr->localeToName, uget, kUNameBuf, &status); + typeString = "region"; + break; + default: + continue; + } + if (U_FAILURE(status)) { + log_data_err("uldn_%sDisplayName 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_unescape(itemPtr->expectResult, uexp, kUNameBuf); + if (ulenget != ulenexp || u_strncmp(uget, uexp, ulenexp) != 0) { + char bexp[kBNameBuf], bget[kBNameBuf]; + u_strToUTF8(bexp, kBNameBuf, NULL, uexp, ulenexp, &status); + u_strToUTF8(bget, kBNameBuf, NULL, uget, ulenget, &status); + log_data_err("uldn_%sDisplayName 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); + } +} + +/* Apple-specific, test for Apple-specific function ualoc_getAppleParent */ +static const char* localesAndAppleParent[] = { + "en", "root", + "en-US", "en", + "en-CA", "en_001", + "en-001", "en", + "en_001", "en", + "en-150", "en_GB", + "en-GB", "en_001", + "en_GB", "en_001", + "en-AU", "en_GB", + "en-BE", "en_150", + "en-DG", "en_GB", + "en-FK", "en_GB", + "en-GG", "en_GB", + "en-GI", "en_GB", + "en-HK", "en_GB", + "en-IE", "en_GB", + "en-IM", "en_GB", + "en-IN", "en_GB", + "en-IO", "en_GB", + "en-JE", "en_GB", + "en-MO", "en_GB", + "en-MT", "en_GB", + "en-MV", "en_GB", + "en-NZ", "en_AU", + "en-PK", "en_GB", + "en-SG", "en_GB", + "en-SH", "en_GB", + "en-VG", "en_GB", + "es", "root", + "es-ES", "es", + "es-419", "es", + "es_419", "es", + "es-MX", "es_419", + "es-AR", "es_419", + "es-BR", "es_419", + "fr", "root", + "fr-CA", "fr", + "fr-CH", "fr", + "haw", "root", + "nl", "root", + "nl-BE", "nl", + "pt", "root", + "pt-BR", "pt", + "pt-PT", "pt", + "pt-MO", "pt_PT", + "pt-CH", "pt_PT", + "pt-GQ", "pt_PT", + "pt-LU", "pt_PT", + "sr", "root", + "sr-Cyrl", "sr", + "sr-Latn", "root", + "tlh", "root", + "zh_CN", "root", + "zh-CN", "root", + "zh", "zh_CN", + "zh-Hans", "zh", + "zh_TW", "root", + "zh-TW", "root", + "zh-Hant", "zh_TW", + "zh_HK", "zh_Hant_HK", + "zh-HK", "zh_Hant_HK", + "zh_Hant", "zh_TW", + "zh-Hant-HK", "zh_Hant", + "zh_Hant_HK", "zh_Hant", + "zh-Hant-MO", "zh_Hant_HK", + "zh-Hans-HK", "zh_Hans", + "root", "root", + "en-Latn", "en", + "en-Latn-US", "en_Latn", + "en_US_POSIX", "en_US", + "en_Latn_US_POSIX", "en_Latn_US", + "en-u-ca-hebrew", "root", + "en@calendar=hebrew", "root", + "en_@calendar=hebrew", "root", + "en-", "root", + "en_", "root", + "Default@2x", "root", + "default", "root", + NULL /* terminator */ +}; + +static void TestGetAppleParent() { + const char **localesPtr = localesAndAppleParent; + const char * locale; + while ((locale = *localesPtr++) != NULL) { + const char * expectParent = *localesPtr++; + UErrorCode status = U_ZERO_ERROR; + char getParent[ULOC_FULLNAME_CAPACITY]; + int32_t plen = ualoc_getAppleParent(locale, getParent, ULOC_FULLNAME_CAPACITY, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_getAppleParent input \"%s\", status %s\n", locale, u_errorName(status)); + } else if (uprv_strcmp(expectParent, getParent) != 0) { + log_err("FAIL: ualoc_getAppleParent input \"%s\", expected parent \"%s\", got parent \"%s\"\n", locale, expectParent, getParent); + } + } +} + +/* Apple-specific, test for Apple-specific function ualoc_getLanguagesForRegion */ +enum { kUALanguageEntryMax = 10 }; + +static void TestGetLanguagesForRegion() { + UALanguageEntry entries[kUALanguageEntryMax]; + int32_t entryCount; + UErrorCode status; + const char * region; + + status = U_ZERO_ERROR; + region = "CN"; + entryCount = ualoc_getLanguagesForRegion(region, 0.001, entries, kUALanguageEntryMax, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, status %s\n", region, u_errorName(status)); + } else { + // Expect approximately: + // zh_Hans 0.90 UALANGSTATUS_OFFICIAL + // wuu 0.06 Wu + // hsn 0.06 Xiang + // yue 0.043 Yue including Cantonese + // hak 0.023 Hakka + // nan 0.019 Minnan + // gan 0.017 Gan + // ii 0.006 Yi + // ug_Arab 0.0055 Uighur UALANGSTATUS_REGIONAL_OFFICIAL + // ...at least 4 more with fractions >= 0.001 + if (entryCount < kUALanguageEntryMax) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, entryCount %d is too small\n", region, entryCount); + } else { + UALanguageEntry* entryPtr = entries; + if (uprv_strcmp(entryPtr->languageCode, "zh_Hans") != 0 || entryPtr->userFraction < 0.8 || entryPtr->userFraction > 1.0 || entryPtr->status != UALANGSTATUS_OFFICIAL) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, invalid entries[0] { %s, %.3f, %d }\n", region, entryPtr->languageCode, entryPtr->userFraction, (int)entryPtr->status); + } + for (entryPtr++; entryPtr < entries + kUALanguageEntryMax && uprv_strcmp(entryPtr->languageCode, "ug_Arab") != 0; entryPtr++) + ; + if (entryPtr < entries + kUALanguageEntryMax) { + // we found ug_Arab, make sure it has correct status + if (entryPtr->status != UALANGSTATUS_REGIONAL_OFFICIAL) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, ug_Arab had incorrect status %d\n", (int)entryPtr->status); + } + } else { + // did not find ug_Arab + log_err("FAIL: ualoc_getLanguagesForRegion %s, entries did not include ug_Arab\n", region); + } + } + } + + status = U_ZERO_ERROR; + region = "CA"; + entryCount = ualoc_getLanguagesForRegion(region, 0.001, entries, kUALanguageEntryMax, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, status %s\n", region, u_errorName(status)); + } else { + // Expect approximately: + // en 0.85 UALANGSTATUS_OFFICIAL + // fr 0.22 UALANGSTATUS_OFFICIAL + // ... + if (entryCount < 2) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, entryCount %d is too small\n", region, entryCount); + } else { + if (uprv_strcmp(entries[0].languageCode, "en") != 0 || entries[0].userFraction < 0.7 || entries[0].userFraction > 1.0 || entries[0].status != UALANGSTATUS_OFFICIAL) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, invalid entries[0] { %s, %.3f, %d }\n", region, entries[0].languageCode, entries[0].userFraction, (int)entries[0].status); + } + if (uprv_strcmp(entries[1].languageCode, "fr") != 0 || entries[1].userFraction < 0.1 || entries[1].userFraction > 1.0 || entries[1].status != UALANGSTATUS_OFFICIAL) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, invalid entries[1] { %s, %.3f, %d }\n", region, entries[1].languageCode, entries[1].userFraction, (int)entries[1].status); + } + } + } + + status = U_ZERO_ERROR; + region = "IN"; + entryCount = ualoc_getLanguagesForRegion(region, 0.001, NULL, 0, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, status %s\n", region, u_errorName(status)); + } else { + if (entryCount < 40) { + log_err("FAIL: ualoc_getLanguagesForRegion %s, entryCount %d is too small\n", region, entryCount); + } + } +} + +/* data for TestAppleLocalizationsToUse */ + +typedef struct { + const char * const *locs; + int32_t locCount; +} AppleLocsAndCount; + +enum { kNumLocSets = 6 }; + +typedef struct { + const char * language; + const char ** expLocsForSets[kNumLocSets]; +} LangAndExpLocs; + + +static const char * appleLocs1[] = { + "Arabic", + "Danish", + "Dutch", + "English", + "Finnish", + "French", + "German", + "Italian", + "Japanese", + "Korean", + "Norwegian", + "Polish", + "Portuguese", + "Russian", + "Spanish", + "Swedish", + "Thai", + "Turkish", + "ca", + "cs", + "el", + "he", + "hr", + "hu", + "id", + "ms", + "ro", + "sk", + "uk", + "vi", + "zh_CN", "zh_TW", +}; + +static const char * appleLocs2[] = { + "ar", + "ca", + "cs", + "da", + "de", + "el", + "en", "en_AU", "en_GB", + "es", "es_MX", + "fi", + "fr", "fr_CA", + "he", + "hr", + "hu", + "id", + "it", + "ja", + "ko", + "ms", + "nl", + "no", + "pl", + "pt", "pt_PT", + "ro", + "ru", + "sk", + "sv", + "th", + "tr", + "uk", + "vi", + "zh_CN", "zh_HK", "zh_TW", +}; + +static const char * appleLocs3[] = { + "ar", + "ca", + "cs", + "da", + "de", + "el", + "en", "en_AU", "en_CA", "en_GB", + "es", "es_419", + "fi", + "fr", "fr_CA", "fr_FR", + "he", + "hr", + "hu", + "id", + "it", + "ja", + "ko", + "ms", + "nb", + "nl", + "pl", + "pt", "pt_BR", "pt_PT", + "ro", + "ru", + "sk", + "sv", + "th", + "tr", + "uk", + "vi", + "zh_CN", "zh_HK", "zh_MO", "zh_TW", +}; + +static const char * appleLocs4[] = { + "en", "en_AU", "en_CA", "en_GB", "en_IN", "en_US", + "es", "es_419", "es_MX", + "fr", "fr_CA", "fr_CH", "fr_FR", + "nl", "nl_BE", "nl_NL", + "pt", "pt_BR", + "ro", "ro_MD", "ro_RO", + "zh_Hans", "zh_Hant", "zh_Hant_HK", +}; + +static const char * appleLocs5[] = { + "en", "en_001", "en_AU", "en_GB", + "es", "es_ES", "es_MX", + "zh_CN", "zh_Hans", "zh_Hant", "zh_TW", + "yi", + "fil", + "haw", + "tlh", + "sr", + "sr-Latn", +}; + +// list 6 +static const char * appleLocs6[] = { + "en", "en_001", "en_150", "en_AU", "en_GB", + "es", "es_419", "es_ES", "es_MX", + "zh_CN", "zh_Hans", "zh_Hant", "zh_Hant_HK", "zh_HK", "zh_TW", + "iw", + "in", + "mo", + "tl", +}; + +static const AppleLocsAndCount locAndCountEntries[kNumLocSets] = { + { appleLocs1, UPRV_LENGTHOF(appleLocs1) }, + { appleLocs2, UPRV_LENGTHOF(appleLocs2) }, + { appleLocs3, UPRV_LENGTHOF(appleLocs3) }, + { appleLocs4, UPRV_LENGTHOF(appleLocs4) }, + { appleLocs5, UPRV_LENGTHOF(appleLocs5) }, + { appleLocs6, UPRV_LENGTHOF(appleLocs6) }, +}; + + +static const char* l1_ar[] = { "ar", NULL }; +static const char* l1_Ara[] = { "Arabic", NULL }; +static const char* l1_ca[] = { "ca", NULL }; +static const char* l1_cs[] = { "cs", NULL }; +static const char* l1_da[] = { "da", NULL }; +static const char* l1_Dan[] = { "Danish", NULL }; +static const char* l1_de[] = { "de", NULL }; +static const char* l1_Ger[] = { "German", NULL }; +static const char* l1_el[] = { "el", NULL }; +static const char* l1_en[] = { "en", NULL }; +static const char* l1_Eng[] = { "English", NULL }; +static const char* l2_en_001_[] = { "en_001", "en", NULL }; +static const char* l2_en_CA_[] = { "en_CA", "en", NULL }; +static const char* l2_en_GB_[] = { "en_GB", "en", NULL }; +static const char* l2_en_US_[] = { "en_US", "en", NULL }; +static const char* l2_en_GB_Eng[] = { "en_GB", "English", NULL }; +static const char* l3_en_GB001_[] = { "en_GB", "en_001", "en", NULL }; +static const char* l3_en_AUGB_[] = { "en_AU", "en_GB", "en", NULL }; +static const char* l3_en_INGB_[] = { "en_IN", "en_GB", "en", NULL }; +static const char* l4_en_150GB001_[] = { "en_150", "en_GB", "en_001", "en", NULL }; +static const char* l4_en_AUGB001_[] = { "en_AU", "en_GB", "en_001", "en", NULL }; +static const char* l1_es[] = { "es", NULL }; +static const char* l1_Spa[] = { "Spanish", NULL }; +static const char* l2_es_419_[] = { "es_419", "es", NULL }; +static const char* l2_es_ES_[] = { "es_ES", "es", NULL }; +static const char* l2_es_MX_[] = { "es_MX", "es", NULL }; +static const char* l2_es_MX_Spa[] = { "es_MX", "Spanish", NULL }; +static const char* l3_es_MX419_[] = { "es_MX", "es_419", "es", NULL }; +static const char* l1_fi[] = { "fi", NULL }; +static const char* l1_Fin[] = { "Finnish", NULL }; +static const char* l1_fil[] = { "fil", NULL }; +static const char* l1_tl[] = { "tl", NULL }; +static const char* l1_fr[] = { "fr", NULL }; +static const char* l1_Fre[] = { "French", NULL }; +static const char* l2_fr_CA_[] = { "fr_CA", "fr", NULL }; +static const char* l2_fr_CH_[] = { "fr_CH", "fr", NULL }; +static const char* l2_fr_FR_[] = { "fr_FR", "fr", NULL }; +static const char* l1_haw[] = { "haw", NULL }; +static const char* l1_he[] = { "he", NULL }; +static const char* l1_hr[] = { "hr", NULL }; +static const char* l1_hu[] = { "hu", NULL }; +static const char* l1_id[] = { "id", NULL }; +static const char* l1_in[] = { "in", NULL }; +static const char* l1_it[] = { "it", NULL }; +static const char* l1_Ita[] = { "Italian", NULL }; +static const char* l1_ja[] = { "ja", NULL }; +static const char* l1_Japn[] = { "Japanese", NULL }; +static const char* l1_ko[] = { "ko", NULL }; +static const char* l1_Kor[] = { "Korean", NULL }; +static const char* l1_ms[] = { "ms", NULL }; +static const char* l1_nb[] = { "nb", NULL }; +static const char* l1_no[] = { "no", NULL }; +static const char* l1_Nor[] = { "Norwegian", NULL }; +static const char* l2_no_NO_[] = { "no_NO", "no", NULL }; +static const char* l1_nl[] = { "nl", NULL }; +static const char* l1_Dut[] = { "Dutch", NULL }; +static const char* l2_nl_BE_[] = { "nl_BE", "nl", NULL }; +static const char* l1_pl[] = { "pl", NULL }; +static const char* l1_Pol[] = { "Polish", NULL }; +static const char* l1_pt[] = { "pt", NULL }; +static const char* l1_pt_PT[] = { "pt_PT", NULL }; +static const char* l1_Port[] = { "Portuguese", NULL }; +static const char* l2_pt_BR_[] = { "pt_BR", "pt", NULL }; +static const char* l2_pt_PT_[] = { "pt_PT", "pt", NULL }; +static const char* l1_ro[] = { "ro", NULL }; +static const char* l2_ro_MD_[] = { "ro_MD", "ro", NULL }; +static const char* l1_mo[] = { "mo", NULL }; +static const char* l1_ru[] = { "ru", NULL }; +static const char* l1_Rus[] = { "Russian", NULL }; +static const char* l1_sk[] = { "sk", NULL }; +static const char* l1_sr[] = { "sr", NULL }; +static const char* l1_srLatn[] = { "sr-Latn", NULL }; +static const char* l1_sv[] = { "sv", NULL }; +static const char* l1_Swe[] = { "Swedish", NULL }; +static const char* l1_th[] = { "th", NULL }; +static const char* l1_Thai[] = { "Thai", NULL }; +static const char* l1_tlh[] = { "tlh", NULL }; +static const char* l1_tr[] = { "tr", NULL }; +static const char* l1_Tur[] = { "Turkish", NULL }; +static const char* l1_uk[] = { "uk", NULL }; +static const char* l1_vi[] = { "vi", NULL }; +static const char* l1_yi[] = { "yi", NULL }; +static const char* l1_iw[] = { "iw", NULL }; +static const char* l1_zh_CN[] = { "zh_CN", NULL }; +static const char* l1_zh_TW[] = { "zh_TW", NULL }; +static const char* l1_zh_Hans[] = { "zh_Hans", NULL }; +static const char* l1_zh_Hant[] = { "zh_Hant", NULL }; +static const char* l1_zhHant[] = { "zh-Hant", NULL }; +static const char* l2_zh_HKTW[] = { "zh_HK", "zh_TW", NULL }; +static const char* l2_zh_Hant_HK_[] = { "zh_Hant_HK", "zh_Hant", NULL }; +static const char* l2_zh_CN_Hans[] = { "zh_CN", "zh_Hans", NULL }; +static const char* l2_zh_TW_Hant[] = { "zh_TW", "zh_Hant", NULL }; +static const char* l3_zh_MOHKTW[] = { "zh_MO", "zh_HK", "zh_TW", NULL }; +static const char* l3_zh_HK_HantHK_Hant[] = { "zh_HK", "zh_Hant_HK", "zh_Hant", NULL }; + +static const LangAndExpLocs appleLangAndLoc[] = { +// language\ result for appleLocs1 appleLocs2 appleLocs3 appleLocs4 appleLocs5 appleLocs6 + { "zh", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l1_zh_Hans, l1_zh_Hans } }, + { "zh-Hans", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l1_zh_Hans, l1_zh_Hans } }, + { "zh-Hant", { l1_zh_TW, l1_zh_TW, l1_zh_TW, l1_zh_Hant, l1_zh_Hant, l1_zh_Hant } }, + { "zh-Hans-CN", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l2_zh_CN_Hans, l2_zh_CN_Hans } }, + { "zh-Hans-SG", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l1_zh_Hans, l1_zh_Hans } }, + { "zh-Hant-TW", { l1_zh_TW, l1_zh_TW, l1_zh_TW, l1_zh_Hant, l2_zh_TW_Hant, l2_zh_TW_Hant } }, + { "zh-Hant-HK", { l1_zh_TW, l2_zh_HKTW, l2_zh_HKTW, l2_zh_Hant_HK_, l1_zh_Hant, l2_zh_Hant_HK_ } }, + { "zh-Hant-MO", { l1_zh_TW, l2_zh_HKTW, l3_zh_MOHKTW, l2_zh_Hant_HK_, l1_zh_Hant, l2_zh_Hant_HK_ } }, + { "zh-Hans-HK", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l1_zh_Hans, l1_zh_Hans } }, + { "zh-CN", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l2_zh_CN_Hans, l2_zh_CN_Hans } }, + { "zh-SG", { l1_zh_CN, l1_zh_CN, l1_zh_CN, l1_zh_Hans, l1_zh_Hans, l1_zh_Hans } }, + { "zh-TW", { l1_zh_TW, l1_zh_TW, l1_zh_TW, l1_zh_Hant, l2_zh_TW_Hant, l2_zh_TW_Hant } }, + { "zh-HK", { l1_zh_TW, l2_zh_HKTW, l2_zh_HKTW, l2_zh_Hant_HK_, l1_zh_Hant, l3_zh_HK_HantHK_Hant } }, + { "zh-MO", { l1_zh_TW, l2_zh_HKTW, l3_zh_MOHKTW, l2_zh_Hant_HK_, l1_zh_Hant, l2_zh_Hant_HK_ } }, + { "en", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en-US", { l1_Eng, l1_en, l1_en, l2_en_US_, l1_en, l1_en } }, + { "en-AU", { l1_Eng, l3_en_AUGB_, l3_en_AUGB_, l3_en_AUGB_, l4_en_AUGB001_, l4_en_AUGB001_ } }, + { "en-CA", { l1_Eng, l1_en, l2_en_CA_, l2_en_CA_, l2_en_001_, l2_en_001_ } }, + { "en-GB", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-IN", { l1_Eng, l2_en_GB_, l2_en_GB_, l3_en_INGB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-US", { l1_Eng, l1_en, l1_en, l2_en_US_, l1_en, l1_en } }, + { "en_US", { l1_Eng, l1_en, l1_en, l2_en_US_, l1_en, l1_en } }, + { "en-FR", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l4_en_150GB001_ } }, + { "en-BE", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l4_en_150GB001_ } }, + { "en-GG", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-HK", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-IE", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-MO", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-MT", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-NZ", { l1_Eng, l3_en_AUGB_, l3_en_AUGB_, l3_en_AUGB_, l4_en_AUGB001_, l4_en_AUGB001_ } }, + { "en-PK", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-SG", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-VG", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l3_en_GB001_ } }, + { "en-IL", { l1_Eng, l1_en, l1_en, l1_en, l2_en_001_, l2_en_001_ } }, + { "en-001", { l1_Eng, l1_en, l1_en, l1_en, l2_en_001_, l2_en_001_ } }, + { "en-150", { l1_Eng, l2_en_GB_, l2_en_GB_, l2_en_GB_, l3_en_GB001_, l4_en_150GB001_ } }, + { "en-Latn", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en-Latn-US", { l1_Eng, l1_en, l1_en, l1_en,/*TODO*/ l1_en, l1_en } }, + { "en-US-POSIX", { l1_Eng, l1_en, l1_en, l2_en_US_, l1_en, l1_en } }, + { "en-Latn-US-POSIX", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en-u-ca-hebrew", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en@calendar=hebrew", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en-", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "en_", { l1_Eng, l1_en, l1_en, l1_en, l1_en, l1_en } }, + { "es", { l1_Spa, l1_es, l1_es, l1_es, l1_es, l1_es } }, + { "es-ES", { l1_Spa, l1_es, l1_es, l1_es, l2_es_ES_, l2_es_ES_ } }, + { "es-419", { l1_Spa, l1_es, l2_es_419_, l2_es_419_, l1_es, l2_es_419_ } }, + { "es-MX", { l1_Spa, l2_es_MX_, l2_es_419_, l3_es_MX419_, l2_es_MX_, l3_es_MX419_ } }, + { "es-AR", { l1_Spa, l1_es, l2_es_419_, l2_es_419_, l1_es, l2_es_419_ } }, + { "es-BR", { l1_Spa, l1_es, l2_es_419_, l2_es_419_, l1_es, l2_es_419_ } }, + { "es-Latn", { l1_Spa, l1_es, l1_es, l1_es, l1_es, l1_es } }, + { "es-Latn-MX", { l1_Spa, l1_es, l1_es, l1_es, l1_es, l1_es } }, + { "pt", { l1_Port, l1_pt, l1_pt, l1_pt, NULL, NULL } }, + { "pt-BR", { l1_Port, l1_pt, l2_pt_BR_, l2_pt_BR_, NULL, NULL } }, + { "pt-PT", { l1_Port, l2_pt_PT_, l2_pt_PT_, l1_pt, NULL, NULL } }, + { "pt-MO", { l1_Port, l2_pt_PT_, l2_pt_PT_, l1_pt, NULL, NULL } }, + { "pt-CH", { l1_Port, l2_pt_PT_, l2_pt_PT_, l1_pt, NULL, NULL } }, + { "pt-GQ", { l1_Port, l2_pt_PT_, l2_pt_PT_, l1_pt, NULL, NULL } }, + { "pt-LU", { l1_Port, l2_pt_PT_, l2_pt_PT_, l1_pt, NULL, NULL } }, + { "fr", { l1_Fre, l1_fr, l1_fr, l1_fr, NULL, NULL } }, + { "fr-FR", { l1_Fre, l1_fr, l2_fr_FR_, l2_fr_FR_, NULL, NULL } }, + { "fr-CA", { l1_Fre, l2_fr_CA_, l2_fr_CA_, l2_fr_CA_, NULL, NULL } }, + { "fr-CH", { l1_Fre, l1_fr, l1_fr, l2_fr_CH_, NULL, NULL } }, + { "ar", { l1_Ara, l1_ar, l1_ar, NULL, NULL, NULL } }, + { "da", { l1_Dan, l1_da, l1_da, NULL, NULL, NULL } }, + { "nl", { l1_Dut, l1_nl, l1_nl, l1_nl, NULL, NULL } }, + { "nl-BE", { l1_Dut, l1_nl, l1_nl, l2_nl_BE_, NULL, NULL } }, + { "fi", { l1_Fin, l1_fi, l1_fi, NULL, NULL, NULL } }, + { "de", { l1_Ger, l1_de, l1_de, NULL, NULL, NULL } }, + { "it", { l1_Ita, l1_it, l1_it, NULL, NULL, NULL } }, + { "ja", { l1_Japn, l1_ja, l1_ja, NULL, NULL, NULL } }, + { "ko", { l1_Kor, l1_ko, l1_ko, NULL, NULL, NULL } }, + { "nb", { l1_Nor, l1_no, l1_nb, NULL, NULL, NULL } }, + { "no", { l1_Nor, l1_no, l1_nb, NULL, NULL, NULL } }, + { "pl", { l1_Pol, l1_pl, l1_pl, NULL, NULL, NULL } }, + { "ru", { l1_Rus, l1_ru, l1_ru, NULL, NULL, NULL } }, + { "sv", { l1_Swe, l1_sv, l1_sv, NULL, NULL, NULL } }, + { "th", { l1_Thai, l1_th, l1_th, NULL, NULL, NULL } }, + { "tr", { l1_Tur, l1_tr, l1_tr, NULL, NULL, NULL } }, + { "ca", { l1_ca, l1_ca, l1_ca, NULL, NULL, NULL } }, + { "cs", { l1_cs, l1_cs, l1_cs, NULL, NULL, NULL } }, + { "el", { l1_el, l1_el, l1_el, NULL, NULL, NULL } }, + { "he", { l1_he, l1_he, l1_he, NULL, NULL, l1_iw } }, + { "iw", { l1_he, l1_he, l1_he, NULL, NULL, l1_iw } }, + { "hr", { l1_hr, l1_hr, l1_hr, NULL, NULL, NULL } }, + { "hu", { l1_hu, l1_hu, l1_hu, NULL, NULL, NULL } }, + { "id", { l1_id, l1_id, l1_id, NULL, NULL, l1_in } }, + { "in", { l1_id, l1_id, l1_id, NULL, NULL, l1_in } }, + { "ms", { l1_ms, l1_ms, l1_ms, NULL, NULL, NULL } }, + { "ro", { l1_ro, l1_ro, l1_ro, l1_ro, NULL, l1_mo } }, + { "mo", { l1_ro, l1_ro, l1_ro, l2_ro_MD_, NULL, l1_mo } }, + { "sk", { l1_sk, l1_sk, l1_sk, NULL, NULL, NULL } }, + { "uk", { l1_uk, l1_uk, l1_uk, NULL, NULL, NULL } }, + { "vi", { l1_vi, l1_vi, l1_vi, NULL, NULL, NULL } }, + { "yi", { NULL, NULL, NULL, NULL, l1_yi, NULL } }, + { "ji", { NULL, NULL, NULL, NULL, l1_yi, NULL } }, + { "fil", { NULL, NULL, NULL, NULL, l1_fil, l1_tl } }, + { "tl", { NULL, NULL, NULL, NULL, l1_fil, l1_tl } }, + { "haw", { NULL, NULL, NULL, NULL, l1_haw, NULL } }, + { "sr", { NULL, NULL, NULL, NULL, l1_sr, NULL } }, + { "sr-Cyrl", { NULL, NULL, NULL, NULL, l1_sr, NULL } }, + { "sr-Latn", { NULL, NULL, NULL, NULL, l1_srLatn, NULL } }, + { "tlh", { NULL, NULL, NULL, NULL, l1_tlh, NULL } }, + { "Default@2x", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "default", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "root", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "_US", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "-US", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "-u-ca-hebrew", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "-u-ca-hebrew", { NULL, NULL, NULL, NULL, NULL, NULL } }, + { "@calendar=hebrew", { NULL, NULL, NULL, NULL, NULL, NULL } }, +}; +enum { kNumAppleLangAndLoc = UPRV_LENGTHOF(appleLangAndLoc) }; + +/* tests from */ + +static const char * appleLocsA1[] = { "en", "fr", "no", "zh-Hant" }; +static const char * appleLocsA2[] = { "en", "fr", "nb", "zh_TW", "zh_CN", "zh-Hant" }; +static const char * appleLocsA3[] = { "en", "en_IN", "en_GB", "fr", "de", "zh_TW" }; +static const char * appleLocsA4[] = { "Spanish", "es_MX", "English", "en_GB" }; +static const char * appleLocsA5[] = { "en", "fr", "de", "pt", "pt_PT" }; +static const char * appleLocsA6[] = { "en", "no", "no_NO", "pt_PT" }; + +static const AppleLocsAndCount locAndCountEntriesA[kNumLocSets] = { + { appleLocsA1, UPRV_LENGTHOF(appleLocsA1) }, + { appleLocsA2, UPRV_LENGTHOF(appleLocsA2) }, + { appleLocsA3, UPRV_LENGTHOF(appleLocsA3) }, + { appleLocsA4, UPRV_LENGTHOF(appleLocsA4) }, + { appleLocsA5, UPRV_LENGTHOF(appleLocsA5) }, + { appleLocsA6, UPRV_LENGTHOF(appleLocsA6) }, +}; + +static const LangAndExpLocs appleLangAndLocA[] = { +// language\ result for appleLocsA1 appleLocsA2 appleLocsA3 appleLocsA4 appleLocsA5 appleLocsA6 + { "zh-Hant", { l1_zhHant,/*0*/ l1_zhHant,/*zh_TW*/ l1_zh_TW, NULL, NULL, NULL } }, + { "zh_Hant", { l1_zhHant, l1_zhHant,/*zh_TW*/ l1_zh_TW, NULL, NULL, NULL } }, + { "zh_HK", { l1_zhHant, l1_zhHant,/*zh_TW*/ l1_zh_TW, NULL, NULL, NULL } }, + { "en_IN", { l1_en, l1_en, l3_en_INGB_, l2_en_GB_Eng, l1_en, l1_en } }, + { "es_MX", { NULL, NULL, NULL, l2_es_MX_Spa, NULL, NULL } }, + { "pt_PT", { NULL, NULL, NULL, NULL, l2_pt_PT_, l1_pt_PT } }, + { "pt", { NULL, NULL, NULL, NULL, l1_pt, l1_pt_PT } }, + { "no", { l1_no, l1_nb, NULL, NULL, NULL, l1_no } }, + { "no_NO", { l1_no, l1_nb, NULL, NULL, NULL, l2_no_NO_ } }, + { "nb", { l1_no, l1_nb, NULL, NULL, NULL, l1_no } }, + { "nb_NO", { l1_no, l1_nb, NULL, NULL, NULL, l2_no_NO_ } }, +}; +enum { kNumAppleLangAndLocA = UPRV_LENGTHOF(appleLangAndLocA) }; + +/* tests from log attached to 21682790 */ + +static const char * appleLocsB1[] = { + "ar", "Base", "ca", "cs", + "da", "Dutch", "el", "English", + "es_MX", "fi", "French", "German", + "he", "hr", "hu", "id", + "Italian", "Japanese", "ko", "ms", + "no", "pl", "pt", "pt_PT", + "ro", "ru", "sk", "Spanish", + "sv", "th", "tr", "uk", + "vi", "zh_CN", "zh_TW" +}; + +static const char * appleLocsB2[] = { + "ar", "ca", "cs", + "da", "Dutch", "el", "English", + "es_MX", "fi", "French", "German", + "he", "hr", "hu", "id", + "Italian", "Japanese", "ko", "ms", + "no", "pl", "pt", "pt_PT", + "ro", "ru", "sk", "Spanish", + "sv", "th", "tr", "uk", + "vi", "zh_CN", "zh_TW" +}; + +static const char * appleLocsB3[] = { + "ar", "ca", "cs", "da", + "de", "el", "en", "es", + "es_MX", "fi", "French", "he", + "hr", "hu", "id", "Italian", + "ja", "ko", "ms", "nl", + "no", "pl", "pt", "pt_PT", + "ro", "ru", "sk", "sv", + "th", "tr", "uk", "vi", + "zh_CN", "zh_TW" +}; + +static const char * appleLocsB4[] = { + "ar", "ca", "cs", "da", + "de", "el", "en", "es", + "es_MX", "fi", "fr", "he", + "hr", "hu", "id", "it", + "ja", "ko", "ms", "nl", + "no", "pl", "pt", "pt_PT", + "ro", "ru", "sk", "sv", + "th", "tr", "uk", "vi", + "zh_CN", "zh_TW" +}; + +static const char * appleLocsB5[] = { "en" }; + +static const char * appleLocsB6[] = { "English" }; + +static const AppleLocsAndCount locAndCountEntriesB[kNumLocSets] = { + { appleLocsB1, UPRV_LENGTHOF(appleLocsB1) }, + { appleLocsB2, UPRV_LENGTHOF(appleLocsB2) }, + { appleLocsB3, UPRV_LENGTHOF(appleLocsB3) }, + { appleLocsB4, UPRV_LENGTHOF(appleLocsB4) }, + { appleLocsB5, UPRV_LENGTHOF(appleLocsB5) }, + { appleLocsB6, UPRV_LENGTHOF(appleLocsB6) }, +}; + +static const LangAndExpLocs appleLangAndLocB[] = { +// language\ result for appleLocsB1 appleLocsB2 appleLocsB3 appleLocsB4 appleLocsB5 appleLocsB6 +// Prefs 1, logged with sets B1-B3 + { "en", { l1_Eng, l1_Eng, l1_en, l1_en, l1_en, l1_Eng } }, + { "es", { l1_Spa, l1_Spa, l1_es, l1_es, NULL, NULL } }, +// Prefs 2, logged with sets B1-B6 + { "English", { l1_Eng, l1_Eng, l1_en, l1_en, l1_en, l1_Eng } }, + { "Spanish", { l1_Spa, l1_Spa, l1_es, l1_es, NULL, NULL } }, +}; +enum { kNumAppleLangAndLocB = UPRV_LENGTHOF(appleLangAndLocB) }; + +typedef struct { + const AppleLocsAndCount * locAndCountEntriesPtr; + const LangAndExpLocs * appleLangAndLocPtr; + int32_t appleLangAndLocCount; +} AppleLocToUseTestSet; + +static const AppleLocToUseTestSet altuTestSets[] = { + { locAndCountEntries, appleLangAndLoc, kNumAppleLangAndLoc }, + { locAndCountEntriesA, appleLangAndLocA, kNumAppleLangAndLocA }, + { locAndCountEntriesB, appleLangAndLocB, kNumAppleLangAndLocB }, + { NULL, NULL, 0 } +}; + +/* tests for multiple prefs sets */ + +static const char * appleLocsM1[] = { "en", "en_GB", "pt", "pt_PT", "zh_CN", "zh_Hant" }; +static const char * prefLangsM1[] = { "tlh", "zh_HK", "zh_SG", "zh_Hans", "pt_BR", "pt_PT", "en_IN", "en" }; +static const char * locsToUseM1[] = { "zh_Hant" }; + +// Tests from first pass at , 2015-11-18 + +static const char * appleLocsM2[] = { "fr-FR", "en-US", "en-GB" }; +static const char * prefLangsM2[] = { "fr-CH" }; +static const char * locsToUseM2[] = { "fr-FR" }; + +static const char * appleLocsM3[] = { "es-es", "fr-fr" }; +static const char * prefLangsM3[] = { "fr-US", "fr", "en-US" }; +static const char * locsToUseM3[] = { "fr-fr" }; + +static const char * appleLocsM4[] = { "es-es", "fr-fr", "fr" }; +static const char * prefLangsM4[] = { "fr-US", "fr", "en-US" }; +static const char * locsToUseM4[] = { "fr" }; + +// Tests from second pass at , 2015-12-08 +// Per Karan M +static const char * appleLocsM5[] = { "en-US", "fr-FR", "de-DE", "es-ES", "es-419", "pt-PT", "pt-BR", "zh-CN", "zh-TW", "zh-HK", "ja-JP", "ko-KR" }; +static const char * prefLangsM5[] = { "fr-US", "en-US" }; +static const char * locsToUseM5[] = { "fr-FR" }; +// Per Peter E; expected result changed from "en-US" to "de-CH" per +static const char * appleLocsM6[] = { "de-CH", "en-US" }; +static const char * prefLangsM6[] = { "de-DE", "en-US" }; +static const char * locsToUseM6[] = { "de-CH" }; +// The following is used for M7-MD +static const char * appleLocsMx[] = { "de-DE", "en-AU", "es-ES", "fr-FR", "hi-IN", "pt-BR", "zh-HK", "zh-TW" }; +// Per Karan M +static const char * prefLangsM7[] = { "fr-ES", "en-AU" }; +static const char * locsToUseM7[] = { "fr-FR" }; +// Per Karan M +static const char * prefLangsM8[] = { "de-IT", "en-AU" }; +static const char * locsToUseM8[] = { "de-DE" }; +// Per Karan M +static const char * prefLangsM9[] = { "hi-US", "en-AU" }; +static const char * locsToUseM9[] = { "hi-IN" }; +// Per Karan M +static const char * prefLangsMA[] = { "en-IN", "zh-HK" }; +static const char * locsToUseMA[] = { "en-AU" }; +// Per Karan M +static const char * prefLangsMB[] = { "pt-PT", "en-AU" }; +static const char * locsToUseMB[] = { "en-AU" }; +// per Paul B: +static const char * prefLangsMC[] = { "pt-PT", "ar" }; +static const char * locsToUseMC[] = { "pt-BR" }; +// Per Karan M +static const char * prefLangsMD[] = { "zh-CN", "en-AU" }; +static const char * locsToUseMD[] = { "en-AU" }; +// Per Karan M +static const char * appleLocsME[] = { "de-DE", "en-AU", "es-ES", "fr-FR", "hi-IN", "pt-BR", "zh-CN", "zh-HK" }; +static const char * prefLangsME[] = { "zh-TW", "en-AU" }; +static const char * locsToUseME[] = { "zh-HK" }; +// Per Peter E in diagnosis for and +static const char * appleLocsMF[] = { "en", "en-GB", "fr", "es" }; +static const char * prefLangsMF[] = { "en-IN", "en-GB", "de", "fr" }; +static const char * locsToUseMF[] = { "en-GB", "en" }; +// Per Karan M in +static const char * appleLocsMG[] = { "zh-Hans", "zh-Hant", "zh-HK" }; +static const char * prefLangsMG[] = { "zh-Hans-US", "zh-HK", "en-US" }; +static const char * locsToUseMG[] = { "zh-Hans" }; +// Per +static const char * appleLocsMH[] = { "zh-TW", "zh-CN", "zh-HK" }; +static const char * prefLangsMH[] = { "zh-Hans-HK", "zh-HK", "en" }; +static const char * locsToUseMH[] = { "zh-CN" }; +// Per +static const char * appleLocsMI[] = { "unk", "en-US", "ar-SA" }; +static const char * prefLangsMI[] = { "ar-US" }; +static const char * locsToUseMI[] = { "ar-SA" }; + +typedef struct { + const char * name; + const char ** availLocs; + int32_t availLocsCount; + const char ** prefLangs; + int32_t prefLangsCount; + const char ** locsToUse; + int32_t locsToUseCount; +} MultiPrefTest; + +static const MultiPrefTest multiTestSets[] = { + { "M1", appleLocsM1, UPRV_LENGTHOF(appleLocsM1), prefLangsM1, UPRV_LENGTHOF(prefLangsM1), locsToUseM1, UPRV_LENGTHOF(locsToUseM1) }, + // + { "M2", appleLocsM2, UPRV_LENGTHOF(appleLocsM2), prefLangsM2, UPRV_LENGTHOF(prefLangsM2), locsToUseM2, UPRV_LENGTHOF(locsToUseM2) }, + { "M3", appleLocsM3, UPRV_LENGTHOF(appleLocsM3), prefLangsM3, UPRV_LENGTHOF(prefLangsM3), locsToUseM3, UPRV_LENGTHOF(locsToUseM3) }, + { "M4", appleLocsM4, UPRV_LENGTHOF(appleLocsM4), prefLangsM4, UPRV_LENGTHOF(prefLangsM4), locsToUseM4, UPRV_LENGTHOF(locsToUseM4) }, + // + { "M5", appleLocsM5, UPRV_LENGTHOF(appleLocsM5), prefLangsM5, UPRV_LENGTHOF(prefLangsM5), locsToUseM5, UPRV_LENGTHOF(locsToUseM5) }, + { "M6", appleLocsM6, UPRV_LENGTHOF(appleLocsM6), prefLangsM6, UPRV_LENGTHOF(prefLangsM6), locsToUseM6, UPRV_LENGTHOF(locsToUseM6) }, + { "M7", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsM7, UPRV_LENGTHOF(prefLangsM7), locsToUseM7, UPRV_LENGTHOF(locsToUseM7) }, + { "M8", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsM8, UPRV_LENGTHOF(prefLangsM8), locsToUseM8, UPRV_LENGTHOF(locsToUseM8) }, + { "M9", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsM9, UPRV_LENGTHOF(prefLangsM9), locsToUseM9, UPRV_LENGTHOF(locsToUseM9) }, + { "MA", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsMA, UPRV_LENGTHOF(prefLangsMA), locsToUseMA, UPRV_LENGTHOF(locsToUseMA) }, + { "MB", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsMB, UPRV_LENGTHOF(prefLangsMB), locsToUseMB, UPRV_LENGTHOF(locsToUseMB) }, + { "MC", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsMC, UPRV_LENGTHOF(prefLangsMC), locsToUseMC, UPRV_LENGTHOF(locsToUseMC) }, + { "MD", appleLocsMx, UPRV_LENGTHOF(appleLocsMx), prefLangsMD, UPRV_LENGTHOF(prefLangsMD), locsToUseMD, UPRV_LENGTHOF(locsToUseMD) }, + { "ME", appleLocsME, UPRV_LENGTHOF(appleLocsME), prefLangsME, UPRV_LENGTHOF(prefLangsME), locsToUseME, UPRV_LENGTHOF(locsToUseME) }, + { "MF", appleLocsMF, UPRV_LENGTHOF(appleLocsMF), prefLangsMF, UPRV_LENGTHOF(prefLangsMF), locsToUseMF, UPRV_LENGTHOF(locsToUseMF) }, + { "MG", appleLocsMG, UPRV_LENGTHOF(appleLocsMG), prefLangsMG, UPRV_LENGTHOF(prefLangsMG), locsToUseMG, UPRV_LENGTHOF(locsToUseMG) }, + { "MH", appleLocsMH, UPRV_LENGTHOF(appleLocsMH), prefLangsMH, UPRV_LENGTHOF(prefLangsMH), locsToUseMH, UPRV_LENGTHOF(locsToUseMH) }, + { "MI", appleLocsMI, UPRV_LENGTHOF(appleLocsMI), prefLangsMI, UPRV_LENGTHOF(prefLangsMI), locsToUseMI, UPRV_LENGTHOF(locsToUseMI) }, + { NULL, NULL, 0, NULL, 0, NULL, 0 } +}; + + +/* general enums */ + +enum { kMaxLocalizationsToUse = 8, kPrintArrayBufSize = 128 }; + +// array, array of pointers to strings to print +// count, count of array elements, may be -1 if array is terminated by a NULL entry +// buf, buffer into which to put concatenated strings +// bufSize, length of buf +static void printStringArray(const char **array, int32_t count, char *buf, int32_t bufSize) { + char * bufPtr = buf; + const char * curEntry; + int32_t idx, countMax = bufSize/16; + if (count < 0 || count > countMax) { + count = countMax; + } + for (idx = 0; idx < count && (curEntry = *array++) != NULL; idx++) { + int32_t len = sprintf(bufPtr, "%s\"%.12s\"", (idx > 0)? ", ": "", curEntry); + if (len <= 0) { + break; + } + bufPtr += len; + } + *bufPtr = 0; /* ensure termination */ +} + +static UBool equalStringArrays(const char **array1, int32_t count1, const char **array2, int32_t count2) { + const char ** array1Ptr = array1; + const char ** array2Ptr = array2; + int32_t idx; + if (count1 < 0) { + count1 = 0; + while (*array1Ptr++ != NULL) { + count1++; + } + } + if (count2 < 0) { + count2 = 0; + while (*array2Ptr++ != NULL) { + count2++; + } + } + if (count1 != count2) { + return FALSE; + } + for (idx = 0; idx < count1; idx++) { + if (uprv_strcmp(array1[idx], array2[idx]) != 0) { + return FALSE; + } + } + return TRUE; +} + +static void TestAppleLocalizationsToUse() { + const AppleLocToUseTestSet * testSetPtr; + const MultiPrefTest * multiSetPtr; + const char * locsToUse[kMaxLocalizationsToUse]; + int32_t numLocsToUse; + UErrorCode status; + char printExpected[kPrintArrayBufSize]; + char printActual[kPrintArrayBufSize]; + + for (testSetPtr = altuTestSets; testSetPtr->locAndCountEntriesPtr != NULL; testSetPtr++) { + int32_t iLocSet, iLang; + + for (iLocSet = 0; iLocSet < kNumLocSets; iLocSet++) { + for (iLang = 0; iLang < testSetPtr->appleLangAndLocCount; iLang++) { + const char * language = testSetPtr->appleLangAndLocPtr[iLang].language; + const char ** expLocsForSet = testSetPtr->appleLangAndLocPtr[iLang].expLocsForSets[iLocSet]; + status = U_ZERO_ERROR; + + numLocsToUse = ualoc_localizationsToUse(&language, 1, + testSetPtr->locAndCountEntriesPtr[iLocSet].locs, testSetPtr->locAndCountEntriesPtr[iLocSet].locCount, + locsToUse, kMaxLocalizationsToUse, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_localizationsToUse testSet %d, locSet %d, lang %s, status %s\n", + testSetPtr-altuTestSets, iLocSet+1, language, u_errorName(status)); + } else if (numLocsToUse == 0 && expLocsForSet != NULL) { + printStringArray(expLocsForSet, -1, printExpected, kPrintArrayBufSize); + log_err("FAIL: ualoc_localizationsToUse testSet %d, locSet %d, lang %s, expect {%s}, get no results\n", + testSetPtr-altuTestSets, iLocSet+1, language, printExpected); + } else if (numLocsToUse > 0 && expLocsForSet == NULL) { + printStringArray(locsToUse, numLocsToUse, printActual, kPrintArrayBufSize); + log_err("FAIL: ualoc_localizationsToUse testSet %d, locSet %d, lang %s, expect no results, get {%s}\n", + testSetPtr-altuTestSets, iLocSet+1, language, printActual); + } else if (numLocsToUse > 0 && !equalStringArrays(expLocsForSet, -1, locsToUse, numLocsToUse)) { + printStringArray(expLocsForSet, -1, printExpected, kPrintArrayBufSize); + printStringArray(locsToUse, numLocsToUse, printActual, kPrintArrayBufSize); + log_err("FAIL: ualoc_localizationsToUse testSet %d, locSet %d, lang %s:\n expect {%s}\n get {%s}\n", + testSetPtr-altuTestSets, iLocSet+1, language, printExpected, printActual); + } + } + } + } + + for (multiSetPtr = multiTestSets; multiSetPtr->name != NULL; multiSetPtr++) { + status = U_ZERO_ERROR; + numLocsToUse = ualoc_localizationsToUse(multiSetPtr->prefLangs, multiSetPtr->prefLangsCount, multiSetPtr->availLocs, multiSetPtr->availLocsCount, locsToUse, kMaxLocalizationsToUse, &status); + if (U_FAILURE(status)) { + log_err("FAIL: ualoc_localizationsToUse appleLocs%s, langs prefLangs%s, status %s\n", multiSetPtr->name, multiSetPtr->name, u_errorName(status)); + } else if (!equalStringArrays(multiSetPtr->locsToUse, multiSetPtr->locsToUseCount, locsToUse, numLocsToUse)) { + printStringArray(multiSetPtr->locsToUse, multiSetPtr->locsToUseCount, printExpected, kPrintArrayBufSize); + printStringArray(locsToUse, numLocsToUse, printActual, kPrintArrayBufSize); + log_err("FAIL: ualoc_localizationsToUse appleLocs%s, langs prefLangs%s:\n expect {%s}\n get {%s}\n", + multiSetPtr->name, multiSetPtr->name, printExpected, printActual); + } + } }