• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 *   Copyright (C) 2012-2016, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *   file name:  listformattertest.cpp
11 *   encoding:   UTF-8
12 *   tab size:   8 (not used)
13 *   indentation:4
14 *
15 *   created on: 2012aug27
16 *   created by: Umesh P. Nair
17 */
18 
19 #include "listformattertest.h"
20 #include "unicode/ulistformatter.h"
21 #include "cmemory.h"
22 #include <string.h>
23 
24 #if !UCONFIG_NO_FORMATTING
25 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)26 void ListFormatterTest::runIndexedTest(int32_t index, UBool exec,
27                                        const char* &name, char* /*par */) {
28     TESTCASE_AUTO_BEGIN;
29     TESTCASE_AUTO(TestRoot);
30     TESTCASE_AUTO(TestBogus);
31     TESTCASE_AUTO(TestEnglish);
32     TESTCASE_AUTO(TestEnglishUS);
33     TESTCASE_AUTO(TestRussian);
34     TESTCASE_AUTO(TestMalayalam);
35     TESTCASE_AUTO(TestZulu);
36     TESTCASE_AUTO(TestOutOfOrderPatterns);
37     TESTCASE_AUTO(Test9946);
38     TESTCASE_AUTO(TestEnglishGB);
39     TESTCASE_AUTO(TestNynorsk);
40     TESTCASE_AUTO(TestChineseTradHK);
41     TESTCASE_AUTO(TestFieldPositionIteratorWith1Item);
42     TESTCASE_AUTO(TestFieldPositionIteratorWith2Items);
43     TESTCASE_AUTO(TestFieldPositionIteratorWith2ItemsPatternShift);
44     TESTCASE_AUTO(TestFieldPositionIteratorWith3Items);
45     TESTCASE_AUTO(TestFieldPositionIteratorWith3ItemsPatternShift);
46     TESTCASE_AUTO(TestFormattedValue);
47     TESTCASE_AUTO(TestDifferentStyles);
48     TESTCASE_AUTO(TestBadStylesFail);
49     TESTCASE_AUTO(TestCreateStyled);
50     TESTCASE_AUTO(TestContextual);
51     TESTCASE_AUTO_END;
52 }
53 
54 namespace {
attrString(int32_t attrId)55 const char* attrString(int32_t attrId) {
56   switch (attrId) {
57     case ULISTFMT_LITERAL_FIELD: return "literal";
58     case ULISTFMT_ELEMENT_FIELD: return "element";
59     default: return "xxx";
60   }
61 }
62 }  // namespace
63 
ExpectPositions(const FormattedList & iter,int32_t * values,int32_t tupleCount,UErrorCode & status)64 void ListFormatterTest::ExpectPositions(
65         const FormattedList& iter,
66         int32_t *values,
67         int32_t tupleCount,
68         UErrorCode& status) {
69     UBool found[10];
70     ConstrainedFieldPosition cfp;
71     cfp.constrainCategory(UFIELD_CATEGORY_LIST);
72     if (tupleCount > 10) {
73       assertTrue("internal error, tupleCount too large", FALSE);
74     } else {
75         for (int i = 0; i < tupleCount; ++i) {
76             found[i] = FALSE;
77         }
78     }
79     while (iter.nextPosition(cfp, status)) {
80         UBool ok = FALSE;
81         int32_t id = cfp.getField();
82         int32_t start = cfp.getStart();
83         int32_t limit = cfp.getLimit();
84         char buf[128];
85         sprintf(buf, "%24s %3d %3d %3d", attrString(id), id, start, limit);
86         logln(buf);
87         for (int i = 0; i < tupleCount; ++i) {
88             if (found[i]) {
89                 continue;
90             }
91             if (values[i*3] == id && values[i*3+1] == start && values[i*3+2] == limit) {
92                 found[i] = ok = TRUE;
93                 break;
94             }
95         }
96         assertTrue((UnicodeString)"found [" + attrString(id) + "," + start + "," + limit + "]", ok);
97     }
98     // check that all were found
99     UBool ok = TRUE;
100     for (int i = 0; i < tupleCount; ++i) {
101         if (!found[i]) {
102             ok = FALSE;
103             assertTrue((UnicodeString) "missing [" + attrString(values[i*3]) + "," + values[i*3+1] +
104                        "," + values[i*3+2] + "]", found[i]);
105         }
106     }
107     assertTrue("no expected values were missing", ok);
108 }
109 
ListFormatterTest()110 ListFormatterTest::ListFormatterTest() :
111         prefix("Prefix: ", -1, US_INV),
112         one("Alice", -1, US_INV), two("Bob", -1, US_INV),
113         three("Charlie", -1, US_INV), four("Delta", -1, US_INV) {
114 }
115 
CheckFormatting(const ListFormatter * formatter,UnicodeString data[],int32_t dataSize,const UnicodeString & expected_result,const char * testName)116 void ListFormatterTest::CheckFormatting(const ListFormatter* formatter, UnicodeString data[], int32_t dataSize,
117                                         const UnicodeString& expected_result, const char* testName) {
118     UnicodeString actualResult(prefix);
119     IcuTestErrorCode errorCode(*this, testName);
120     formatter->format(data, dataSize, actualResult, errorCode);
121     UnicodeString expectedStringWithPrefix = prefix + expected_result;
122     if (expectedStringWithPrefix != actualResult) {
123         errln(UnicodeString("Expected: |") + expectedStringWithPrefix +  "|, Actual: |" + actualResult + "|");
124     }
125 }
126 
CheckFourCases(const char * locale_string,UnicodeString one,UnicodeString two,UnicodeString three,UnicodeString four,UnicodeString results[4],const char * testName)127 void ListFormatterTest::CheckFourCases(const char* locale_string, UnicodeString one, UnicodeString two,
128         UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName) {
129     IcuTestErrorCode errorCode(*this, testName);
130     LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale(locale_string), errorCode));
131     if (U_FAILURE(errorCode)) {
132         dataerrln("ListFormatter::createInstance(Locale(\"%s\"), errorCode) failed in CheckFourCases: %s", locale_string, u_errorName(errorCode));
133         return;
134     }
135     UnicodeString input1[] = {one};
136     CheckFormatting(formatter.getAlias(), input1, 1, results[0], testName);
137 
138     UnicodeString input2[] = {one, two};
139     CheckFormatting(formatter.getAlias(), input2, 2, results[1], testName);
140 
141     UnicodeString input3[] = {one, two, three};
142     CheckFormatting(formatter.getAlias(), input3, 3, results[2], testName);
143 
144     UnicodeString input4[] = {one, two, three, four};
145     CheckFormatting(formatter.getAlias(), input4, 4, results[3], testName);
146 }
147 
RecordFourCases(const Locale & locale,UnicodeString one,UnicodeString two,UnicodeString three,UnicodeString four,UnicodeString results[4],const char * testName)148 UBool ListFormatterTest::RecordFourCases(const Locale& locale, UnicodeString one, UnicodeString two,
149         UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName)  {
150     IcuTestErrorCode errorCode(*this, testName);
151     LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(locale, errorCode));
152     if (U_FAILURE(errorCode)) {
153         dataerrln("ListFormatter::createInstance(\"%s\", errorCode) failed in RecordFourCases: %s", locale.getName(), u_errorName(errorCode));
154         return FALSE;
155     }
156     UnicodeString input1[] = {one};
157     formatter->format(input1, 1, results[0], errorCode);
158     UnicodeString input2[] = {one, two};
159     formatter->format(input2, 2, results[1], errorCode);
160     UnicodeString input3[] = {one, two, three};
161     formatter->format(input3, 3, results[2], errorCode);
162     UnicodeString input4[] = {one, two, three, four};
163     formatter->format(input4, 4, results[3], errorCode);
164     if (U_FAILURE(errorCode)) {
165         errln("RecordFourCases failed: %s", u_errorName(errorCode));
166         return FALSE;
167     }
168     return TRUE;
169 }
170 
TestRoot()171 void ListFormatterTest::TestRoot() {
172     UnicodeString results[4] = {
173         one,
174         one + ", " + two,
175         one + ", " + two + ", " + three,
176         one + ", " + two + ", " + three + ", " + four
177     };
178 
179     CheckFourCases("", one, two, three, four, results, "TestRoot()");
180 }
181 
182 // Bogus locale should fallback to root.
TestBogus()183 void ListFormatterTest::TestBogus() {
184     UnicodeString results[4];
185     if (RecordFourCases(Locale::getDefault(), one, two, three, four, results, "TestBogus()")) {
186       CheckFourCases("ex_PY", one, two, three, four, results, "TestBogus()");
187     }
188 }
189 
190 // Formatting in English.
191 // "and" is used before the last element, and all elements up to (and including) the penultimate are followed by a comma.
TestEnglish()192 void ListFormatterTest::TestEnglish() {
193     UnicodeString results[4] = {
194         one,
195         one + " and " + two,
196         one + ", " + two + ", and " + three,
197         one + ", " + two + ", " + three + ", and " + four
198     };
199 
200     CheckFourCases("en", one, two, three, four, results, "TestEnglish()");
201 }
202 
Test9946()203 void ListFormatterTest::Test9946() {
204     IcuTestErrorCode errorCode(*this, "Test9946()");
205     LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale("en"), errorCode));
206     if (U_FAILURE(errorCode)) {
207         dataerrln(
208             "ListFormatter::createInstance(Locale(\"en\"), errorCode) failed in Test9946: %s",
209             u_errorName(errorCode));
210         return;
211     }
212     UnicodeString data[3] = {"{0}", "{1}", "{2}"};
213     UnicodeString actualResult;
214     formatter->format(data, 3, actualResult, errorCode);
215     if (U_FAILURE(errorCode)) {
216         dataerrln(
217             "ListFormatter::createInstance(Locale(\"en\"), errorCode) failed in Test9946: %s",
218             u_errorName(errorCode));
219         return;
220     }
221     UnicodeString expected("{0}, {1}, and {2}");
222     if (expected != actualResult) {
223         errln("Expected " + expected + ", got " + actualResult);
224     }
225 }
226 
TestEnglishUS()227 void ListFormatterTest::TestEnglishUS() {
228     UnicodeString results[4] = {
229         one,
230         one + " and " + two,
231         one + ", " + two + ", and " + three,
232         one + ", " + two + ", " + three + ", and " + four
233     };
234 
235     CheckFourCases("en_US", one, two, three, four, results, "TestEnglishUS()");
236 }
237 
238 // Tests resource loading and inheritance when region sublocale
239 // has only partial data for the listPattern element (overriding
240 // some of the parent data). #12994
TestEnglishGB()241 void ListFormatterTest::TestEnglishGB() {
242     UnicodeString results[4] = {
243         one,
244         one + " and " + two,
245         one + ", " + two + " and " + three,
246         one + ", " + two + ", " + three + " and " + four
247     };
248 
249     CheckFourCases("en_GB", one, two, three, four, results, "TestEnglishGB()");
250 }
251 
RunTestFieldPositionIteratorWithFormatter(ListFormatter * formatter,UnicodeString data[],int32_t n,int32_t expected[],int32_t tupleCount,const char16_t * expectedFormatted,const char * testName)252 void ListFormatterTest::RunTestFieldPositionIteratorWithFormatter(
253         ListFormatter* formatter,
254         UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
255         const char16_t *expectedFormatted,
256         const char* testName) {
257     IcuTestErrorCode errorCode(*this, testName);
258     FormattedList fl = formatter->formatStringsToValue(data, n, errorCode);
259     UnicodeString actual = fl.toString(errorCode);
260     if (U_FAILURE(errorCode)) {
261         dataerrln(
262             "ListFormatter::format(data, %d, &iter, errorCode) "
263             "failed in %s: %s", n, testName, u_errorName(errorCode));
264         return;
265     }
266     if (actual != expectedFormatted) {
267         errln(UnicodeString("Expected: |") + expectedFormatted +  "|, Actual: |" + actual + "|");
268     }
269     ExpectPositions(fl, expected, tupleCount, errorCode);
270 }
271 
RunTestFieldPositionIteratorWithNItemsPatternShift(UnicodeString data[],int32_t n,int32_t expected[],int32_t tupleCount,const char16_t * expectedFormatted,const char * testName)272 void ListFormatterTest::RunTestFieldPositionIteratorWithNItemsPatternShift(
273         UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
274         const char16_t *expectedFormatted,
275         const char* testName) {
276     IcuTestErrorCode errorCode(*this, testName);
277     LocalPointer<ListFormatter> formatter(
278         ListFormatter::createInstance(Locale("ur", "IN"), "unit-narrow", errorCode));
279     if (U_FAILURE(errorCode)) {
280         dataerrln(
281             "ListFormatter::createInstance(Locale(\"ur\", \"IN\"), \"unit-narrow\", errorCode) failed in "
282             "%s: %s", testName, u_errorName(errorCode));
283         return;
284     }
285     RunTestFieldPositionIteratorWithFormatter(
286         formatter.getAlias(),
287         data, n, expected, tupleCount, expectedFormatted, testName);
288 }
289 
RunTestFieldPositionIteratorWithNItems(UnicodeString data[],int32_t n,int32_t expected[],int32_t tupleCount,const char16_t * expectedFormatted,const char * testName)290 void ListFormatterTest::RunTestFieldPositionIteratorWithNItems(
291         UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
292         const char16_t *expectedFormatted,
293         const char* testName) {
294     IcuTestErrorCode errorCode(*this, testName);
295     LocalPointer<ListFormatter> formatter(
296         ListFormatter::createInstance(Locale("en"), errorCode));
297     if (U_FAILURE(errorCode)) {
298         dataerrln(
299             "ListFormatter::createInstance(Locale(\"en\"), errorCode) failed in "
300             "%s: %s", testName, u_errorName(errorCode));
301         return;
302     }
303     RunTestFieldPositionIteratorWithFormatter(
304         formatter.getAlias(),
305         data, n, expected, tupleCount, expectedFormatted, testName);
306 }
307 
TestFieldPositionIteratorWith3Items()308 void ListFormatterTest::TestFieldPositionIteratorWith3Items() {
309     //  0         1
310     //  012345678901234
311     // "a, bbb, and cc"
312     UnicodeString data[3] = {"a", "bbb", "cc"};
313     int32_t expected[] = {
314         ULISTFMT_ELEMENT_FIELD, 0, 1,
315         ULISTFMT_LITERAL_FIELD, 1, 3,
316         ULISTFMT_ELEMENT_FIELD, 3, 6,
317         ULISTFMT_LITERAL_FIELD, 6, 12,
318         ULISTFMT_ELEMENT_FIELD, 12, 14
319     };
320     int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
321     RunTestFieldPositionIteratorWithNItems(
322         data, 3, expected, tupleCount,
323         u"a, bbb, and cc",
324         "TestFieldPositionIteratorWith3Items");
325 }
326 
TestFieldPositionIteratorWith3ItemsPatternShift()327 void ListFormatterTest::TestFieldPositionIteratorWith3ItemsPatternShift() {
328     //  0         1
329     //  012345678901234
330     // "cc bbb a"
331     UnicodeString data[3] = {"a", "bbb", "cc"};
332     int32_t expected[] = {
333         ULISTFMT_ELEMENT_FIELD, 7, 8,
334         ULISTFMT_LITERAL_FIELD, 6, 7,
335         ULISTFMT_ELEMENT_FIELD, 3, 6,
336         ULISTFMT_LITERAL_FIELD, 2, 3,
337         ULISTFMT_ELEMENT_FIELD, 0, 2
338     };
339     int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
340     RunTestFieldPositionIteratorWithNItemsPatternShift(
341         data, 3, expected, tupleCount,
342         u"cc bbb a",
343         "TestFieldPositionIteratorWith3ItemsPatternShift");
344 }
345 
TestFieldPositionIteratorWith2Items()346 void ListFormatterTest::TestFieldPositionIteratorWith2Items() {
347     //  0         1
348     //  01234567890
349     // "bbb and cc"
350     UnicodeString data[2] = {"bbb", "cc"};
351     int32_t expected[] = {
352         ULISTFMT_ELEMENT_FIELD, 0, 3,
353         ULISTFMT_LITERAL_FIELD, 3, 8,
354         ULISTFMT_ELEMENT_FIELD, 8, 10
355     };
356     int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
357     RunTestFieldPositionIteratorWithNItems(
358         data, 2, expected, tupleCount,
359         u"bbb and cc",
360         "TestFieldPositionIteratorWith2Items");
361 }
362 
TestFieldPositionIteratorWith2ItemsPatternShift()363 void ListFormatterTest::TestFieldPositionIteratorWith2ItemsPatternShift() {
364     //  0         1
365     //  01234567890
366     // "cc bbb"
367     UnicodeString data[2] = {"bbb", "cc"};
368     int32_t expected[] = {
369         ULISTFMT_ELEMENT_FIELD, 3, 6,
370         ULISTFMT_LITERAL_FIELD, 2, 3,
371         ULISTFMT_ELEMENT_FIELD, 0, 2
372     };
373     int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
374     RunTestFieldPositionIteratorWithNItemsPatternShift(
375         data, 2, expected, tupleCount,
376         u"cc bbb",
377         "TestFieldPositionIteratorWith2ItemsPatternShift");
378 }
379 
TestFieldPositionIteratorWith1Item()380 void ListFormatterTest::TestFieldPositionIteratorWith1Item() {
381     //  012
382     // "cc"
383     UnicodeString data[1] = {"cc"};
384     int32_t expected[] = {
385         ULISTFMT_ELEMENT_FIELD, 0, 2
386     };
387     int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
388     RunTestFieldPositionIteratorWithNItems(
389         data, 1, expected, tupleCount,
390         u"cc",
391         "TestFieldPositionIteratorWith1Item");
392 }
393 
394 // Tests resource loading and inheritance when region sublocale
395 // has only partial data for the listPattern element (overriding
396 // some of the parent data). #12994
TestNynorsk()397 void ListFormatterTest::TestNynorsk() {
398     UnicodeString results[4] = {
399         one,
400         one + " og " + two,
401         one + ", " + two + " og " + three,
402         one + ", " + two + ", " + three + " og " + four
403     };
404 
405     CheckFourCases("nn", one, two, three, four, results, "TestNynorsk()");
406 }
407 
408 // Tests resource loading and inheritance when region sublocale
409 // has only partial data for the listPattern element (overriding
410 // some of the parent data). #12994
TestChineseTradHK()411 void ListFormatterTest::TestChineseTradHK() {
412     UnicodeString and_string = UnicodeString("\\u53CA", -1, US_INV).unescape();
413     UnicodeString comma_string = UnicodeString("\\u3001", -1, US_INV).unescape();
414     UnicodeString results[4] = {
415         one,
416         one + and_string + two,
417         one + comma_string + two + and_string + three,
418         one + comma_string + two + comma_string + three + and_string + four
419     };
420 
421     CheckFourCases("zh_Hant_HK", one, two, three, four, results, "TestChineseTradHK()");
422 }
423 
424 // Formatting in Russian.
425 // "\\u0438" is used before the last element, and all elements up to (but not including) the penultimate are followed by a comma.
TestRussian()426 void ListFormatterTest::TestRussian() {
427     UnicodeString and_string = UnicodeString(" \\u0438 ", -1, US_INV).unescape();
428     UnicodeString results[4] = {
429         one,
430         one + and_string + two,
431         one + ", " + two + and_string + three,
432         one + ", " + two + ", " + three + and_string + four
433     };
434 
435     CheckFourCases("ru", one, two, three, four, results, "TestRussian()");
436 }
437 
438 // Formatting in Malayalam.
439 // For two elements, "\\u0d15\\u0d42\\u0d1f\\u0d3e\\u0d24\\u0d46" is inserted in between.
440 // For more than two elements, comma is inserted between all elements up to (and including) the penultimate,
441 // and the word \\u0d0e\\u0d28\\u0d4d\\u0d28\\u0d3f\\u0d35 is inserted in the end.
TestMalayalam()442 void ListFormatterTest::TestMalayalam() {
443     UnicodeString pair_string = UnicodeString(" \\u0d15\\u0d42\\u0d1f\\u0d3e\\u0d24\\u0d46 ", -1, US_INV).unescape();
444     UnicodeString total_string = UnicodeString(" \\u0d0e\\u0d28\\u0d4d\\u0d28\\u0d3f\\u0d35", -1, US_INV).unescape();
445     UnicodeString results[4] = {
446         one,
447         one + pair_string + two,
448         one + ", " + two + ", " + three + total_string,
449         one + ", " + two + ", " + three + ", " + four + total_string
450     };
451 
452     CheckFourCases("ml", one, two, three, four, results, "TestMalayalam()");
453 }
454 
455 // Formatting in Zulu.
456 // "and" is used before the last element, and all elements up to (and including) the penultimate are followed by a comma.
TestZulu()457 void ListFormatterTest::TestZulu() {
458     UnicodeString results[4] = {
459         one,
460         one + " ne-" + two,
461         one + ", " + two + ", ne-" + three,
462         one + ", " + two + ", " + three + ", ne-" + four
463     };
464 
465     CheckFourCases("zu", one, two, three, four, results, "TestZulu()");
466 }
467 
TestOutOfOrderPatterns()468 void ListFormatterTest::TestOutOfOrderPatterns() {
469     UnicodeString results[4] = {
470         one,
471         two + " after " + one,
472         three + " in the last after " + two + " after the first " + one,
473         four + " in the last after " + three + " after " + two + " after the first " + one
474     };
475 
476     IcuTestErrorCode errorCode(*this, "TestOutOfOrderPatterns()");
477     Locale locale("en");
478     ListFormatData data("{1} after {0}", "{1} after the first {0}",
479                         "{1} after {0}", "{1} in the last after {0}", locale);
480     ListFormatter formatter(data, errorCode);
481 
482     UnicodeString input1[] = {one};
483     CheckFormatting(&formatter, input1, 1, results[0], "TestOutOfOrderPatterns()");
484 
485     UnicodeString input2[] = {one, two};
486     CheckFormatting(&formatter, input2, 2, results[1], "TestOutOfOrderPatterns()");
487 
488     UnicodeString input3[] = {one, two, three};
489     CheckFormatting(&formatter, input3, 3, results[2], "TestOutOfOrderPatterns()");
490 
491     UnicodeString input4[] = {one, two, three, four};
492     CheckFormatting(&formatter, input4, 4, results[3], "TestOutOfOrderPatterns()");
493 }
494 
TestFormattedValue()495 void ListFormatterTest::TestFormattedValue() {
496     IcuTestErrorCode status(*this, "TestFormattedValue");
497     LocalPointer<ListFormatter> fmt(ListFormatter::createInstance("en", status));
498     if (status.errIfFailureAndReset()) { return; }
499 
500     {
501         const char16_t* message = u"Field position test 1";
502         const char16_t* expectedString = u"hello, wonderful, and world";
503         const UnicodeString inputs[] = {
504             u"hello",
505             u"wonderful",
506             u"world"
507         };
508         FormattedList result = fmt->formatStringsToValue(inputs, UPRV_LENGTHOF(inputs), status);
509         static const UFieldPositionWithCategory expectedFieldPositions[] = {
510             // field, begin index, end index
511             {UFIELD_CATEGORY_LIST_SPAN, 0, 0, 5},
512             {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 0, 5},
513             {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 5, 7},
514             {UFIELD_CATEGORY_LIST_SPAN, 1, 7, 16},
515             {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 7, 16},
516             {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 16, 22},
517             {UFIELD_CATEGORY_LIST_SPAN, 2, 22, 27},
518             {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 22, 27}};
519         checkMixedFormattedValue(
520             message,
521             result,
522             expectedString,
523             expectedFieldPositions,
524             UPRV_LENGTHOF(expectedFieldPositions));
525     }
526 }
527 
DoTheRealListStyleTesting(Locale locale,UnicodeString items[],int itemCount,const char * style,const char * expected,IcuTestErrorCode status)528 void ListFormatterTest::DoTheRealListStyleTesting(Locale locale,
529         UnicodeString items[], int itemCount,
530         const char* style, const char* expected, IcuTestErrorCode status) {
531 
532     LocalPointer<ListFormatter> formatter(
533             ListFormatter::createInstance(locale, style, status));
534 
535     UnicodeString actualResult;
536     formatter->format(items, itemCount, actualResult, status);
537     assertEquals(style, UnicodeString(expected), actualResult);
538 }
539 
TestDifferentStyles()540 void ListFormatterTest::TestDifferentStyles() {
541     Locale locale("fr");
542     UnicodeString input[4] = { u"rouge", u"jaune", u"bleu", u"vert" };
543     IcuTestErrorCode status(*this, "TestDifferentStyles()");
544 
545     DoTheRealListStyleTesting(locale, input, 4, "standard", "rouge, jaune, bleu et vert", status);
546     DoTheRealListStyleTesting(locale, input, 4, "or", "rouge, jaune, bleu ou vert", status);
547     DoTheRealListStyleTesting(locale, input, 4, "unit", "rouge, jaune, bleu et vert", status);
548     DoTheRealListStyleTesting(locale, input, 4, "unit-narrow", "rouge jaune bleu vert", status);
549     DoTheRealListStyleTesting(locale, input, 4, "unit-short", "rouge, jaune, bleu et vert", status);
550 }
551 
TestBadStylesFail()552 void ListFormatterTest::TestBadStylesFail() {
553     Locale locale("fr");
554     const char * badStyles[4] = { "", "duration", "duration-short", "something-clearly-wrong" };
555     IcuTestErrorCode status(*this, "TestBadStylesFail()");
556 
557     for (int i = 0; i < 4; ++i) {
558       LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(locale, badStyles[i], status));
559       if (!status.expectErrorAndReset(U_MISSING_RESOURCE_ERROR, "style \"%s\"", badStyles[i])) {
560         // Do nothing, expectErrorAndReset already reports the error
561       }
562     }
563 }
564 
TestCreateStyled()565 void ListFormatterTest::TestCreateStyled() {
566     IcuTestErrorCode status(*this, "TestCreateStyled");
567     // Locale en has interesting data
568     struct TestCase {
569         const char* locale;
570         UListFormatterType type;
571         UListFormatterWidth width;
572         const char16_t* expected3;
573         const char16_t* expected2;
574         const char16_t* expected1;
575     } cases[] = {
576         { "pt", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, u"A, B e C", u"A e B", u"A" },
577         { "pt", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_SHORT, u"A, B e C", u"A e B", u"A" },
578         { "pt", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_NARROW, u"A, B, C", u"A, B", u"A" },
579         { "pt", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_WIDE, u"A, B ou C", u"A ou B", u"A" },
580         { "pt", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_SHORT, u"A, B ou C", u"A ou B", u"A" },
581         { "pt", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_NARROW, u"A, B ou C", u"A ou B", u"A" },
582         { "pt", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_WIDE, u"A, B e C", u"A e B", u"A" },
583         { "pt", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_SHORT, u"A, B e C", u"A e B", u"A" },
584         { "pt", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_NARROW, u"A B C", u"A B", u"A" },
585         { "en", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, u"A, B, and C", u"A and B", u"A" },
586         { "en", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_SHORT, u"A, B, & C", u"A & B", u"A" },
587         { "en", ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_NARROW, u"A, B, C", u"A, B", u"A" },
588         { "en", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_WIDE, u"A, B, or C", u"A or B", u"A" },
589         { "en", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_SHORT, u"A, B, or C", u"A or B", u"A" },
590         { "en", ULISTFMT_TYPE_OR, ULISTFMT_WIDTH_NARROW, u"A, B, or C", u"A or B", u"A" },
591         { "en", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_WIDE, u"A, B, C", u"A, B", u"A" },
592         { "en", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_SHORT, u"A, B, C", u"A, B", u"A" },
593         { "en", ULISTFMT_TYPE_UNITS, ULISTFMT_WIDTH_NARROW, u"A B C", u"A B", u"A" },
594     };
595     for (auto cas : cases) {
596         LocalPointer<ListFormatter> fmt(
597             ListFormatter::createInstance(cas.locale, cas.type, cas.width, status),
598             status);
599         if (status.errIfFailureAndReset()) {
600             continue;
601         }
602         UnicodeString message = UnicodeString(u"TestCreateStyled loc=")
603             + cas.locale + u" type="
604             + Int64ToUnicodeString(cas.type) + u" width="
605             + Int64ToUnicodeString(cas.width);
606         const UnicodeString inputs3[] = {
607             u"A",
608             u"B",
609             u"C"
610         };
611         FormattedList result = fmt->formatStringsToValue(inputs3, UPRV_LENGTHOF(inputs3), status);
612         assertEquals(message, cas.expected3, result.toTempString(status));
613         const UnicodeString inputs2[] = {
614             u"A",
615             u"B"
616         };
617         result = fmt->formatStringsToValue(inputs2, UPRV_LENGTHOF(inputs2), status);
618         assertEquals(message, cas.expected2, result.toTempString(status));
619         const UnicodeString inputs1[] = {
620             u"A"
621         };
622         result = fmt->formatStringsToValue(inputs1, UPRV_LENGTHOF(inputs1), status);
623         assertEquals(message, cas.expected1, result.toTempString(status));
624     }
625 }
626 
TestContextual()627 void ListFormatterTest::TestContextual() {
628     IcuTestErrorCode status(*this, "TestContextual");
629     std::vector<std::string> es = { "es", "es_419" , "es_PY", "es_DO" };
630     std::vector<std::string> he = { "he", "he_IL", "iw", "iw_IL" };
631     UListFormatterWidth widths [] = {
632         ULISTFMT_WIDTH_WIDE, ULISTFMT_WIDTH_SHORT, ULISTFMT_WIDTH_NARROW
633     };
634     struct TestCase {
635         std::vector<std::string> locales;
636         UListFormatterType type;
637         const char16_t* expected;
638         const char16_t* data1;
639         const char16_t* data2;
640         const char16_t* data3;
641     } cases[] = {
642         { es, ULISTFMT_TYPE_AND, u"fascinante e increíblemente",
643           u"fascinante",                     u"increíblemente",       nullptr },
644         { es, ULISTFMT_TYPE_AND, u"Comunicaciones Industriales e IIoT",
645           u"Comunicaciones Industriales",    u"IIoT",                 nullptr },
646         { es, ULISTFMT_TYPE_AND, u"España e Italia",         u"España",   u"Italia",      nullptr },
647         { es, ULISTFMT_TYPE_AND, u"hijas intrépidas e hijos solidarios",
648           u"hijas intrépidas",               u"hijos solidarios",     nullptr },
649         { es, ULISTFMT_TYPE_AND, u"a un hombre e hirieron a otro",
650           u"a un hombre",                    u"hirieron a otro",      nullptr },
651         { es, ULISTFMT_TYPE_AND, u"hija e hijo",             u"hija",     u"hijo",        nullptr },
652         { es, ULISTFMT_TYPE_AND, u"esposa, hija e hijo",     u"esposa",   u"hija",        u"hijo" },
653         // For 'y' exception
654         { es, ULISTFMT_TYPE_AND, u"oro y hierro",            u"oro",      u"hierro",      nullptr },
655         { es, ULISTFMT_TYPE_AND, u"agua y hielo",            u"agua",     u"hielo",       nullptr },
656         { es, ULISTFMT_TYPE_AND, u"colágeno y hialurónico",  u"colágeno", u"hialurónico", nullptr },
657 
658         { es, ULISTFMT_TYPE_OR, u"desierto u oasis",         u"desierto", u"oasis",       nullptr },
659         { es, ULISTFMT_TYPE_OR, u"oasis, desierto u océano", u"oasis",    u"desierto",    u"océano" },
660         { es, ULISTFMT_TYPE_OR, u"7 u 8",                    u"7",        u"8",           nullptr },
661         { es, ULISTFMT_TYPE_OR, u"7 u 80",                   u"7",        u"80",          nullptr },
662         { es, ULISTFMT_TYPE_OR, u"7 u 800",                  u"7",        u"800",         nullptr },
663         { es, ULISTFMT_TYPE_OR, u"6, 7 u 8",                 u"6",        u"7",           u"8" },
664         { es, ULISTFMT_TYPE_OR, u"10 u 11",                  u"10",       u"11",          nullptr },
665         { es, ULISTFMT_TYPE_OR, u"10 o 111",                 u"10",       u"111",         nullptr },
666         { es, ULISTFMT_TYPE_OR, u"10 o 11.2",                u"10",       u"11.2",        nullptr },
667         { es, ULISTFMT_TYPE_OR, u"9, 10 u 11",               u"9",        u"10",          u"11" },
668 
669         { he, ULISTFMT_TYPE_AND, u"a, b ו-c",               u"a",      u"b",      u"c" },
670         { he, ULISTFMT_TYPE_AND, u"a ו-b",                  u"a",      u"b",      nullptr },
671         { he, ULISTFMT_TYPE_AND, u"1, 2 ו-3",               u"1",      u"2",      u"3" },
672         { he, ULISTFMT_TYPE_AND, u"1 ו-2",                  u"1",      u"2",      nullptr },
673         { he, ULISTFMT_TYPE_AND, u"אהבה ומקווה",            u"אהבה",   u"מקווה",  nullptr },
674         { he, ULISTFMT_TYPE_AND, u"אהבה, מקווה ואמונה",     u"אהבה",   u"מקווה",  u"אמונה" },
675     };
676     for (auto width : widths) {
677         for (auto cas : cases) {
678             for (auto locale : cas.locales) {
679                 LocalPointer<ListFormatter> fmt(
680                     ListFormatter::createInstance(locale.c_str(), cas.type, width, status),
681                     status);
682                 if (status.errIfFailureAndReset()) {
683                     continue;
684                 }
685                 UnicodeString message = UnicodeString(u"TestContextual loc=")
686                     + locale.c_str() + u" type="
687                     + Int64ToUnicodeString(cas.type) + u" width="
688                     + Int64ToUnicodeString(width);
689                 if (cas.data3 == nullptr) {
690                     const UnicodeString inputs2[] = { cas.data1, cas.data2 };
691                     FormattedList result = fmt->formatStringsToValue(inputs2, UPRV_LENGTHOF(inputs2), status);
692                     assertEquals(message, cas.expected, result.toTempString(status));
693                 } else {
694                     const UnicodeString inputs3[] = { cas.data1, cas.data2, cas.data3 };
695                     FormattedList result = fmt->formatStringsToValue(inputs3, UPRV_LENGTHOF(inputs3), status);
696                     assertEquals(message, cas.expected, result.toTempString(status));
697                 }
698             }
699         }
700     }
701 }
702 
703 #endif /* #if !UCONFIG_NO_FORMATTING */
704