"Fossies" - the Fresh Open Source Software Archive

Member "icu/source/test/intltest/localematchertest.cpp" (22 Apr 2020, 27688 Bytes) of package /linux/misc/icu4c-67_1-src.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes reports for "localematchertest.cpp": 67rc_vs_67_1 or 66_1_vs_67_1.

    1 // © 2019 and later: Unicode, Inc. and others.
    2 // License & terms of use: http://www.unicode.org/copyright.html#License
    3 
    4 // localematchertest.cpp
    5 // created: 2019jul04 Markus W. Scherer
    6 
    7 #include <string>
    8 #include <vector>
    9 #include <utility>
   10 
   11 #include "unicode/utypes.h"
   12 #include "unicode/localematcher.h"
   13 #include "unicode/locid.h"
   14 #include "charstr.h"
   15 #include "cmemory.h"
   16 #include "intltest.h"
   17 #include "localeprioritylist.h"
   18 #include "ucbuf.h"
   19 
   20 #define ARRAY_RANGE(array) (array), ((array) + UPRV_LENGTHOF(array))
   21 
   22 namespace {
   23 
   24 const char *locString(const Locale *loc) {
   25     return loc != nullptr ? loc->getName() : "(null)";
   26 }
   27 
   28 struct TestCase {
   29     int32_t lineNr = 0;
   30 
   31     CharString supported;
   32     CharString def;
   33     UnicodeString favor;
   34     UnicodeString threshold;
   35     CharString desired;
   36     CharString expMatch;
   37     CharString expDesired;
   38     CharString expCombined;
   39 
   40     void reset() {
   41         supported.clear();
   42         def.clear();
   43         favor.remove();
   44         threshold.remove();
   45     }
   46 };
   47 
   48 }  // namespace
   49 
   50 class LocaleMatcherTest : public IntlTest {
   51 public:
   52     LocaleMatcherTest() {}
   53 
   54     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL);
   55 
   56     void testEmpty();
   57     void testCopyErrorTo();
   58     void testBasics();
   59     void testSupportedDefault();
   60     void testUnsupportedDefault();
   61     void testDemotion();
   62     void testDirection();
   63     void testMatch();
   64     void testResolvedLocale();
   65     void testDataDriven();
   66 
   67 private:
   68     UBool dataDriven(const TestCase &test, IcuTestErrorCode &errorCode);
   69 };
   70 
   71 extern IntlTest *createLocaleMatcherTest() {
   72     return new LocaleMatcherTest();
   73 }
   74 
   75 void LocaleMatcherTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
   76     if(exec) {
   77         logln("TestSuite LocaleMatcherTest: ");
   78     }
   79     TESTCASE_AUTO_BEGIN;
   80     TESTCASE_AUTO(testEmpty);
   81     TESTCASE_AUTO(testCopyErrorTo);
   82     TESTCASE_AUTO(testBasics);
   83     TESTCASE_AUTO(testSupportedDefault);
   84     TESTCASE_AUTO(testUnsupportedDefault);
   85     TESTCASE_AUTO(testDemotion);
   86     TESTCASE_AUTO(testDirection);
   87     TESTCASE_AUTO(testMatch);
   88     TESTCASE_AUTO(testResolvedLocale);
   89     TESTCASE_AUTO(testDataDriven);
   90     TESTCASE_AUTO_END;
   91 }
   92 
   93 void LocaleMatcherTest::testEmpty() {
   94     IcuTestErrorCode errorCode(*this, "testEmpty");
   95     LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode);
   96     const Locale *best = matcher.getBestMatch(Locale::getFrench(), errorCode);
   97     assertEquals("getBestMatch(fr)", "(null)", locString(best));
   98     LocaleMatcher::Result result = matcher.getBestMatchResult("fr", errorCode);
   99     assertEquals("getBestMatchResult(fr).des", "(null)", locString(result.getDesiredLocale()));
  100     assertEquals("getBestMatchResult(fr).desIndex", -1, result.getDesiredIndex());
  101     assertEquals("getBestMatchResult(fr).supp",
  102                  "(null)", locString(result.getSupportedLocale()));
  103     assertEquals("getBestMatchResult(fr).suppIndex",
  104                  -1, result.getSupportedIndex());
  105 }
  106 
  107 void LocaleMatcherTest::testCopyErrorTo() {
  108     IcuTestErrorCode errorCode(*this, "testCopyErrorTo");
  109     // The builder does not set any errors except out-of-memory.
  110     // Test what we can.
  111     LocaleMatcher::Builder builder;
  112     UErrorCode success = U_ZERO_ERROR;
  113     assertFalse("no error", builder.copyErrorTo(success));
  114     assertTrue("still success", U_SUCCESS(success));
  115     UErrorCode failure = U_INVALID_FORMAT_ERROR;
  116     assertTrue("failure passed in", builder.copyErrorTo(failure));
  117     assertEquals("same failure", U_INVALID_FORMAT_ERROR, failure);
  118 }
  119 
  120 void LocaleMatcherTest::testBasics() {
  121     IcuTestErrorCode errorCode(*this, "testBasics");
  122     Locale locales[] = { "fr", "en_GB", "en" };
  123     {
  124         LocaleMatcher matcher = LocaleMatcher::Builder().
  125             setSupportedLocales(ARRAY_RANGE(locales)).build(errorCode);
  126         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  127         assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best));
  128         best = matcher.getBestMatch("en_US", errorCode);
  129         assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best));
  130         best = matcher.getBestMatch("fr_FR", errorCode);
  131         assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best));
  132         best = matcher.getBestMatch("ja_JP", errorCode);
  133         assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best));
  134     }
  135     // Code coverage: Variations of setting supported locales.
  136     {
  137         std::vector<Locale> locales{ "fr", "en_GB", "en" };
  138         LocaleMatcher matcher = LocaleMatcher::Builder().
  139             setSupportedLocales(locales.begin(), locales.end()).build(errorCode);
  140         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  141         assertEquals("fromRange.getBestMatch(en_GB)", "en_GB", locString(best));
  142         best = matcher.getBestMatch("en_US", errorCode);
  143         assertEquals("fromRange.getBestMatch(en_US)", "en", locString(best));
  144         best = matcher.getBestMatch("fr_FR", errorCode);
  145         assertEquals("fromRange.getBestMatch(fr_FR)", "fr", locString(best));
  146         best = matcher.getBestMatch("ja_JP", errorCode);
  147         assertEquals("fromRange.getBestMatch(ja_JP)", "fr", locString(best));
  148     }
  149     {
  150         Locale::RangeIterator<Locale *> iter(ARRAY_RANGE(locales));
  151         LocaleMatcher matcher = LocaleMatcher::Builder().
  152             setSupportedLocales(iter).build(errorCode);
  153         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  154         assertEquals("fromIter.getBestMatch(en_GB)", "en_GB", locString(best));
  155         best = matcher.getBestMatch("en_US", errorCode);
  156         assertEquals("fromIter.getBestMatch(en_US)", "en", locString(best));
  157         best = matcher.getBestMatch("fr_FR", errorCode);
  158         assertEquals("fromIter.getBestMatch(fr_FR)", "fr", locString(best));
  159         best = matcher.getBestMatch("ja_JP", errorCode);
  160         assertEquals("fromIter.getBestMatch(ja_JP)", "fr", locString(best));
  161     }
  162     {
  163         Locale *pointers[] = { locales, locales + 1, locales + 2 };
  164         // Lambda with explicit reference return type to prevent copy-constructing a temporary
  165         // which would be destructed right away.
  166         LocaleMatcher matcher = LocaleMatcher::Builder().
  167             setSupportedLocalesViaConverter(
  168                 ARRAY_RANGE(pointers), [](const Locale *p) -> const Locale & { return *p; }).
  169             build(errorCode);
  170         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  171         assertEquals("viaConverter.getBestMatch(en_GB)", "en_GB", locString(best));
  172         best = matcher.getBestMatch("en_US", errorCode);
  173         assertEquals("viaConverter.getBestMatch(en_US)", "en", locString(best));
  174         best = matcher.getBestMatch("fr_FR", errorCode);
  175         assertEquals("viaConverter.getBestMatch(fr_FR)", "fr", locString(best));
  176         best = matcher.getBestMatch("ja_JP", errorCode);
  177         assertEquals("viaConverter.getBestMatch(ja_JP)", "fr", locString(best));
  178     }
  179     {
  180         LocaleMatcher matcher = LocaleMatcher::Builder().
  181             addSupportedLocale(locales[0]).
  182             addSupportedLocale(locales[1]).
  183             addSupportedLocale(locales[2]).
  184             build(errorCode);
  185         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  186         assertEquals("added.getBestMatch(en_GB)", "en_GB", locString(best));
  187         best = matcher.getBestMatch("en_US", errorCode);
  188         assertEquals("added.getBestMatch(en_US)", "en", locString(best));
  189         best = matcher.getBestMatch("fr_FR", errorCode);
  190         assertEquals("added.getBestMatch(fr_FR)", "fr", locString(best));
  191         best = matcher.getBestMatch("ja_JP", errorCode);
  192         assertEquals("added.getBestMatch(ja_JP)", "fr", locString(best));
  193     }
  194     {
  195         LocaleMatcher matcher = LocaleMatcher::Builder().
  196             setSupportedLocalesFromListString(
  197                 " el, fr;q=0.555555, en-GB ; q = 0.88  , el; q =0, en;q=0.88 , fr ").
  198             build(errorCode);
  199         const Locale *best = matcher.getBestMatchForListString("el, fr, fr;q=0, en-GB", errorCode);
  200         assertEquals("fromList.getBestMatch(en_GB)", "en_GB", locString(best));
  201         best = matcher.getBestMatch("en_US", errorCode);
  202         assertEquals("fromList.getBestMatch(en_US)", "en", locString(best));
  203         best = matcher.getBestMatch("fr_FR", errorCode);
  204         assertEquals("fromList.getBestMatch(fr_FR)", "fr", locString(best));
  205         best = matcher.getBestMatch("ja_JP", errorCode);
  206         assertEquals("fromList.getBestMatch(ja_JP)", "fr", locString(best));
  207     }
  208     // more API coverage
  209     {
  210         LocalePriorityList list("fr, en-GB", errorCode);
  211         LocalePriorityList::Iterator iter(list.iterator());
  212         LocaleMatcher matcher = LocaleMatcher::Builder().
  213             setSupportedLocales(iter).
  214             addSupportedLocale(Locale::getEnglish()).
  215             setDefaultLocale(&Locale::getGerman()).
  216             build(errorCode);
  217         const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  218         assertEquals("withDefault.getBestMatch(en_GB)", "en_GB", locString(best));
  219         best = matcher.getBestMatch("en_US", errorCode);
  220         assertEquals("withDefault.getBestMatch(en_US)", "en", locString(best));
  221         best = matcher.getBestMatch("fr_FR", errorCode);
  222         assertEquals("withDefault.getBestMatch(fr_FR)", "fr", locString(best));
  223         best = matcher.getBestMatch("ja_JP", errorCode);
  224         assertEquals("withDefault.getBestMatch(ja_JP)", "de", locString(best));
  225 
  226         Locale desired("en_GB");  // distinct object from Locale.UK
  227         LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode);
  228         assertTrue("withDefault: exactly desired en-GB object",
  229                    &desired == result.getDesiredLocale());
  230         assertEquals("withDefault: en-GB desired index", 0, result.getDesiredIndex());
  231         assertEquals("withDefault: en-GB supported",
  232                      "en_GB", locString(result.getSupportedLocale()));
  233         assertEquals("withDefault: en-GB supported index", 1, result.getSupportedIndex());
  234 
  235         LocalePriorityList list2("ja-JP, en-US", errorCode);
  236         LocalePriorityList::Iterator iter2(list2.iterator());
  237         result = matcher.getBestMatchResult(iter2, errorCode);
  238         assertEquals("withDefault: ja-JP, en-US desired index", 1, result.getDesiredIndex());
  239         assertEquals("withDefault: ja-JP, en-US desired",
  240                      "en_US", locString(result.getDesiredLocale()));
  241 
  242         desired = Locale("en", "US");  // distinct object from Locale.US
  243         result = matcher.getBestMatchResult(desired, errorCode);
  244         assertTrue("withDefault: exactly desired en-US object",
  245                    &desired == result.getDesiredLocale());
  246         assertEquals("withDefault: en-US desired index", 0, result.getDesiredIndex());
  247         assertEquals("withDefault: en-US supported", "en", locString(result.getSupportedLocale()));
  248         assertEquals("withDefault: en-US supported index", 2, result.getSupportedIndex());
  249 
  250         result = matcher.getBestMatchResult("ja_JP", errorCode);
  251         assertEquals("withDefault: ja-JP desired", "(null)", locString(result.getDesiredLocale()));
  252         assertEquals("withDefault: ja-JP desired index", -1, result.getDesiredIndex());
  253         assertEquals("withDefault: ja-JP supported", "de", locString(result.getSupportedLocale()));
  254         assertEquals("withDefault: ja-JP supported index", -1, result.getSupportedIndex());
  255     }
  256 }
  257 
  258 void LocaleMatcherTest::testSupportedDefault() {
  259     // The default locale is one of the supported locales.
  260     IcuTestErrorCode errorCode(*this, "testSupportedDefault");
  261     Locale locales[] = { "fr", "en_GB", "en" };
  262     LocaleMatcher matcher = LocaleMatcher::Builder().
  263         setSupportedLocales(ARRAY_RANGE(locales)).
  264         setDefaultLocale(&locales[1]).
  265         build(errorCode);
  266     const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  267     assertEquals("getBestMatch(en_GB)", "en_GB", locString(best));
  268     best = matcher.getBestMatch("en_US", errorCode);
  269     assertEquals("getBestMatch(en_US)", "en", locString(best));
  270     best = matcher.getBestMatch("fr_FR", errorCode);
  271     assertEquals("getBestMatch(fr_FR)", "fr", locString(best));
  272     best = matcher.getBestMatch("ja_JP", errorCode);
  273     assertEquals("getBestMatch(ja_JP)", "en_GB", locString(best));
  274     LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode);
  275     assertEquals("getBestMatchResult(ja_JP).supp",
  276                  "en_GB", locString(result.getSupportedLocale()));
  277     assertEquals("getBestMatchResult(ja_JP).suppIndex",
  278                  -1, result.getSupportedIndex());
  279 }
  280 
  281 void LocaleMatcherTest::testUnsupportedDefault() {
  282     // The default locale does not match any of the supported locales.
  283     IcuTestErrorCode errorCode(*this, "testUnsupportedDefault");
  284     Locale locales[] = { "fr", "en_GB", "en" };
  285     Locale def("de");
  286     LocaleMatcher matcher = LocaleMatcher::Builder().
  287         setSupportedLocales(ARRAY_RANGE(locales)).
  288         setDefaultLocale(&def).
  289         build(errorCode);
  290     const Locale *best = matcher.getBestMatch("en_GB", errorCode);
  291     assertEquals("getBestMatch(en_GB)", "en_GB", locString(best));
  292     best = matcher.getBestMatch("en_US", errorCode);
  293     assertEquals("getBestMatch(en_US)", "en", locString(best));
  294     best = matcher.getBestMatch("fr_FR", errorCode);
  295     assertEquals("getBestMatch(fr_FR)", "fr", locString(best));
  296     best = matcher.getBestMatch("ja_JP", errorCode);
  297     assertEquals("getBestMatch(ja_JP)", "de", locString(best));
  298     LocaleMatcher::Result result = matcher.getBestMatchResult("ja_JP", errorCode);
  299     assertEquals("getBestMatchResult(ja_JP).supp",
  300                  "de", locString(result.getSupportedLocale()));
  301     assertEquals("getBestMatchResult(ja_JP).suppIndex",
  302                  -1, result.getSupportedIndex());
  303 }
  304 
  305 void LocaleMatcherTest::testDemotion() {
  306     IcuTestErrorCode errorCode(*this, "testDemotion");
  307     Locale supported[] = { "fr", "de-CH", "it" };
  308     Locale desired[] = { "fr-CH", "de-CH", "it" };
  309     {
  310         LocaleMatcher noDemotion = LocaleMatcher::Builder().
  311             setSupportedLocales(ARRAY_RANGE(supported)).
  312             setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_NONE).build(errorCode);
  313         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
  314         assertEquals("no demotion",
  315                      "de_CH", locString(noDemotion.getBestMatch(desiredIter, errorCode)));
  316     }
  317 
  318     {
  319         LocaleMatcher regionDemotion = LocaleMatcher::Builder().
  320             setSupportedLocales(ARRAY_RANGE(supported)).
  321             setDemotionPerDesiredLocale(ULOCMATCH_DEMOTION_REGION).build(errorCode);
  322         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
  323         assertEquals("region demotion",
  324                      "fr", locString(regionDemotion.getBestMatch(desiredIter, errorCode)));
  325     }
  326 }
  327 
  328 void LocaleMatcherTest::testDirection() {
  329     IcuTestErrorCode errorCode(*this, "testDirection");
  330     Locale supported[] = { "ar", "nn" };
  331     Locale desired[] = { "arz-EG", "nb-DK" };
  332     LocaleMatcher::Builder builder;
  333     builder.setSupportedLocales(ARRAY_RANGE(supported));
  334     {
  335         // arz is a close one-way match to ar, and the region matches.
  336         // (Egyptian Arabic vs. Arabic)
  337         // Also explicitly exercise the move copy constructor.
  338         LocaleMatcher built = builder.build(errorCode);
  339         LocaleMatcher withOneWay(std::move(built));
  340         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
  341         assertEquals("with one-way", "ar",
  342                      locString(withOneWay.getBestMatch(desiredIter, errorCode)));
  343     }
  344     {
  345         // nb is a less close two-way match to nn, and the regions differ.
  346         // (Norwegian Bokmal vs. Nynorsk)
  347         // Also explicitly exercise the move assignment operator.
  348         LocaleMatcher onlyTwoWay = builder.build(errorCode);
  349         LocaleMatcher built =
  350             builder.setDirection(ULOCMATCH_DIRECTION_ONLY_TWO_WAY).build(errorCode);
  351         onlyTwoWay = std::move(built);
  352         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
  353         assertEquals("only two-way", "nn",
  354                      locString(onlyTwoWay.getBestMatch(desiredIter, errorCode)));
  355     }
  356 }
  357 
  358 void LocaleMatcherTest::testMatch() {
  359     IcuTestErrorCode errorCode(*this, "testMatch");
  360     LocaleMatcher matcher = LocaleMatcher::Builder().build(errorCode);
  361 
  362     // Java test function testMatch_exact()
  363     Locale en_CA("en_CA");
  364     assertEquals("exact match", 1.0, matcher.internalMatch(en_CA, en_CA, errorCode));
  365 
  366     // testMatch_none
  367     Locale ar_MK("ar_MK");
  368     double match = matcher.internalMatch(ar_MK, en_CA, errorCode);
  369     assertTrue("mismatch: 0<=match<0.2", 0 <= match && match < 0.2);
  370 
  371     // testMatch_matchOnMaximized
  372     Locale und_TW("und_TW");
  373     Locale zh("zh");
  374     Locale zh_Hant("zh_Hant");
  375     double matchZh = matcher.internalMatch(und_TW, zh, errorCode);
  376     double matchZhHant = matcher.internalMatch(und_TW, zh_Hant, errorCode);
  377     assertTrue("und_TW should be closer to zh_Hant than to zh",
  378                matchZh < matchZhHant);
  379     Locale en_Hant_TW("en_Hant_TW");
  380     double matchEnHantTw = matcher.internalMatch(en_Hant_TW, zh_Hant, errorCode);
  381     assertTrue("zh_Hant should be closer to und_TW than to en_Hant_TW",
  382                matchEnHantTw < matchZhHant);
  383     assertTrue("zh should be closer to und_TW than to en_Hant_TW",
  384                matchEnHantTw < matchZh);
  385 }
  386 
  387 void LocaleMatcherTest::testResolvedLocale() {
  388     IcuTestErrorCode errorCode(*this, "testResolvedLocale");
  389     LocaleMatcher matcher = LocaleMatcher::Builder().
  390         addSupportedLocale("ar-EG").
  391         build(errorCode);
  392     Locale desired("ar-SA-u-nu-latn");
  393     LocaleMatcher::Result result = matcher.getBestMatchResult(desired, errorCode);
  394     assertEquals("best", "ar_EG", locString(result.getSupportedLocale()));
  395     Locale resolved = result.makeResolvedLocale(errorCode);
  396     assertEquals("ar-EG + ar-SA-u-nu-latn = ar-SA-u-nu-latn",
  397                  "ar-SA-u-nu-latn",
  398                  resolved.toLanguageTag<std::string>(errorCode).data());
  399 }
  400 
  401 namespace {
  402 
  403 bool toInvariant(const UnicodeString &s, CharString &inv, ErrorCode &errorCode) {
  404     if (errorCode.isSuccess()) {
  405         inv.clear().appendInvariantChars(s, errorCode);
  406         return errorCode.isSuccess();
  407     }
  408     return false;
  409 }
  410 
  411 bool getSuffixAfterPrefix(const UnicodeString &s, int32_t limit,
  412                           const UnicodeString &prefix, UnicodeString &suffix) {
  413     if (prefix.length() <= limit && s.startsWith(prefix)) {
  414         suffix.setTo(s, prefix.length(), limit - prefix.length());
  415         return true;
  416     } else {
  417         return false;
  418     }
  419 }
  420 
  421 bool getInvariantSuffixAfterPrefix(const UnicodeString &s, int32_t limit,
  422                                    const UnicodeString &prefix, CharString &suffix,
  423                                    ErrorCode &errorCode) {
  424     UnicodeString u_suffix;
  425     return getSuffixAfterPrefix(s, limit, prefix, u_suffix) &&
  426         toInvariant(u_suffix, suffix, errorCode);
  427 }
  428 
  429 bool readTestCase(const UnicodeString &line, TestCase &test, IcuTestErrorCode &errorCode) {
  430     if (errorCode.isFailure()) { return false; }
  431     ++test.lineNr;
  432     // Start of comment, or end of line, minus trailing spaces.
  433     int32_t limit = line.indexOf(u'#');
  434     if (limit < 0) {
  435         limit = line.length();
  436         // Remove trailing CR LF.
  437         char16_t c;
  438         while (limit > 0 && ((c = line.charAt(limit - 1)) == u'\n' || c == u'\r')) {
  439             --limit;
  440         }
  441     }
  442     // Remove spaces before comment or at the end of the line.
  443     char16_t c;
  444     while (limit > 0 && ((c = line.charAt(limit - 1)) == u' ' || c == u'\t')) {
  445         --limit;
  446     }
  447     if (limit == 0) {  // empty line
  448         return false;
  449     }
  450     if (line.startsWith(u"** test: ")) {
  451         test.reset();
  452     } else if (getInvariantSuffixAfterPrefix(line, limit, u"@supported=",
  453                                              test.supported, errorCode)) {
  454     } else if (getInvariantSuffixAfterPrefix(line, limit, u"@default=",
  455                                              test.def, errorCode)) {
  456     } else if (getSuffixAfterPrefix(line, limit, u"@favor=", test.favor)) {
  457     } else if (getSuffixAfterPrefix(line, limit, u"@threshold=", test.threshold)) {
  458     } else {
  459         int32_t matchSep = line.indexOf(u">>");
  460         // >> before an inline comment, and followed by more than white space.
  461         if (0 <= matchSep && (matchSep + 2) < limit) {
  462             toInvariant(line.tempSubStringBetween(0, matchSep).trim(), test.desired, errorCode);
  463             test.expDesired.clear();
  464             test.expCombined.clear();
  465             int32_t start = matchSep + 2;
  466             int32_t expLimit = line.indexOf(u'|', start);
  467             if (expLimit < 0) {
  468                 toInvariant(line.tempSubStringBetween(start, limit).trim(),
  469                             test.expMatch, errorCode);
  470             } else {
  471                 toInvariant(line.tempSubStringBetween(start, expLimit).trim(),
  472                             test.expMatch, errorCode);
  473                 start = expLimit + 1;
  474                 expLimit = line.indexOf(u'|', start);
  475                 if (expLimit < 0) {
  476                     toInvariant(line.tempSubStringBetween(start, limit).trim(),
  477                                 test.expDesired, errorCode);
  478                 } else {
  479                     toInvariant(line.tempSubStringBetween(start, expLimit).trim(),
  480                                 test.expDesired, errorCode);
  481                     toInvariant(line.tempSubStringBetween(expLimit + 1, limit).trim(),
  482                                 test.expCombined, errorCode);
  483                 }
  484             }
  485             return errorCode.isSuccess();
  486         } else {
  487             errorCode.set(U_INVALID_FORMAT_ERROR);
  488         }
  489     }
  490     return false;
  491 }
  492 
  493 Locale *getLocaleOrNull(const CharString &s, Locale &locale) {
  494     if (s == "null") {
  495         return nullptr;
  496     } else {
  497         return &(locale = Locale(s.data()));
  498     }
  499 }
  500 
  501 }  // namespace
  502 
  503 UBool LocaleMatcherTest::dataDriven(const TestCase &test, IcuTestErrorCode &errorCode) {
  504     LocaleMatcher::Builder builder;
  505     builder.setSupportedLocalesFromListString(test.supported.toStringPiece());
  506     if (!test.def.isEmpty()) {
  507         Locale defaultLocale(test.def.data());
  508         builder.setDefaultLocale(&defaultLocale);
  509     }
  510     if (!test.favor.isEmpty()) {
  511         ULocMatchFavorSubtag favor;
  512         if (test.favor == u"normal") {
  513             favor = ULOCMATCH_FAVOR_LANGUAGE;
  514         } else if (test.favor == u"script") {
  515             favor = ULOCMATCH_FAVOR_SCRIPT;
  516         } else {
  517             errln(UnicodeString(u"unsupported FavorSubtag value ") + test.favor);
  518             return FALSE;
  519         }
  520         builder.setFavorSubtag(favor);
  521     }
  522     if (!test.threshold.isEmpty()) {
  523         infoln("skipping test case on line %d with non-default threshold: not exposed via API",
  524                (int)test.lineNr);
  525         return TRUE;
  526         // int32_t threshold = Integer.valueOf(test.threshold);
  527         // builder.internalSetThresholdDistance(threshold);
  528     }
  529     LocaleMatcher matcher = builder.build(errorCode);
  530     if (errorCode.errIfFailureAndReset("LocaleMatcher::Builder::build()")) {
  531         return FALSE;
  532     }
  533 
  534     Locale expMatchLocale("");
  535     Locale *expMatch = getLocaleOrNull(test.expMatch, expMatchLocale);
  536     if (test.expDesired.isEmpty() && test.expCombined.isEmpty()) {
  537         StringPiece desiredSP = test.desired.toStringPiece();
  538         const Locale *bestSupported = matcher.getBestMatchForListString(desiredSP, errorCode);
  539         if (!assertEquals("bestSupported from string",
  540                           locString(expMatch), locString(bestSupported))) {
  541             return FALSE;
  542         }
  543         LocalePriorityList desired(test.desired.toStringPiece(), errorCode);
  544         LocalePriorityList::Iterator desiredIter = desired.iterator();
  545         if (desired.getLength() == 1) {
  546             const Locale &desiredLocale = desiredIter.next();
  547             bestSupported = matcher.getBestMatch(desiredLocale, errorCode);
  548             UBool ok = assertEquals("bestSupported from Locale",
  549                                     locString(expMatch), locString(bestSupported));
  550 
  551             LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocale, errorCode);
  552             return ok & assertEquals("result.getSupportedLocale from Locale",
  553                                      locString(expMatch), locString(result.getSupportedLocale()));
  554         } else {
  555             bestSupported = matcher.getBestMatch(desiredIter, errorCode);
  556             return assertEquals("bestSupported from Locale iterator",
  557                                 locString(expMatch), locString(bestSupported));
  558         }
  559     } else {
  560         LocalePriorityList desired(test.desired.toStringPiece(), errorCode);
  561         LocalePriorityList::Iterator desiredIter = desired.iterator();
  562         LocaleMatcher::Result result = matcher.getBestMatchResult(desiredIter, errorCode);
  563         UBool ok = assertEquals("result.getSupportedLocale from Locales",
  564                                 locString(expMatch), locString(result.getSupportedLocale()));
  565         if (!test.expDesired.isEmpty()) {
  566             Locale expDesiredLocale("");
  567             Locale *expDesired = getLocaleOrNull(test.expDesired, expDesiredLocale);
  568             ok &= assertEquals("result.getDesiredLocale from Locales",
  569                                locString(expDesired), locString(result.getDesiredLocale()));
  570         }
  571         if (!test.expCombined.isEmpty()) {
  572             if (test.expMatch.contains("-u-")) {
  573                 logKnownIssue("20727",
  574                               UnicodeString(u"ignoring makeResolvedLocale() line ") + test.lineNr);
  575                 return ok;
  576             }
  577             Locale expCombinedLocale("");
  578             Locale *expCombined = getLocaleOrNull(test.expCombined, expCombinedLocale);
  579             Locale combined = result.makeResolvedLocale(errorCode);
  580             ok &= assertEquals("combined Locale from Locales",
  581                                locString(expCombined), locString(&combined));
  582         }
  583         return ok;
  584     }
  585 }
  586 
  587 void LocaleMatcherTest::testDataDriven() {
  588     IcuTestErrorCode errorCode(*this, "testDataDriven");
  589     CharString path(getSourceTestData(errorCode), errorCode);
  590     path.appendPathPart("localeMatcherTest.txt", errorCode);
  591     const char *codePage = "UTF-8";
  592     LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, TRUE, FALSE, errorCode));
  593     if(errorCode.errIfFailureAndReset("ucbuf_open(localeMatcherTest.txt)")) {
  594         return;
  595     }
  596     int32_t lineLength;
  597     const UChar *p;
  598     UnicodeString line;
  599     TestCase test;
  600     int32_t numPassed = 0;
  601     while ((p = ucbuf_readline(f.getAlias(), &lineLength, errorCode)) != nullptr &&
  602             errorCode.isSuccess()) {
  603         line.setTo(FALSE, p, lineLength);
  604         if (!readTestCase(line, test, errorCode)) {
  605             if (errorCode.errIfFailureAndReset(
  606                     "test data syntax error on line %d", (int)test.lineNr)) {
  607                 infoln(line);
  608             }
  609             continue;
  610         }
  611         UBool ok = dataDriven(test, errorCode);
  612         if (errorCode.errIfFailureAndReset("test error on line %d", (int)test.lineNr)) {
  613             infoln(line);
  614         } else if (!ok) {
  615             infoln("test failure on line %d", (int)test.lineNr);
  616             infoln(line);
  617         } else {
  618             ++numPassed;
  619         }
  620     }
  621     infoln("number of passing test cases: %d", (int)numPassed);
  622 }