12e5b6d6dSopenharmony_ci// © 2019 and later: Unicode, Inc. and others.
22e5b6d6dSopenharmony_ci// License & terms of use: http://www.unicode.org/copyright.html
32e5b6d6dSopenharmony_ci
42e5b6d6dSopenharmony_ci// localematchertest.cpp
52e5b6d6dSopenharmony_ci// created: 2019jul04 Markus W. Scherer
62e5b6d6dSopenharmony_ci
72e5b6d6dSopenharmony_ci#include <string>
82e5b6d6dSopenharmony_ci#include <vector>
92e5b6d6dSopenharmony_ci#include <utility>
102e5b6d6dSopenharmony_ci
112e5b6d6dSopenharmony_ci#include "unicode/utypes.h"
122e5b6d6dSopenharmony_ci#include "unicode/localematcher.h"
132e5b6d6dSopenharmony_ci#include "unicode/locid.h"
142e5b6d6dSopenharmony_ci#include "charstr.h"
152e5b6d6dSopenharmony_ci#include "cmemory.h"
162e5b6d6dSopenharmony_ci#include "intltest.h"
172e5b6d6dSopenharmony_ci#include "localeprioritylist.h"
182e5b6d6dSopenharmony_ci#include "ucbuf.h"
192e5b6d6dSopenharmony_ci
202e5b6d6dSopenharmony_ci#define ARRAY_RANGE(array) (array), ((array) + UPRV_LENGTHOF(array))
212e5b6d6dSopenharmony_ci
222e5b6d6dSopenharmony_cinamespace {
232e5b6d6dSopenharmony_ci
242e5b6d6dSopenharmony_ciconst char *locString(const Locale *loc) {
252e5b6d6dSopenharmony_ci    return loc != nullptr ? loc->getName() : "(null)";
262e5b6d6dSopenharmony_ci}
272e5b6d6dSopenharmony_ci
282e5b6d6dSopenharmony_cistruct TestCase {
292e5b6d6dSopenharmony_ci    int32_t lineNr = 0;
302e5b6d6dSopenharmony_ci
312e5b6d6dSopenharmony_ci    CharString supported;
322e5b6d6dSopenharmony_ci    CharString def;
332e5b6d6dSopenharmony_ci    UnicodeString favor;
342e5b6d6dSopenharmony_ci    UnicodeString threshold;
352e5b6d6dSopenharmony_ci    CharString desired;
362e5b6d6dSopenharmony_ci    CharString expMatch;
372e5b6d6dSopenharmony_ci    CharString expDesired;
382e5b6d6dSopenharmony_ci    CharString expCombined;
392e5b6d6dSopenharmony_ci
402e5b6d6dSopenharmony_ci    void reset() {
412e5b6d6dSopenharmony_ci        supported.clear();
422e5b6d6dSopenharmony_ci        def.clear();
432e5b6d6dSopenharmony_ci        favor.remove();
442e5b6d6dSopenharmony_ci        threshold.remove();
452e5b6d6dSopenharmony_ci    }
462e5b6d6dSopenharmony_ci};
472e5b6d6dSopenharmony_ci
482e5b6d6dSopenharmony_ci}  // namespace
492e5b6d6dSopenharmony_ci
502e5b6d6dSopenharmony_ciclass LocaleMatcherTest : public IntlTest {
512e5b6d6dSopenharmony_cipublic:
522e5b6d6dSopenharmony_ci    LocaleMatcherTest() {}
532e5b6d6dSopenharmony_ci
542e5b6d6dSopenharmony_ci    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL) override;
552e5b6d6dSopenharmony_ci
562e5b6d6dSopenharmony_ci    void testEmpty();
572e5b6d6dSopenharmony_ci    void testCopyErrorTo();
582e5b6d6dSopenharmony_ci    void testBasics();
592e5b6d6dSopenharmony_ci    void testSupportedDefault();
602e5b6d6dSopenharmony_ci    void testUnsupportedDefault();
612e5b6d6dSopenharmony_ci    void testNoDefault();
622e5b6d6dSopenharmony_ci    void testDemotion();
632e5b6d6dSopenharmony_ci    void testDirection();
642e5b6d6dSopenharmony_ci    void testMaxDistanceAndIsMatch();
652e5b6d6dSopenharmony_ci    void testMatch();
662e5b6d6dSopenharmony_ci    void testResolvedLocale();
672e5b6d6dSopenharmony_ci    void testDataDriven();
682e5b6d6dSopenharmony_ci
692e5b6d6dSopenharmony_ciprivate:
702e5b6d6dSopenharmony_ci    UBool dataDriven(const TestCase &test, IcuTestErrorCode &errorCode);
712e5b6d6dSopenharmony_ci};
722e5b6d6dSopenharmony_ci
732e5b6d6dSopenharmony_ciextern IntlTest *createLocaleMatcherTest() {
742e5b6d6dSopenharmony_ci    return new LocaleMatcherTest();
752e5b6d6dSopenharmony_ci}
762e5b6d6dSopenharmony_ci
772e5b6d6dSopenharmony_civoid LocaleMatcherTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
782e5b6d6dSopenharmony_ci    if(exec) {
792e5b6d6dSopenharmony_ci        logln("TestSuite LocaleMatcherTest: ");
802e5b6d6dSopenharmony_ci    }
812e5b6d6dSopenharmony_ci    TESTCASE_AUTO_BEGIN;
822e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testEmpty);
832e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testCopyErrorTo);
842e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testBasics);
852e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testSupportedDefault);
862e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testUnsupportedDefault);
872e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testNoDefault);
882e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testDemotion);
892e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testDirection);
902e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testMaxDistanceAndIsMatch);
912e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testMatch);
922e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testResolvedLocale);
932e5b6d6dSopenharmony_ci    TESTCASE_AUTO(testDataDriven);
942e5b6d6dSopenharmony_ci    TESTCASE_AUTO_END;
952e5b6d6dSopenharmony_ci}
962e5b6d6dSopenharmony_ci
972e5b6d6dSopenharmony_civoid LocaleMatcherTest::testEmpty() {
982e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testEmpty");
992e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode);
1002e5b6d6dSopenharmony_ci    const Locale *best = matcher.getBestMatch(Locale::getFrench(), errorCode);
1012e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(fr)", "(null)", locString(best));
1022e5b6d6dSopenharmony_ci    LocaleMatcher::Result result = matcher.getBestMatchResult("fr", errorCode);
1032e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(fr).des", "(null)", locString(result.getDesiredLocale()));
1042e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(fr).desIndex", -1, result.getDesiredIndex());
1052e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(fr).supp",
1062e5b6d6dSopenharmony_ci                 "(null)", locString(result.getSupportedLocale()));
1072e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(fr).suppIndex",
1082e5b6d6dSopenharmony_ci                 -1, result.getSupportedIndex());
1092e5b6d6dSopenharmony_ci}
1102e5b6d6dSopenharmony_ci
1112e5b6d6dSopenharmony_civoid LocaleMatcherTest::testCopyErrorTo() {
1122e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testCopyErrorTo");
1132e5b6d6dSopenharmony_ci    // The builder does not set any errors except out-of-memory.
1142e5b6d6dSopenharmony_ci    // Test what we can.
1152e5b6d6dSopenharmony_ci    LocaleMatcher::Builder builder;
1162e5b6d6dSopenharmony_ci    UErrorCode success = U_ZERO_ERROR;
1172e5b6d6dSopenharmony_ci    assertFalse("no error", builder.copyErrorTo(success));
1182e5b6d6dSopenharmony_ci    assertTrue("still success", U_SUCCESS(success));
1192e5b6d6dSopenharmony_ci    UErrorCode failure = U_INVALID_FORMAT_ERROR;
1202e5b6d6dSopenharmony_ci    assertTrue("failure passed in", builder.copyErrorTo(failure));
1212e5b6d6dSopenharmony_ci    assertEquals("same failure", U_INVALID_FORMAT_ERROR, failure);
1222e5b6d6dSopenharmony_ci}
1232e5b6d6dSopenharmony_ci
1242e5b6d6dSopenharmony_civoid LocaleMatcherTest::testBasics() {
1252e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testBasics");
1262e5b6d6dSopenharmony_ci    Locale locales[] = { "fr", "en_GB", "en" };
1272e5b6d6dSopenharmony_ci    {
1282e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
1292e5b6d6dSopenharmony_ci            setSupportedLocales(ARRAY_RANGE(locales)).build(errorCode);
1302e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
1312e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best));
1322e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
1332e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best));
1342e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
1352e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best));
1362e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
1372e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best));
1382e5b6d6dSopenharmony_ci    }
1392e5b6d6dSopenharmony_ci    // Code coverage: Variations of setting supported locales.
1402e5b6d6dSopenharmony_ci    {
1412e5b6d6dSopenharmony_ci        std::vector<Locale> locales{ "fr", "en_GB", "en" };
1422e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
1432e5b6d6dSopenharmony_ci            setSupportedLocales(locales.begin(), locales.end()).build(errorCode);
1442e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
1452e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best));
1462e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
1472e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best));
1482e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
1492e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best));
1502e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
1512e5b6d6dSopenharmony_ci        assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best));
1522e5b6d6dSopenharmony_ci    }
1532e5b6d6dSopenharmony_ci    {
1542e5b6d6dSopenharmony_ci        Locale::RangeIterator<Locale *> iter(ARRAY_RANGE(locales));
1552e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
1562e5b6d6dSopenharmony_ci            setSupportedLocales(iter).build(errorCode);
1572e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
1582e5b6d6dSopenharmony_ci        assertEquals("fromIter.getBestMatch(en_GB)", "en_GB", locString(best));
1592e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
1602e5b6d6dSopenharmony_ci        assertEquals("fromIter.getBestMatch(en_US)", "en", locString(best));
1612e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
1622e5b6d6dSopenharmony_ci        assertEquals("fromIter.getBestMatch(fr_FR)", "fr", locString(best));
1632e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
1642e5b6d6dSopenharmony_ci        assertEquals("fromIter.getBestMatch(ja_JP)", "fr", locString(best));
1652e5b6d6dSopenharmony_ci    }
1662e5b6d6dSopenharmony_ci    {
1672e5b6d6dSopenharmony_ci        Locale *pointers[] = { locales, locales + 1, locales + 2 };
1682e5b6d6dSopenharmony_ci        // Lambda with explicit reference return type to prevent copy-constructing a temporary
1692e5b6d6dSopenharmony_ci        // which would be destructed right away.
1702e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
1712e5b6d6dSopenharmony_ci            setSupportedLocalesViaConverter(
1722e5b6d6dSopenharmony_ci                ARRAY_RANGE(pointers), [](const Locale *p) -> const Locale & { return *p; }).
1732e5b6d6dSopenharmony_ci            build(errorCode);
1742e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
1752e5b6d6dSopenharmony_ci        assertEquals("viaConverter.getBestMatch(en_GB)", "en_GB", locString(best));
1762e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
1772e5b6d6dSopenharmony_ci        assertEquals("viaConverter.getBestMatch(en_US)", "en", locString(best));
1782e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
1792e5b6d6dSopenharmony_ci        assertEquals("viaConverter.getBestMatch(fr_FR)", "fr", locString(best));
1802e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
1812e5b6d6dSopenharmony_ci        assertEquals("viaConverter.getBestMatch(ja_JP)", "fr", locString(best));
1822e5b6d6dSopenharmony_ci    }
1832e5b6d6dSopenharmony_ci    {
1842e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
1852e5b6d6dSopenharmony_ci            addSupportedLocale(locales[0]).
1862e5b6d6dSopenharmony_ci            addSupportedLocale(locales[1]).
1872e5b6d6dSopenharmony_ci            addSupportedLocale(locales[2]).
1882e5b6d6dSopenharmony_ci            build(errorCode);
1892e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
1902e5b6d6dSopenharmony_ci        assertEquals("added.getBestMatch(en_GB)", "en_GB", locString(best));
1912e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
1922e5b6d6dSopenharmony_ci        assertEquals("added.getBestMatch(en_US)", "en", locString(best));
1932e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
1942e5b6d6dSopenharmony_ci        assertEquals("added.getBestMatch(fr_FR)", "fr", locString(best));
1952e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
1962e5b6d6dSopenharmony_ci        assertEquals("added.getBestMatch(ja_JP)", "fr", locString(best));
1972e5b6d6dSopenharmony_ci    }
1982e5b6d6dSopenharmony_ci    {
1992e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
2002e5b6d6dSopenharmony_ci            setSupportedLocalesFromListString(
2012e5b6d6dSopenharmony_ci                " el, fr;q=0.555555, en-GB ; q = 0.88  , el; q =0, en;q=0.88 , fr ").
2022e5b6d6dSopenharmony_ci            build(errorCode);
2032e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatchForListString("el, fr, fr;q=0, en-GB", errorCode);
2042e5b6d6dSopenharmony_ci        assertEquals("fromList.getBestMatch(en_GB)", "en_GB", locString(best));
2052e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
2062e5b6d6dSopenharmony_ci        assertEquals("fromList.getBestMatch(en_US)", "en", locString(best));
2072e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
2082e5b6d6dSopenharmony_ci        assertEquals("fromList.getBestMatch(fr_FR)", "fr", locString(best));
2092e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
2102e5b6d6dSopenharmony_ci        assertEquals("fromList.getBestMatch(ja_JP)", "fr", locString(best));
2112e5b6d6dSopenharmony_ci    }
2122e5b6d6dSopenharmony_ci    // more API coverage
2132e5b6d6dSopenharmony_ci    {
2142e5b6d6dSopenharmony_ci        LocalePriorityList list("fr, en-GB", errorCode);
2152e5b6d6dSopenharmony_ci        LocalePriorityList::Iterator iter(list.iterator());
2162e5b6d6dSopenharmony_ci        LocaleMatcher matcher = LocaleMatcher::Builder().
2172e5b6d6dSopenharmony_ci            setSupportedLocales(iter).
2182e5b6d6dSopenharmony_ci            addSupportedLocale(Locale::getEnglish()).
2192e5b6d6dSopenharmony_ci            setDefaultLocale(&Locale::getGerman()).
2202e5b6d6dSopenharmony_ci            build(errorCode);
2212e5b6d6dSopenharmony_ci        const Locale *best = matcher.getBestMatch("en_GB", errorCode);
2222e5b6d6dSopenharmony_ci        assertEquals("withDefault.getBestMatch(en_GB)", "en_GB", locString(best));
2232e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("en_US", errorCode);
2242e5b6d6dSopenharmony_ci        assertEquals("withDefault.getBestMatch(en_US)", "en", locString(best));
2252e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("fr_FR", errorCode);
2262e5b6d6dSopenharmony_ci        assertEquals("withDefault.getBestMatch(fr_FR)", "fr", locString(best));
2272e5b6d6dSopenharmony_ci        best = matcher.getBestMatch("ja_JP", errorCode);
2282e5b6d6dSopenharmony_ci        assertEquals("withDefault.getBestMatch(ja_JP)", "de", locString(best));
2292e5b6d6dSopenharmony_ci
2302e5b6d6dSopenharmony_ci        Locale desired("en_GB");  // distinct object from Locale.UK
2312e5b6d6dSopenharmony_ci        LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode);
2322e5b6d6dSopenharmony_ci        assertTrue("withDefault: exactly desired en-GB object",
2332e5b6d6dSopenharmony_ci                   &desired == result.getDesiredLocale());
2342e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-GB desired index", 0, result.getDesiredIndex());
2352e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-GB supported",
2362e5b6d6dSopenharmony_ci                     "en_GB", locString(result.getSupportedLocale()));
2372e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-GB supported index", 1, result.getSupportedIndex());
2382e5b6d6dSopenharmony_ci
2392e5b6d6dSopenharmony_ci        LocalePriorityList list2("ja-JP, en-US", errorCode);
2402e5b6d6dSopenharmony_ci        LocalePriorityList::Iterator iter2(list2.iterator());
2412e5b6d6dSopenharmony_ci        result = matcher.getBestMatchResult(iter2, errorCode);
2422e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP, en-US desired index", 1, result.getDesiredIndex());
2432e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP, en-US desired",
2442e5b6d6dSopenharmony_ci                     "en_US", locString(result.getDesiredLocale()));
2452e5b6d6dSopenharmony_ci
2462e5b6d6dSopenharmony_ci        desired = Locale("en", "US");  // distinct object from Locale.US
2472e5b6d6dSopenharmony_ci        result = matcher.getBestMatchResult(desired, errorCode);
2482e5b6d6dSopenharmony_ci        assertTrue("withDefault: exactly desired en-US object",
2492e5b6d6dSopenharmony_ci                   &desired == result.getDesiredLocale());
2502e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-US desired index", 0, result.getDesiredIndex());
2512e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-US supported", "en", locString(result.getSupportedLocale()));
2522e5b6d6dSopenharmony_ci        assertEquals("withDefault: en-US supported index", 2, result.getSupportedIndex());
2532e5b6d6dSopenharmony_ci
2542e5b6d6dSopenharmony_ci        result = matcher.getBestMatchResult("ja_JP", errorCode);
2552e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP desired", "(null)", locString(result.getDesiredLocale()));
2562e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP desired index", -1, result.getDesiredIndex());
2572e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP supported", "de", locString(result.getSupportedLocale()));
2582e5b6d6dSopenharmony_ci        assertEquals("withDefault: ja-JP supported index", -1, result.getSupportedIndex());
2592e5b6d6dSopenharmony_ci    }
2602e5b6d6dSopenharmony_ci}
2612e5b6d6dSopenharmony_ci
2622e5b6d6dSopenharmony_civoid LocaleMatcherTest::testSupportedDefault() {
2632e5b6d6dSopenharmony_ci    // The default locale is one of the supported locales.
2642e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testSupportedDefault");
2652e5b6d6dSopenharmony_ci    Locale locales[] = { "fr", "en_GB", "en" };
2662e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().
2672e5b6d6dSopenharmony_ci        setSupportedLocales(ARRAY_RANGE(locales)).
2682e5b6d6dSopenharmony_ci        setDefaultLocale(&locales[1]).
2692e5b6d6dSopenharmony_ci        build(errorCode);
2702e5b6d6dSopenharmony_ci    const Locale *best = matcher.getBestMatch("en_GB", errorCode);
2712e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_GB)", "en_GB", locString(best));
2722e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("en_US", errorCode);
2732e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_US)", "en", locString(best));
2742e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("fr_FR", errorCode);
2752e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(fr_FR)", "fr", locString(best));
2762e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("ja_JP", errorCode);
2772e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(ja_JP)", "en_GB", locString(best));
2782e5b6d6dSopenharmony_ci    LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode);
2792e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).supp",
2802e5b6d6dSopenharmony_ci                 "en_GB", locString(result.getSupportedLocale()));
2812e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).suppIndex",
2822e5b6d6dSopenharmony_ci                 -1, result.getSupportedIndex());
2832e5b6d6dSopenharmony_ci}
2842e5b6d6dSopenharmony_ci
2852e5b6d6dSopenharmony_civoid LocaleMatcherTest::testUnsupportedDefault() {
2862e5b6d6dSopenharmony_ci    // The default locale does not match any of the supported locales.
2872e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testUnsupportedDefault");
2882e5b6d6dSopenharmony_ci    Locale locales[] = { "fr", "en_GB", "en" };
2892e5b6d6dSopenharmony_ci    Locale def("de");
2902e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().
2912e5b6d6dSopenharmony_ci        setSupportedLocales(ARRAY_RANGE(locales)).
2922e5b6d6dSopenharmony_ci        setDefaultLocale(&def).
2932e5b6d6dSopenharmony_ci        build(errorCode);
2942e5b6d6dSopenharmony_ci    const Locale *best = matcher.getBestMatch("en_GB", errorCode);
2952e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_GB)", "en_GB", locString(best));
2962e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("en_US", errorCode);
2972e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_US)", "en", locString(best));
2982e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("fr_FR", errorCode);
2992e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(fr_FR)", "fr", locString(best));
3002e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("ja_JP", errorCode);
3012e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(ja_JP)", "de", locString(best));
3022e5b6d6dSopenharmony_ci    LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode);
3032e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).supp",
3042e5b6d6dSopenharmony_ci                 "de", locString(result.getSupportedLocale()));
3052e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).suppIndex",
3062e5b6d6dSopenharmony_ci                 -1, result.getSupportedIndex());
3072e5b6d6dSopenharmony_ci}
3082e5b6d6dSopenharmony_ci
3092e5b6d6dSopenharmony_civoid LocaleMatcherTest::testNoDefault() {
3102e5b6d6dSopenharmony_ci    // We want nullptr instead of any default locale.
3112e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testNoDefault");
3122e5b6d6dSopenharmony_ci    Locale locales[] = { "fr", "en_GB", "en" };
3132e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().
3142e5b6d6dSopenharmony_ci        setSupportedLocales(ARRAY_RANGE(locales)).
3152e5b6d6dSopenharmony_ci        setNoDefaultLocale().
3162e5b6d6dSopenharmony_ci        build(errorCode);
3172e5b6d6dSopenharmony_ci    const Locale *best = matcher.getBestMatch("en_GB", errorCode);
3182e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_GB)", "en_GB", locString(best));
3192e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("en_US", errorCode);
3202e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(en_US)", "en", locString(best));
3212e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("fr_FR", errorCode);
3222e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(fr_FR)", "fr", locString(best));
3232e5b6d6dSopenharmony_ci    best = matcher.getBestMatch("ja_JP", errorCode);
3242e5b6d6dSopenharmony_ci    assertEquals("getBestMatch(ja_JP)", "(null)", locString(best));
3252e5b6d6dSopenharmony_ci    LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode);
3262e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).supp",
3272e5b6d6dSopenharmony_ci                 "(null)", locString(result.getSupportedLocale()));
3282e5b6d6dSopenharmony_ci    assertEquals("getBestMatchResult(ja_JP).suppIndex",
3292e5b6d6dSopenharmony_ci                 -1, result.getSupportedIndex());
3302e5b6d6dSopenharmony_ci}
3312e5b6d6dSopenharmony_ci
3322e5b6d6dSopenharmony_civoid LocaleMatcherTest::testDemotion() {
3332e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testDemotion");
3342e5b6d6dSopenharmony_ci    Locale supported[] = { "fr", "de-CH", "it" };
3352e5b6d6dSopenharmony_ci    Locale desired[] = { "fr-CH", "de-CH", "it" };
3362e5b6d6dSopenharmony_ci    {
3372e5b6d6dSopenharmony_ci        LocaleMatcher noDemotion = LocaleMatcher::Builder().
3382e5b6d6dSopenharmony_ci            setSupportedLocales(ARRAY_RANGE(supported)).
3392e5b6d6dSopenharmony_ci            setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_NONE).build(errorCode);
3402e5b6d6dSopenharmony_ci        Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
3412e5b6d6dSopenharmony_ci        assertEquals("no demotion",
3422e5b6d6dSopenharmony_ci                     "de_CH", locString(noDemotion.getBestMatch(desiredIter, errorCode)));
3432e5b6d6dSopenharmony_ci    }
3442e5b6d6dSopenharmony_ci
3452e5b6d6dSopenharmony_ci    {
3462e5b6d6dSopenharmony_ci        LocaleMatcher regionDemotion = LocaleMatcher::Builder().
3472e5b6d6dSopenharmony_ci            setSupportedLocales(ARRAY_RANGE(supported)).
3482e5b6d6dSopenharmony_ci            setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_REGION).build(errorCode);
3492e5b6d6dSopenharmony_ci        Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
3502e5b6d6dSopenharmony_ci        assertEquals("region demotion",
3512e5b6d6dSopenharmony_ci                     "fr", locString(regionDemotion.getBestMatch(desiredIter, errorCode)));
3522e5b6d6dSopenharmony_ci    }
3532e5b6d6dSopenharmony_ci}
3542e5b6d6dSopenharmony_ci
3552e5b6d6dSopenharmony_civoid LocaleMatcherTest::testDirection() {
3562e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testDirection");
3572e5b6d6dSopenharmony_ci    Locale supported[] = { "ar", "nn" };
3582e5b6d6dSopenharmony_ci    Locale desired[] = { "arz-EG", "nb-DK" };
3592e5b6d6dSopenharmony_ci    LocaleMatcher::Builder builder;
3602e5b6d6dSopenharmony_ci    builder.setSupportedLocales(ARRAY_RANGE(supported));
3612e5b6d6dSopenharmony_ci    {
3622e5b6d6dSopenharmony_ci        // arz is a close one-way match to ar, and the region matches.
3632e5b6d6dSopenharmony_ci        // (Egyptian Arabic vs. Arabic)
3642e5b6d6dSopenharmony_ci        // Also explicitly exercise the move copy constructor.
3652e5b6d6dSopenharmony_ci        LocaleMatcher built = builder.build(errorCode);
3662e5b6d6dSopenharmony_ci        LocaleMatcher withOneWay(std::move(built));
3672e5b6d6dSopenharmony_ci        Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
3682e5b6d6dSopenharmony_ci        assertEquals("with one-way", "ar",
3692e5b6d6dSopenharmony_ci                     locString(withOneWay.getBestMatch(desiredIter, errorCode)));
3702e5b6d6dSopenharmony_ci    }
3712e5b6d6dSopenharmony_ci    {
3722e5b6d6dSopenharmony_ci        // nb is a less close two-way match to nn, and the regions differ.
3732e5b6d6dSopenharmony_ci        // (Norwegian Bokmal vs. Nynorsk)
3742e5b6d6dSopenharmony_ci        // Also explicitly exercise the move assignment operator.
3752e5b6d6dSopenharmony_ci        LocaleMatcher onlyTwoWay = builder.build(errorCode);
3762e5b6d6dSopenharmony_ci        LocaleMatcher built =
3772e5b6d6dSopenharmony_ci            builder.setDirection(ULOCMATCH_DIRECTION_ONLY_TWO_WAY).build(errorCode);
3782e5b6d6dSopenharmony_ci        onlyTwoWay = std::move(built);
3792e5b6d6dSopenharmony_ci        Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
3802e5b6d6dSopenharmony_ci        assertEquals("only two-way", "nn",
3812e5b6d6dSopenharmony_ci                     locString(onlyTwoWay.getBestMatch(desiredIter, errorCode)));
3822e5b6d6dSopenharmony_ci    }
3832e5b6d6dSopenharmony_ci}
3842e5b6d6dSopenharmony_ci
3852e5b6d6dSopenharmony_civoid LocaleMatcherTest::testMaxDistanceAndIsMatch() {
3862e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testMaxDistanceAndIsMatch");
3872e5b6d6dSopenharmony_ci    LocaleMatcher::Builder builder;
3882e5b6d6dSopenharmony_ci    LocaleMatcher standard = builder.build(errorCode);
3892e5b6d6dSopenharmony_ci    Locale germanLux("de-LU");
3902e5b6d6dSopenharmony_ci    Locale germanPhoenician("de-Phnx-AT");
3912e5b6d6dSopenharmony_ci    Locale greek("el");
3922e5b6d6dSopenharmony_ci    assertTrue("standard de-LU / de", standard.isMatch(germanLux, Locale::getGerman(), errorCode));
3932e5b6d6dSopenharmony_ci    assertFalse("standard de-Phnx-AT / de",
3942e5b6d6dSopenharmony_ci                standard.isMatch(germanPhoenician, Locale::getGerman(), errorCode));
3952e5b6d6dSopenharmony_ci
3962e5b6d6dSopenharmony_ci    // Allow a script difference to still match.
3972e5b6d6dSopenharmony_ci    LocaleMatcher loose =
3982e5b6d6dSopenharmony_ci        builder.setMaxDistance(germanPhoenician, Locale::getGerman()).build(errorCode);
3992e5b6d6dSopenharmony_ci    assertTrue("loose de-LU / de", loose.isMatch(germanLux, Locale::getGerman(), errorCode));
4002e5b6d6dSopenharmony_ci    assertTrue("loose de-Phnx-AT / de",
4012e5b6d6dSopenharmony_ci               loose.isMatch(germanPhoenician, Locale::getGerman(), errorCode));
4022e5b6d6dSopenharmony_ci    assertFalse("loose el / de", loose.isMatch(greek, Locale::getGerman(), errorCode));
4032e5b6d6dSopenharmony_ci
4042e5b6d6dSopenharmony_ci    // Allow at most a regional difference.
4052e5b6d6dSopenharmony_ci    LocaleMatcher regional =
4062e5b6d6dSopenharmony_ci        builder.setMaxDistance(Locale("de-AT"), Locale::getGerman()).build(errorCode);
4072e5b6d6dSopenharmony_ci    assertTrue("regional de-LU / de",
4082e5b6d6dSopenharmony_ci               regional.isMatch(Locale("de-LU"), Locale::getGerman(), errorCode));
4092e5b6d6dSopenharmony_ci    assertFalse("regional da / no", regional.isMatch(Locale("da"), Locale("no"), errorCode));
4102e5b6d6dSopenharmony_ci    assertFalse("regional zh-Hant / zh",
4112e5b6d6dSopenharmony_ci                regional.isMatch(Locale::getChinese(), Locale::getTraditionalChinese(), errorCode));
4122e5b6d6dSopenharmony_ci}
4132e5b6d6dSopenharmony_ci
4142e5b6d6dSopenharmony_ci
4152e5b6d6dSopenharmony_civoid LocaleMatcherTest::testMatch() {
4162e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testMatch");
4172e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode);
4182e5b6d6dSopenharmony_ci
4192e5b6d6dSopenharmony_ci    // Java test function testMatch_exact()
4202e5b6d6dSopenharmony_ci    Locale en_CA("en_CA");
4212e5b6d6dSopenharmony_ci    assertEquals("exact match", 1.0, matcher.internalMatch(en_CA, en_CA, errorCode));
4222e5b6d6dSopenharmony_ci
4232e5b6d6dSopenharmony_ci    // testMatch_none
4242e5b6d6dSopenharmony_ci    Locale ar_MK("ar_MK");
4252e5b6d6dSopenharmony_ci    double match = matcher.internalMatch(ar_MK, en_CA, errorCode);
4262e5b6d6dSopenharmony_ci    assertTrue("mismatch: 0<=match<0.2", 0 <= match && match < 0.2);
4272e5b6d6dSopenharmony_ci
4282e5b6d6dSopenharmony_ci    // testMatch_matchOnMaximized
4292e5b6d6dSopenharmony_ci    Locale und_TW("und_TW");
4302e5b6d6dSopenharmony_ci    Locale zh("zh");
4312e5b6d6dSopenharmony_ci    Locale zh_Hant("zh_Hant");
4322e5b6d6dSopenharmony_ci    double matchZh = matcher.internalMatch(und_TW, zh, errorCode);
4332e5b6d6dSopenharmony_ci    double matchZhHant = matcher.internalMatch(und_TW, zh_Hant, errorCode);
4342e5b6d6dSopenharmony_ci    assertTrue("und_TW should be closer to zh_Hant than to zh",
4352e5b6d6dSopenharmony_ci               matchZh < matchZhHant);
4362e5b6d6dSopenharmony_ci    Locale en_Hant_TW("en_Hant_TW");
4372e5b6d6dSopenharmony_ci    double matchEnHantTw = matcher.internalMatch(en_Hant_TW, zh_Hant, errorCode);
4382e5b6d6dSopenharmony_ci    assertTrue("zh_Hant should be closer to und_TW than to en_Hant_TW",
4392e5b6d6dSopenharmony_ci               matchEnHantTw < matchZhHant);
4402e5b6d6dSopenharmony_ci    assertTrue("zh should not match und_TW or en_Hant_TW",
4412e5b6d6dSopenharmony_ci               matchZh == 0.0 && matchEnHantTw == 0.0); // with changes in CLDR-1435
4422e5b6d6dSopenharmony_ci}
4432e5b6d6dSopenharmony_ci
4442e5b6d6dSopenharmony_civoid LocaleMatcherTest::testResolvedLocale() {
4452e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testResolvedLocale");
4462e5b6d6dSopenharmony_ci    LocaleMatcher matcher = LocaleMatcher::Builder().
4472e5b6d6dSopenharmony_ci        addSupportedLocale("ar-EG").
4482e5b6d6dSopenharmony_ci        build(errorCode);
4492e5b6d6dSopenharmony_ci    Locale desired("ar-SA-u-nu-latn");
4502e5b6d6dSopenharmony_ci    LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode);
4512e5b6d6dSopenharmony_ci    assertEquals("best", "ar_EG", locString(result.getSupportedLocale()));
4522e5b6d6dSopenharmony_ci    Locale resolved = result.makeResolvedLocale(errorCode);
4532e5b6d6dSopenharmony_ci    assertEquals("ar-EG + ar-SA-u-nu-latn = ar-SA-u-nu-latn",
4542e5b6d6dSopenharmony_ci                 "ar-SA-u-nu-latn",
4552e5b6d6dSopenharmony_ci                 resolved.toLanguageTag<std::string>(errorCode).data());
4562e5b6d6dSopenharmony_ci}
4572e5b6d6dSopenharmony_ci
4582e5b6d6dSopenharmony_cinamespace {
4592e5b6d6dSopenharmony_ci
4602e5b6d6dSopenharmony_cibool toInvariant(const UnicodeString &s, CharString &inv, ErrorCode &errorCode) {
4612e5b6d6dSopenharmony_ci    if (errorCode.isSuccess()) {
4622e5b6d6dSopenharmony_ci        inv.clear().appendInvariantChars(s, errorCode);
4632e5b6d6dSopenharmony_ci        return errorCode.isSuccess();
4642e5b6d6dSopenharmony_ci    }
4652e5b6d6dSopenharmony_ci    return false;
4662e5b6d6dSopenharmony_ci}
4672e5b6d6dSopenharmony_ci
4682e5b6d6dSopenharmony_cibool getSuffixAfterPrefix(const UnicodeString &s, int32_t limit,
4692e5b6d6dSopenharmony_ci                          const UnicodeString &prefix, UnicodeString &suffix) {
4702e5b6d6dSopenharmony_ci    if (prefix.length() <= limit && s.startsWith(prefix)) {
4712e5b6d6dSopenharmony_ci        suffix.setTo(s, prefix.length(), limit - prefix.length());
4722e5b6d6dSopenharmony_ci        return true;
4732e5b6d6dSopenharmony_ci    } else {
4742e5b6d6dSopenharmony_ci        return false;
4752e5b6d6dSopenharmony_ci    }
4762e5b6d6dSopenharmony_ci}
4772e5b6d6dSopenharmony_ci
4782e5b6d6dSopenharmony_cibool getInvariantSuffixAfterPrefix(const UnicodeString &s, int32_t limit,
4792e5b6d6dSopenharmony_ci                                   const UnicodeString &prefix, CharString &suffix,
4802e5b6d6dSopenharmony_ci                                   ErrorCode &errorCode) {
4812e5b6d6dSopenharmony_ci    UnicodeString u_suffix;
4822e5b6d6dSopenharmony_ci    return getSuffixAfterPrefix(s, limit, prefix, u_suffix) &&
4832e5b6d6dSopenharmony_ci        toInvariant(u_suffix, suffix, errorCode);
4842e5b6d6dSopenharmony_ci}
4852e5b6d6dSopenharmony_ci
4862e5b6d6dSopenharmony_cibool readTestCase(const UnicodeString &line, TestCase &test, IcuTestErrorCode &errorCode) {
4872e5b6d6dSopenharmony_ci    if (errorCode.isFailure()) { return false; }
4882e5b6d6dSopenharmony_ci    ++test.lineNr;
4892e5b6d6dSopenharmony_ci    // Start of comment, or end of line, minus trailing spaces.
4902e5b6d6dSopenharmony_ci    int32_t limit = line.indexOf(u'#');
4912e5b6d6dSopenharmony_ci    if (limit < 0) {
4922e5b6d6dSopenharmony_ci        limit = line.length();
4932e5b6d6dSopenharmony_ci        // Remove trailing CR LF.
4942e5b6d6dSopenharmony_ci        char16_t c;
4952e5b6d6dSopenharmony_ci        while (limit > 0 && ((c = line.charAt(limit - 1)) == u'\n' || c == u'\r')) {
4962e5b6d6dSopenharmony_ci            --limit;
4972e5b6d6dSopenharmony_ci        }
4982e5b6d6dSopenharmony_ci    }
4992e5b6d6dSopenharmony_ci    // Remove spaces before comment or at the end of the line.
5002e5b6d6dSopenharmony_ci    char16_t c;
5012e5b6d6dSopenharmony_ci    while (limit > 0 && ((c = line.charAt(limit - 1)) == u' ' || c == u'\t')) {
5022e5b6d6dSopenharmony_ci        --limit;
5032e5b6d6dSopenharmony_ci    }
5042e5b6d6dSopenharmony_ci    if (limit == 0) {  // empty line
5052e5b6d6dSopenharmony_ci        return false;
5062e5b6d6dSopenharmony_ci    }
5072e5b6d6dSopenharmony_ci    if (line.startsWith(u"** test: ")) {
5082e5b6d6dSopenharmony_ci        test.reset();
5092e5b6d6dSopenharmony_ci    } else if (getInvariantSuffixAfterPrefix(line, limit, u"@supported=",
5102e5b6d6dSopenharmony_ci                                             test.supported, errorCode)) {
5112e5b6d6dSopenharmony_ci    } else if (getInvariantSuffixAfterPrefix(line, limit, u"@default=",
5122e5b6d6dSopenharmony_ci                                             test.def, errorCode)) {
5132e5b6d6dSopenharmony_ci    } else if (getSuffixAfterPrefix(line, limit, u"@favor=", test.favor)) {
5142e5b6d6dSopenharmony_ci    } else if (getSuffixAfterPrefix(line, limit, u"@threshold=", test.threshold)) {
5152e5b6d6dSopenharmony_ci    } else {
5162e5b6d6dSopenharmony_ci        int32_t matchSep = line.indexOf(u">>");
5172e5b6d6dSopenharmony_ci        // >> before an inline comment, and followed by more than white space.
5182e5b6d6dSopenharmony_ci        if (0 <= matchSep && (matchSep + 2) < limit) {
5192e5b6d6dSopenharmony_ci            toInvariant(line.tempSubStringBetween(0, matchSep).trim(), test.desired, errorCode);
5202e5b6d6dSopenharmony_ci            test.expDesired.clear();
5212e5b6d6dSopenharmony_ci            test.expCombined.clear();
5222e5b6d6dSopenharmony_ci            int32_t start = matchSep + 2;
5232e5b6d6dSopenharmony_ci            int32_t expLimit = line.indexOf(u'|', start);
5242e5b6d6dSopenharmony_ci            if (expLimit < 0) {
5252e5b6d6dSopenharmony_ci                toInvariant(line.tempSubStringBetween(start, limit).trim(),
5262e5b6d6dSopenharmony_ci                            test.expMatch, errorCode);
5272e5b6d6dSopenharmony_ci            } else {
5282e5b6d6dSopenharmony_ci                toInvariant(line.tempSubStringBetween(start, expLimit).trim(),
5292e5b6d6dSopenharmony_ci                            test.expMatch, errorCode);
5302e5b6d6dSopenharmony_ci                start = expLimit + 1;
5312e5b6d6dSopenharmony_ci                expLimit = line.indexOf(u'|', start);
5322e5b6d6dSopenharmony_ci                if (expLimit < 0) {
5332e5b6d6dSopenharmony_ci                    toInvariant(line.tempSubStringBetween(start, limit).trim(),
5342e5b6d6dSopenharmony_ci                                test.expDesired, errorCode);
5352e5b6d6dSopenharmony_ci                } else {
5362e5b6d6dSopenharmony_ci                    toInvariant(line.tempSubStringBetween(start, expLimit).trim(),
5372e5b6d6dSopenharmony_ci                                test.expDesired, errorCode);
5382e5b6d6dSopenharmony_ci                    toInvariant(line.tempSubStringBetween(expLimit + 1, limit).trim(),
5392e5b6d6dSopenharmony_ci                                test.expCombined, errorCode);
5402e5b6d6dSopenharmony_ci                }
5412e5b6d6dSopenharmony_ci            }
5422e5b6d6dSopenharmony_ci            return errorCode.isSuccess();
5432e5b6d6dSopenharmony_ci        } else {
5442e5b6d6dSopenharmony_ci            errorCode.set(U_INVALID_FORMAT_ERROR);
5452e5b6d6dSopenharmony_ci        }
5462e5b6d6dSopenharmony_ci    }
5472e5b6d6dSopenharmony_ci    return false;
5482e5b6d6dSopenharmony_ci}
5492e5b6d6dSopenharmony_ci
5502e5b6d6dSopenharmony_ciLocale *getLocaleOrNull(const CharString &s, Locale &locale) {
5512e5b6d6dSopenharmony_ci    if (s == "null") {
5522e5b6d6dSopenharmony_ci        return nullptr;
5532e5b6d6dSopenharmony_ci    } else {
5542e5b6d6dSopenharmony_ci        return &(locale = Locale(s.data()));
5552e5b6d6dSopenharmony_ci    }
5562e5b6d6dSopenharmony_ci}
5572e5b6d6dSopenharmony_ci
5582e5b6d6dSopenharmony_ci}  // namespace
5592e5b6d6dSopenharmony_ci
5602e5b6d6dSopenharmony_ciUBool LocaleMatcherTest::dataDriven(const TestCase &test, IcuTestErrorCode &errorCode) {
5612e5b6d6dSopenharmony_ci    LocaleMatcher::Builder builder;
5622e5b6d6dSopenharmony_ci    builder.setSupportedLocalesFromListString(test.supported.toStringPiece());
5632e5b6d6dSopenharmony_ci    if (!test.def.isEmpty()) {
5642e5b6d6dSopenharmony_ci        Locale defaultLocale(test.def.data());
5652e5b6d6dSopenharmony_ci        builder.setDefaultLocale(&defaultLocale);
5662e5b6d6dSopenharmony_ci    }
5672e5b6d6dSopenharmony_ci    if (!test.favor.isEmpty()) {
5682e5b6d6dSopenharmony_ci        ULocMatchFavorSubtag favor;
5692e5b6d6dSopenharmony_ci        if (test.favor == u"normal") {
5702e5b6d6dSopenharmony_ci            favor = ULOCMATCH_FAVOR_LANGUAGE;
5712e5b6d6dSopenharmony_ci        } else if (test.favor == u"script") {
5722e5b6d6dSopenharmony_ci            favor = ULOCMATCH_FAVOR_SCRIPT;
5732e5b6d6dSopenharmony_ci        } else {
5742e5b6d6dSopenharmony_ci            errln(UnicodeString(u"unsupported FavorSubtag value ") + test.favor);
5752e5b6d6dSopenharmony_ci            return false;
5762e5b6d6dSopenharmony_ci        }
5772e5b6d6dSopenharmony_ci        builder.setFavorSubtag(favor);
5782e5b6d6dSopenharmony_ci    }
5792e5b6d6dSopenharmony_ci    if (!test.threshold.isEmpty()) {
5802e5b6d6dSopenharmony_ci        infoln("skipping test case on line %d with non-default threshold: not exposed via API",
5812e5b6d6dSopenharmony_ci               (int)test.lineNr);
5822e5b6d6dSopenharmony_ci        return true;
5832e5b6d6dSopenharmony_ci        // int32_t threshold = Integer.valueOf(test.threshold);
5842e5b6d6dSopenharmony_ci        // builder.internalSetThresholdDistance(threshold);
5852e5b6d6dSopenharmony_ci    }
5862e5b6d6dSopenharmony_ci    LocaleMatcher matcher = builder.build(errorCode);
5872e5b6d6dSopenharmony_ci    if (errorCode.errIfFailureAndReset("LocaleMatcher::Builder::build()")) {
5882e5b6d6dSopenharmony_ci        return false;
5892e5b6d6dSopenharmony_ci    }
5902e5b6d6dSopenharmony_ci
5912e5b6d6dSopenharmony_ci    Locale expMatchLocale("");
5922e5b6d6dSopenharmony_ci    Locale *expMatch = getLocaleOrNull(test.expMatch, expMatchLocale);
5932e5b6d6dSopenharmony_ci    if (test.expDesired.isEmpty() && test.expCombined.isEmpty()) {
5942e5b6d6dSopenharmony_ci        StringPiece desiredSP = test.desired.toStringPiece();
5952e5b6d6dSopenharmony_ci        const Locale *bestSupported = matcher.getBestMatchForListString(desiredSP, errorCode);
5962e5b6d6dSopenharmony_ci        if (!assertEquals("bestSupported from string",
5972e5b6d6dSopenharmony_ci                          locString(expMatch), locString(bestSupported))) {
5982e5b6d6dSopenharmony_ci            return false;
5992e5b6d6dSopenharmony_ci        }
6002e5b6d6dSopenharmony_ci        LocalePriorityList desired(test.desired.toStringPiece(), errorCode);
6012e5b6d6dSopenharmony_ci        LocalePriorityList::Iterator desiredIter = desired.iterator();
6022e5b6d6dSopenharmony_ci        if (desired.getLength() == 1) {
6032e5b6d6dSopenharmony_ci            const Locale &desiredLocale = desiredIter.next();
6042e5b6d6dSopenharmony_ci            bestSupported = matcher.getBestMatch(desiredLocale, errorCode);
6052e5b6d6dSopenharmony_ci            UBool ok = assertEquals("bestSupported from Locale",
6062e5b6d6dSopenharmony_ci                                    locString(expMatch), locString(bestSupported));
6072e5b6d6dSopenharmony_ci
6082e5b6d6dSopenharmony_ci            LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocale, errorCode);
6092e5b6d6dSopenharmony_ci            return ok & assertEquals("result.getSupportedLocale from Locale",
6102e5b6d6dSopenharmony_ci                                     locString(expMatch), locString(result.getSupportedLocale()));
6112e5b6d6dSopenharmony_ci        } else {
6122e5b6d6dSopenharmony_ci            bestSupported = matcher.getBestMatch(desiredIter, errorCode);
6132e5b6d6dSopenharmony_ci            return assertEquals("bestSupported from Locale iterator",
6142e5b6d6dSopenharmony_ci                                locString(expMatch), locString(bestSupported));
6152e5b6d6dSopenharmony_ci        }
6162e5b6d6dSopenharmony_ci    } else {
6172e5b6d6dSopenharmony_ci        LocalePriorityList desired(test.desired.toStringPiece(), errorCode);
6182e5b6d6dSopenharmony_ci        LocalePriorityList::Iterator desiredIter = desired.iterator();
6192e5b6d6dSopenharmony_ci        LocaleMatcher::Result result = matcher.getBestMatchResult(desiredIter, errorCode);
6202e5b6d6dSopenharmony_ci        UBool ok = assertEquals("result.getSupportedLocale from Locales",
6212e5b6d6dSopenharmony_ci                                locString(expMatch), locString(result.getSupportedLocale()));
6222e5b6d6dSopenharmony_ci        if (!test.expDesired.isEmpty()) {
6232e5b6d6dSopenharmony_ci            Locale expDesiredLocale("");
6242e5b6d6dSopenharmony_ci            Locale *expDesired = getLocaleOrNull(test.expDesired, expDesiredLocale);
6252e5b6d6dSopenharmony_ci            ok &= assertEquals("result.getDesiredLocale from Locales",
6262e5b6d6dSopenharmony_ci                               locString(expDesired), locString(result.getDesiredLocale()));
6272e5b6d6dSopenharmony_ci        }
6282e5b6d6dSopenharmony_ci        if (!test.expCombined.isEmpty()) {
6292e5b6d6dSopenharmony_ci            if (test.expMatch.contains("-u-")) {
6302e5b6d6dSopenharmony_ci                logKnownIssue("20727",
6312e5b6d6dSopenharmony_ci                              UnicodeString(u"ignoring makeResolvedLocale() line ") + test.lineNr);
6322e5b6d6dSopenharmony_ci                return ok;
6332e5b6d6dSopenharmony_ci            }
6342e5b6d6dSopenharmony_ci            Locale expCombinedLocale("");
6352e5b6d6dSopenharmony_ci            Locale *expCombined = getLocaleOrNull(test.expCombined, expCombinedLocale);
6362e5b6d6dSopenharmony_ci            Locale combined = result.makeResolvedLocale(errorCode);
6372e5b6d6dSopenharmony_ci            ok &= assertEquals("combined Locale from Locales",
6382e5b6d6dSopenharmony_ci                               locString(expCombined), locString(&combined));
6392e5b6d6dSopenharmony_ci        }
6402e5b6d6dSopenharmony_ci        return ok;
6412e5b6d6dSopenharmony_ci    }
6422e5b6d6dSopenharmony_ci}
6432e5b6d6dSopenharmony_ci
6442e5b6d6dSopenharmony_civoid LocaleMatcherTest::testDataDriven() {
6452e5b6d6dSopenharmony_ci    IcuTestErrorCode errorCode(*this, "testDataDriven");
6462e5b6d6dSopenharmony_ci    CharString path(getSourceTestData(errorCode), errorCode);
6472e5b6d6dSopenharmony_ci    path.appendPathPart("localeMatcherTest.txt", errorCode);
6482e5b6d6dSopenharmony_ci    const char *codePage = "UTF-8";
6492e5b6d6dSopenharmony_ci    LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, true, false, errorCode));
6502e5b6d6dSopenharmony_ci    if(errorCode.errIfFailureAndReset("ucbuf_open(localeMatcherTest.txt)")) {
6512e5b6d6dSopenharmony_ci        return;
6522e5b6d6dSopenharmony_ci    }
6532e5b6d6dSopenharmony_ci    int32_t lineLength;
6542e5b6d6dSopenharmony_ci    const UChar *p;
6552e5b6d6dSopenharmony_ci    UnicodeString line;
6562e5b6d6dSopenharmony_ci    TestCase test;
6572e5b6d6dSopenharmony_ci    int32_t numPassed = 0;
6582e5b6d6dSopenharmony_ci    while ((p = ucbuf_readline(f.getAlias(), &lineLength, errorCode)) != nullptr &&
6592e5b6d6dSopenharmony_ci            errorCode.isSuccess()) {
6602e5b6d6dSopenharmony_ci        line.setTo(false, p, lineLength);
6612e5b6d6dSopenharmony_ci        if (!readTestCase(line, test, errorCode)) {
6622e5b6d6dSopenharmony_ci            if (errorCode.errIfFailureAndReset(
6632e5b6d6dSopenharmony_ci                    "test data syntax error on line %d", (int)test.lineNr)) {
6642e5b6d6dSopenharmony_ci                infoln(line);
6652e5b6d6dSopenharmony_ci            }
6662e5b6d6dSopenharmony_ci            continue;
6672e5b6d6dSopenharmony_ci        }
6682e5b6d6dSopenharmony_ci        UBool ok = dataDriven(test, errorCode);
6692e5b6d6dSopenharmony_ci        if (errorCode.errIfFailureAndReset("test error on line %d", (int)test.lineNr)) {
6702e5b6d6dSopenharmony_ci            infoln(line);
6712e5b6d6dSopenharmony_ci        } else if (!ok) {
6722e5b6d6dSopenharmony_ci            infoln("test failure on line %d", (int)test.lineNr);
6732e5b6d6dSopenharmony_ci            infoln(line);
6742e5b6d6dSopenharmony_ci        } else {
6752e5b6d6dSopenharmony_ci            ++numPassed;
6762e5b6d6dSopenharmony_ci        }
6772e5b6d6dSopenharmony_ci    }
6782e5b6d6dSopenharmony_ci    infoln("number of passing test cases: %d", (int)numPassed);
6792e5b6d6dSopenharmony_ci}
680