590 lines
26 KiB
C++
590 lines
26 KiB
C++
// © 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);
|
|
}
|