--- /dev/null
+// © 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 <string>
+#include <vector>
+
+#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<Locale> 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<Locale *> 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<Locale *> 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<Locale *> 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<std::string>(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);
+}