X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/a01113dcd0f39d5da295ef82785beff9ed86fe38..340931cb2e044a2141d11567dd0f782524e32994:/icuSources/test/intltest/localematchertest.cpp diff --git a/icuSources/test/intltest/localematchertest.cpp b/icuSources/test/intltest/localematchertest.cpp new file mode 100644 index 00000000..f8cb7a31 --- /dev/null +++ b/icuSources/test/intltest/localematchertest.cpp @@ -0,0 +1,589 @@ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License + +// localematchertest.cpp +// created: 2019jul04 Markus W. Scherer + +#include +#include + +#include "unicode/utypes.h" +#include "unicode/localematcher.h" +#include "unicode/locid.h" +#include "charstr.h" +#include "cmemory.h" +#include "intltest.h" +#include "localeprioritylist.h" +#include "ucbuf.h" + +#define ARRAY_RANGE(array) (array), ((array) + UPRV_LENGTHOF(array)) + +namespace { + +const char *locString(const Locale *loc) { + return loc != nullptr ? loc->getName() : "(null)"; +} + +struct TestCase { + int32_t lineNr = 0; + + CharString supported; + CharString def; + UnicodeString favor; + UnicodeString threshold; + CharString desired; + CharString expMatch; + CharString expDesired; + CharString expCombined; + + void reset() { + supported.clear(); + def.clear(); + favor.remove(); + threshold.remove(); + } +}; + +} // namespace + +class LocaleMatcherTest : public IntlTest { +public: + LocaleMatcherTest() {} + + void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL); + + void testEmpty(); + void testCopyErrorTo(); + void testBasics(); + void testSupportedDefault(); + void testUnsupportedDefault(); + void testDemotion(); + void testMatch(); + void testResolvedLocale(); + void testDataDriven(); + +private: + UBool dataDriven(const TestCase &test, IcuTestErrorCode &errorCode); +}; + +extern IntlTest *createLocaleMatcherTest() { + return new LocaleMatcherTest(); +} + +void LocaleMatcherTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) { + if(exec) { + logln("TestSuite LocaleMatcherTest: "); + } + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO(testEmpty); + TESTCASE_AUTO(testCopyErrorTo); + TESTCASE_AUTO(testBasics); + TESTCASE_AUTO(testSupportedDefault); + TESTCASE_AUTO(testUnsupportedDefault); + TESTCASE_AUTO(testDemotion); + TESTCASE_AUTO(testMatch); + TESTCASE_AUTO(testResolvedLocale); + TESTCASE_AUTO(testDataDriven); + TESTCASE_AUTO_END; +} + +void LocaleMatcherTest::testEmpty() { + IcuTestErrorCode errorCode(*this, "testEmpty"); + LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode); + const Locale *best = matcher.getBestMatch(Locale::getFrench(), errorCode); + assertEquals("getBestMatch(fr)", "(null)", locString(best)); + LocaleMatcher::Result result = matcher.getBestMatchResult("fr", errorCode); + assertEquals("getBestMatchResult(fr).des", "(null)", locString(result.getDesiredLocale())); + assertEquals("getBestMatchResult(fr).desIndex", -1, result.getDesiredIndex()); + assertEquals("getBestMatchResult(fr).supp", + "(null)", locString(result.getSupportedLocale())); + assertEquals("getBestMatchResult(fr).suppIndex", + -1, result.getSupportedIndex()); +} + +void LocaleMatcherTest::testCopyErrorTo() { + IcuTestErrorCode errorCode(*this, "testCopyErrorTo"); + // The builder does not set any errors except out-of-memory. + // Test what we can. + LocaleMatcher::Builder builder; + UErrorCode success = U_ZERO_ERROR; + assertFalse("no error", builder.copyErrorTo(success)); + assertTrue("still success", U_SUCCESS(success)); + UErrorCode failure = U_INVALID_FORMAT_ERROR; + assertTrue("failure passed in", builder.copyErrorTo(failure)); + assertEquals("same failure", U_INVALID_FORMAT_ERROR, failure); +} + +void LocaleMatcherTest::testBasics() { + IcuTestErrorCode errorCode(*this, "testBasics"); + Locale locales[] = { "fr", "en_GB", "en" }; + { + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(ARRAY_RANGE(locales)).build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best)); + } + // Code coverage: Variations of setting supported locales. + { + std::vector locales{ "fr", "en_GB", "en" }; + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(locales.begin(), locales.end()).build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best)); + } + { + Locale::RangeIterator iter(ARRAY_RANGE(locales)); + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(iter).build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("fromIter.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("fromIter.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("fromIter.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("fromIter.getBestMatch(ja_JP)", "fr", locString(best)); + } + { + Locale *pointers[] = { locales, locales + 1, locales + 2 }; + // Lambda with explicit reference return type to prevent copy-constructing a temporary + // which would be destructed right away. + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocalesViaConverter( + ARRAY_RANGE(pointers), [](const Locale *p) -> const Locale & { return *p; }). + build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("viaConverter.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("viaConverter.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("viaConverter.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("viaConverter.getBestMatch(ja_JP)", "fr", locString(best)); + } + { + LocaleMatcher matcher = LocaleMatcher::Builder(). + addSupportedLocale(locales[0]). + addSupportedLocale(locales[1]). + addSupportedLocale(locales[2]). + build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("added.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("added.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("added.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("added.getBestMatch(ja_JP)", "fr", locString(best)); + } + { + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocalesFromListString( + " el, fr;q=0.555555, en-GB ; q = 0.88 , el; q =0, en;q=0.88 , fr "). + build(errorCode); + const Locale *best = matcher.getBestMatchForListString("el, fr, fr;q=0, en-GB", errorCode); + assertEquals("fromList.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("fromList.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("fromList.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("fromList.getBestMatch(ja_JP)", "fr", locString(best)); + } + // more API coverage + { + LocalePriorityList list("fr, en-GB", errorCode); + LocalePriorityList::Iterator iter(list.iterator()); + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(iter). + addSupportedLocale(Locale::getEnglish()). + setDefaultLocale(&Locale::getGerman()). + build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("withDefault.getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("withDefault.getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("withDefault.getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("withDefault.getBestMatch(ja_JP)", "de", locString(best)); + + Locale desired("en_GB"); // distinct object from Locale.UK + LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode); + assertTrue("withDefault: exactly desired en-GB object", + &desired == result.getDesiredLocale()); + assertEquals("withDefault: en-GB desired index", 0, result.getDesiredIndex()); + assertEquals("withDefault: en-GB supported", + "en_GB", locString(result.getSupportedLocale())); + assertEquals("withDefault: en-GB supported index", 1, result.getSupportedIndex()); + + LocalePriorityList list2("ja-JP, en-US", errorCode); + LocalePriorityList::Iterator iter2(list2.iterator()); + result = matcher.getBestMatchResult(iter2, errorCode); + assertEquals("withDefault: ja-JP, en-US desired index", 1, result.getDesiredIndex()); + assertEquals("withDefault: ja-JP, en-US desired", + "en_US", locString(result.getDesiredLocale())); + + desired = Locale("en", "US"); // distinct object from Locale.US + result = matcher.getBestMatchResult(desired, errorCode); + assertTrue("withDefault: exactly desired en-US object", + &desired == result.getDesiredLocale()); + assertEquals("withDefault: en-US desired index", 0, result.getDesiredIndex()); + assertEquals("withDefault: en-US supported", "en", locString(result.getSupportedLocale())); + assertEquals("withDefault: en-US supported index", 2, result.getSupportedIndex()); + + result = matcher.getBestMatchResult("ja_JP", errorCode); + assertEquals("withDefault: ja-JP desired", "(null)", locString(result.getDesiredLocale())); + assertEquals("withDefault: ja-JP desired index", -1, result.getDesiredIndex()); + assertEquals("withDefault: ja-JP supported", "de", locString(result.getSupportedLocale())); + assertEquals("withDefault: ja-JP supported index", -1, result.getSupportedIndex()); + } +} + +void LocaleMatcherTest::testSupportedDefault() { + // The default locale is one of the supported locales. + IcuTestErrorCode errorCode(*this, "testSupportedDefault"); + Locale locales[] = { "fr", "en_GB", "en" }; + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(ARRAY_RANGE(locales)). + setDefaultLocale(&locales[1]). + build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("getBestMatch(ja_JP)", "en_GB", locString(best)); + LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode); + assertEquals("getBestMatchResult(ja_JP).supp", + "en_GB", locString(result.getSupportedLocale())); + assertEquals("getBestMatchResult(ja_JP).suppIndex", + 1, result.getSupportedIndex()); +} + +void LocaleMatcherTest::testUnsupportedDefault() { + // The default locale does not match any of the supported locales. + IcuTestErrorCode errorCode(*this, "testUnsupportedDefault"); + Locale locales[] = { "fr", "en_GB", "en" }; + Locale def("de"); + LocaleMatcher matcher = LocaleMatcher::Builder(). + setSupportedLocales(ARRAY_RANGE(locales)). + setDefaultLocale(&def). + build(errorCode); + const Locale *best = matcher.getBestMatch("en_GB", errorCode); + assertEquals("getBestMatch(en_GB)", "en_GB", locString(best)); + best = matcher.getBestMatch("en_US", errorCode); + assertEquals("getBestMatch(en_US)", "en", locString(best)); + best = matcher.getBestMatch("fr_FR", errorCode); + assertEquals("getBestMatch(fr_FR)", "fr", locString(best)); + best = matcher.getBestMatch("ja_JP", errorCode); + assertEquals("getBestMatch(ja_JP)", "de", locString(best)); + LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode); + assertEquals("getBestMatchResult(ja_JP).supp", + "de", locString(result.getSupportedLocale())); + assertEquals("getBestMatchResult(ja_JP).suppIndex", + -1, result.getSupportedIndex()); +} + +void LocaleMatcherTest::testDemotion() { + IcuTestErrorCode errorCode(*this, "testDemotion"); + Locale supported[] = { "fr", "de-CH", "it" }; + Locale desired[] = { "fr-CH", "de-CH", "it" }; + { + LocaleMatcher noDemotion = LocaleMatcher::Builder(). + setSupportedLocales(ARRAY_RANGE(supported)). + setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_NONE).build(errorCode); + Locale::RangeIterator desiredIter(ARRAY_RANGE(desired)); + assertEquals("no demotion", + "de_CH", locString(noDemotion.getBestMatch(desiredIter, errorCode))); + } + + { + LocaleMatcher regionDemotion = LocaleMatcher::Builder(). + setSupportedLocales(ARRAY_RANGE(supported)). + setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_REGION).build(errorCode); + Locale::RangeIterator desiredIter(ARRAY_RANGE(desired)); + assertEquals("region demotion", + "fr", locString(regionDemotion.getBestMatch(desiredIter, errorCode))); + } +} + +void LocaleMatcherTest::testMatch() { + IcuTestErrorCode errorCode(*this, "testMatch"); + LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode); + + // Java test function testMatch_exact() + Locale en_CA("en_CA"); + assertEquals("exact match", 1.0, matcher.internalMatch(en_CA, en_CA, errorCode)); + + // testMatch_none + Locale ar_MK("ar_MK"); + double match = matcher.internalMatch(ar_MK, en_CA, errorCode); + assertTrue("mismatch: 0<=match<0.2", 0 <= match && match < 0.2); + + // testMatch_matchOnMaximized + Locale und_TW("und_TW"); + Locale zh("zh"); + Locale zh_Hant("zh_Hant"); + double matchZh = matcher.internalMatch(und_TW, zh, errorCode); + double matchZhHant = matcher.internalMatch(und_TW, zh_Hant, errorCode); + assertTrue("und_TW should be closer to zh_Hant than to zh", + matchZh < matchZhHant); + Locale en_Hant_TW("en_Hant_TW"); + double matchEnHantTw = matcher.internalMatch(en_Hant_TW, zh_Hant, errorCode); + assertTrue("zh_Hant should be closer to und_TW than to en_Hant_TW", + matchEnHantTw < matchZhHant); + assertTrue("zh should be closer to und_TW than to en_Hant_TW", + matchEnHantTw < matchZh); +} + +void LocaleMatcherTest::testResolvedLocale() { + IcuTestErrorCode errorCode(*this, "testResolvedLocale"); + LocaleMatcher matcher = LocaleMatcher::Builder(). + addSupportedLocale("ar-EG"). + build(errorCode); + Locale desired("ar-SA-u-nu-latn"); + LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode); + assertEquals("best", "ar_EG", locString(result.getSupportedLocale())); + Locale resolved = result.makeResolvedLocale(errorCode); + assertEquals("ar-EG + ar-SA-u-nu-latn = ar-SA-u-nu-latn", + "ar-SA-u-nu-latn", + resolved.toLanguageTag(errorCode).data()); +} + +namespace { + +bool toInvariant(const UnicodeString &s, CharString &inv, ErrorCode &errorCode) { + if (errorCode.isSuccess()) { + inv.clear().appendInvariantChars(s, errorCode); + return errorCode.isSuccess(); + } + return false; +} + +bool getSuffixAfterPrefix(const UnicodeString &s, int32_t limit, + const UnicodeString &prefix, UnicodeString &suffix) { + if (prefix.length() <= limit && s.startsWith(prefix)) { + suffix.setTo(s, prefix.length(), limit - prefix.length()); + return true; + } else { + return false; + } +} + +bool getInvariantSuffixAfterPrefix(const UnicodeString &s, int32_t limit, + const UnicodeString &prefix, CharString &suffix, + ErrorCode &errorCode) { + UnicodeString u_suffix; + return getSuffixAfterPrefix(s, limit, prefix, u_suffix) && + toInvariant(u_suffix, suffix, errorCode); +} + +bool readTestCase(const UnicodeString &line, TestCase &test, IcuTestErrorCode &errorCode) { + if (errorCode.isFailure()) { return false; } + ++test.lineNr; + // Start of comment, or end of line, minus trailing spaces. + int32_t limit = line.indexOf(u'#'); + if (limit < 0) { + limit = line.length(); + // Remove trailing CR LF. + char16_t c; + while (limit > 0 && ((c = line.charAt(limit - 1)) == u'\n' || c == u'\r')) { + --limit; + } + } + // Remove spaces before comment or at the end of the line. + char16_t c; + while (limit > 0 && ((c = line.charAt(limit - 1)) == u' ' || c == u'\t')) { + --limit; + } + if (limit == 0) { // empty line + return false; + } + if (line.startsWith(u"** test: ")) { + test.reset(); + } else if (getInvariantSuffixAfterPrefix(line, limit, u"@supported=", + test.supported, errorCode)) { + } else if (getInvariantSuffixAfterPrefix(line, limit, u"@default=", + test.def, errorCode)) { + } else if (getSuffixAfterPrefix(line, limit, u"@favor=", test.favor)) { + } else if (getSuffixAfterPrefix(line, limit, u"@threshold=", test.threshold)) { + } else { + int32_t matchSep = line.indexOf(u">>"); + // >> before an inline comment, and followed by more than white space. + if (0 <= matchSep && (matchSep + 2) < limit) { + toInvariant(line.tempSubStringBetween(0, matchSep).trim(), test.desired, errorCode); + test.expDesired.clear(); + test.expCombined.clear(); + int32_t start = matchSep + 2; + int32_t expLimit = line.indexOf(u'|', start); + if (expLimit < 0) { + toInvariant(line.tempSubStringBetween(start, limit).trim(), + test.expMatch, errorCode); + } else { + toInvariant(line.tempSubStringBetween(start, expLimit).trim(), + test.expMatch, errorCode); + start = expLimit + 1; + expLimit = line.indexOf(u'|', start); + if (expLimit < 0) { + toInvariant(line.tempSubStringBetween(start, limit).trim(), + test.expDesired, errorCode); + } else { + toInvariant(line.tempSubStringBetween(start, expLimit).trim(), + test.expDesired, errorCode); + toInvariant(line.tempSubStringBetween(expLimit + 1, limit).trim(), + test.expCombined, errorCode); + } + } + return errorCode.isSuccess(); + } else { + errorCode.set(U_INVALID_FORMAT_ERROR); + } + } + return false; +} + +Locale *getLocaleOrNull(const CharString &s, Locale &locale) { + if (s == "null") { + return nullptr; + } else { + return &(locale = Locale(s.data())); + } +} + +} // namespace + +UBool LocaleMatcherTest::dataDriven(const TestCase &test, IcuTestErrorCode &errorCode) { + LocaleMatcher::Builder builder; + builder.setSupportedLocalesFromListString(test.supported.toStringPiece()); + if (!test.def.isEmpty()) { + Locale defaultLocale(test.def.data()); + builder.setDefaultLocale(&defaultLocale); + } + if (!test.favor.isEmpty()) { + ULocMatchFavorSubtag favor; + if (test.favor == u"normal") { + favor = ULOCMATCH_FAVOR_LANGUAGE; + } else if (test.favor == u"script") { + favor = ULOCMATCH_FAVOR_SCRIPT; + } else { + errln(UnicodeString(u"unsupported FavorSubtag value ") + test.favor); + return FALSE; + } + builder.setFavorSubtag(favor); + } + if (!test.threshold.isEmpty()) { + infoln("skipping test case on line %d with non-default threshold: not exposed via API", + (int)test.lineNr); + return TRUE; + // int32_t threshold = Integer.valueOf(test.threshold); + // builder.internalSetThresholdDistance(threshold); + } + LocaleMatcher matcher = builder.build(errorCode); + if (errorCode.errIfFailureAndReset("LocaleMatcher::Builder::build()")) { + return FALSE; + } + + Locale expMatchLocale(""); + Locale *expMatch = getLocaleOrNull(test.expMatch, expMatchLocale); + if (test.expDesired.isEmpty() && test.expCombined.isEmpty()) { + StringPiece desiredSP = test.desired.toStringPiece(); + const Locale *bestSupported = matcher.getBestMatchForListString(desiredSP, errorCode); + if (!assertEquals("bestSupported from string", + locString(expMatch), locString(bestSupported))) { + return FALSE; + } + LocalePriorityList desired(test.desired.toStringPiece(), errorCode); + LocalePriorityList::Iterator desiredIter = desired.iterator(); + if (desired.getLength() == 1) { + const Locale &desiredLocale = desiredIter.next(); + bestSupported = matcher.getBestMatch(desiredLocale, errorCode); + UBool ok = assertEquals("bestSupported from Locale", + locString(expMatch), locString(bestSupported)); + + LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocale, errorCode); + return ok & assertEquals("result.getSupportedLocale from Locale", + locString(expMatch), locString(result.getSupportedLocale())); + } else { + bestSupported = matcher.getBestMatch(desiredIter, errorCode); + return assertEquals("bestSupported from Locale iterator", + locString(expMatch), locString(bestSupported)); + } + } else { + LocalePriorityList desired(test.desired.toStringPiece(), errorCode); + LocalePriorityList::Iterator desiredIter = desired.iterator(); + LocaleMatcher::Result result = matcher.getBestMatchResult(desiredIter, errorCode); + UBool ok = assertEquals("result.getSupportedLocale from Locales", + locString(expMatch), locString(result.getSupportedLocale())); + if (!test.expDesired.isEmpty()) { + Locale expDesiredLocale(""); + Locale *expDesired = getLocaleOrNull(test.expDesired, expDesiredLocale); + ok &= assertEquals("result.getDesiredLocale from Locales", + locString(expDesired), locString(result.getDesiredLocale())); + } + if (!test.expCombined.isEmpty()) { + if (test.expMatch.contains("-u-")) { + logKnownIssue("20727", + UnicodeString(u"ignoring makeResolvedLocale() line ") + test.lineNr); + return ok; + } + Locale expCombinedLocale(""); + Locale *expCombined = getLocaleOrNull(test.expCombined, expCombinedLocale); + Locale combined = result.makeResolvedLocale(errorCode); + ok &= assertEquals("combined Locale from Locales", + locString(expCombined), locString(&combined)); + } + return ok; + } +} + +void LocaleMatcherTest::testDataDriven() { + IcuTestErrorCode errorCode(*this, "testDataDriven"); + CharString path(getSourceTestData(errorCode), errorCode); + path.appendPathPart("localeMatcherTest.txt", errorCode); + const char *codePage = "UTF-8"; + LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, TRUE, FALSE, errorCode)); + if(errorCode.errIfFailureAndReset("ucbuf_open(localeMatcherTest.txt)")) { + return; + } + int32_t lineLength; + const UChar *p; + UnicodeString line; + TestCase test; + int32_t numPassed = 0; + while ((p = ucbuf_readline(f.getAlias(), &lineLength, errorCode)) != nullptr && + errorCode.isSuccess()) { + line.setTo(FALSE, p, lineLength); + if (!readTestCase(line, test, errorCode)) { + if (errorCode.errIfFailureAndReset( + "test data syntax error on line %d", (int)test.lineNr)) { + infoln(line); + } + continue; + } + UBool ok = dataDriven(test, errorCode); + if (errorCode.errIfFailureAndReset("test error on line %d", (int)test.lineNr)) { + infoln(line); + } else if (!ok) { + infoln("test failure on line %d", (int)test.lineNr); + infoln(line); + } else { + ++numPassed; + } + } + infoln("number of passing test cases: %d", (int)numPassed); +}