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