xref: /aosp_15_r20/external/cronet/third_party/icu/source/test/intltest/tmsgfmt.cpp (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 1997-2016, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  ********************************************************************
8  * File TMSGFMT.CPP
9  *
10  * Modification History:
11  *
12  *   Date        Name        Description
13  *   03/24/97    helena      Converted from Java.
14  *   07/11/97    helena      Updated to work on AIX.
15  *   08/04/97    jfitz       Updated to intltest
16  *******************************************************************/
17 
18 #include "unicode/utypes.h"
19 
20 #if !UCONFIG_NO_FORMATTING
21 
22 #include "tmsgfmt.h"
23 #include "cmemory.h"
24 #include "loctest.h"
25 
26 #include "unicode/format.h"
27 #include "unicode/decimfmt.h"
28 #include "unicode/localpointer.h"
29 #include "unicode/locid.h"
30 #include "unicode/msgfmt.h"
31 #include "unicode/numfmt.h"
32 #include "unicode/choicfmt.h"
33 #include "unicode/messagepattern.h"
34 #include "unicode/selfmt.h"
35 #include "unicode/gregocal.h"
36 #include "unicode/strenum.h"
37 #include <stdio.h>
38 
39 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)40 TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
41                                   const char* &name, char* /*par*/) {
42     TESTCASE_AUTO_BEGIN;
43     TESTCASE_AUTO(testBug1);
44     TESTCASE_AUTO(testBug2);
45     TESTCASE_AUTO(sample);
46     TESTCASE_AUTO(PatternTest);
47     TESTCASE_AUTO(testStaticFormat);
48     TESTCASE_AUTO(testSimpleFormat);
49     TESTCASE_AUTO(testMsgFormatChoice);
50     TESTCASE_AUTO(testCopyConstructor);
51     TESTCASE_AUTO(testAssignment);
52     TESTCASE_AUTO(testClone);
53     TESTCASE_AUTO(testEquals);
54     TESTCASE_AUTO(testNotEquals);
55     TESTCASE_AUTO(testSetLocale);
56     TESTCASE_AUTO(testFormat);
57     TESTCASE_AUTO(testParse);
58     TESTCASE_AUTO(testAdopt);
59     TESTCASE_AUTO(testCopyConstructor2);
60     TESTCASE_AUTO(TestUnlimitedArgsAndSubformats);
61     TESTCASE_AUTO(TestRBNF);
62     TESTCASE_AUTO(TestTurkishCasing);
63     TESTCASE_AUTO(testAutoQuoteApostrophe);
64     TESTCASE_AUTO(testMsgFormatPlural);
65     TESTCASE_AUTO(testMsgFormatSelect);
66     TESTCASE_AUTO(testApostropheInPluralAndSelect);
67     TESTCASE_AUTO(TestApostropheMode);
68     TESTCASE_AUTO(TestCompatibleApostrophe);
69     TESTCASE_AUTO(testCoverage);
70     TESTCASE_AUTO(testGetFormatNames);
71     TESTCASE_AUTO(TestTrimArgumentName);
72     TESTCASE_AUTO(TestSelectOrdinal);
73     TESTCASE_AUTO(TestDecimals);
74     TESTCASE_AUTO(TestArgIsPrefixOfAnother);
75     TESTCASE_AUTO(TestMessageFormatNumberSkeleton);
76     TESTCASE_AUTO(TestMessageFormatDateSkeleton);
77     TESTCASE_AUTO(TestMessageFormatTimeSkeleton);
78     TESTCASE_AUTO_END;
79 }
80 
testBug3()81 void TestMessageFormat::testBug3()
82 {
83     double myNumber = -123456;
84     DecimalFormat *form = 0;
85     Locale locale[] = {
86         Locale("ar", "", ""),
87         Locale("be", "", ""),
88         Locale("bg", "", ""),
89         Locale("ca", "", ""),
90         Locale("cs", "", ""),
91         Locale("da", "", ""),
92         Locale("de", "", ""),
93         Locale("de", "AT", ""),
94         Locale("de", "CH", ""),
95         Locale("el", "", ""),       // 10
96         Locale("en", "CA", ""),
97         Locale("en", "GB", ""),
98         Locale("en", "IE", ""),
99         Locale("en", "US", ""),
100         Locale("es", "", ""),
101         Locale("et", "", ""),
102         Locale("fi", "", ""),
103         Locale("fr", "", ""),
104         Locale("fr", "BE", ""),
105         Locale("fr", "CA", ""),     // 20
106         Locale("fr", "CH", ""),
107         Locale("he", "", ""),
108         Locale("hr", "", ""),
109         Locale("hu", "", ""),
110         Locale("is", "", ""),
111         Locale("it", "", ""),
112         Locale("it", "CH", ""),
113         Locale("ja", "", ""),
114         Locale("ko", "", ""),
115         Locale("lt", "", ""),       // 30
116         Locale("lv", "", ""),
117         Locale("mk", "", ""),
118         Locale("nl", "", ""),
119         Locale("nl", "BE", ""),
120         Locale("no", "", ""),
121         Locale("pl", "", ""),
122         Locale("pt", "", ""),
123         Locale("ro", "", ""),
124         Locale("ru", "", ""),
125         Locale("sh", "", ""),       // 40
126         Locale("sk", "", ""),
127         Locale("sl", "", ""),
128         Locale("sq", "", ""),
129         Locale("sr", "", ""),
130         Locale("sv", "", ""),
131         Locale("tr", "", ""),
132         Locale("uk", "", ""),
133         Locale("zh", "", ""),
134         Locale("zh", "TW", "")      // 49
135     };
136     int32_t i;
137     for (i= 0; i < 49; i++) {
138         UnicodeString buffer;
139         logln(locale[i].getDisplayName(buffer));
140         UErrorCode success = U_ZERO_ERROR;
141 //        form = (DecimalFormat*)NumberFormat::createCurrencyInstance(locale[i], success);
142         form = dynamic_cast<DecimalFormat*>(NumberFormat::createInstance(locale[i], success));
143         if (U_FAILURE(success)) {
144             errln("Err: Number Format ");
145             logln("Number format creation failed.");
146             continue;
147         }
148         Formattable result;
149         FieldPosition pos(FieldPosition::DONT_CARE);
150         buffer.remove();
151         form->format(myNumber, buffer, pos);
152         success = U_ZERO_ERROR;
153         ParsePosition parsePos;
154         form->parse(buffer, result, parsePos);
155         logln(UnicodeString(" -> ") /* + << dec*/ + toString(result) + UnicodeString("[supposed output for result]"));
156         if (U_FAILURE(success)) {
157             errln("Err: Number Format parse");
158             logln("Number format parse failed.");
159         }
160         delete form;
161     }
162 }
163 
testBug1()164 void TestMessageFormat::testBug1()
165 {
166     const double limit[] = {0.0, 1.0, 2.0};
167     const UnicodeString formats[] = {"0.0<=Arg<1.0",
168                                "1.0<=Arg<2.0",
169                                "2.0<-Arg"};
170     ChoiceFormat *cf = new ChoiceFormat(limit, formats, 3);
171     FieldPosition status(FieldPosition::DONT_CARE);
172     UnicodeString toAppendTo;
173     cf->format((int32_t)1, toAppendTo, status);
174     if (toAppendTo != "1.0<=Arg<2.0") {
175         errln("ChoiceFormat cmp in testBug1");
176     }
177     logln(toAppendTo);
178     delete cf;
179 }
180 
testBug2()181 void TestMessageFormat::testBug2()
182 {
183     UErrorCode status = U_ZERO_ERROR;
184     UnicodeString result;
185     // {sfb} use double format in pattern, so result will match (not strictly necessary)
186     const UnicodeString pattern = "There {0,choice,0#are no files|1#is one file|1<are {0, number} files} on disk {1}. ";
187     logln("The input pattern : " + pattern);
188     LocalPointer<MessageFormat> fmt(new MessageFormat(pattern, status));
189     if (U_FAILURE(status)) {
190         dataerrln("MessageFormat pattern creation failed. - %s", u_errorName(status));
191         return;
192     }
193     logln("The output pattern is : " + fmt->toPattern(result));
194     if (pattern != result) {
195         errln("MessageFormat::toPattern() failed.");
196     }
197 }
198 
199 #if 0
200 #if defined(_DEBUG)
201 //----------------------------------------------------
202 // console I/O
203 //----------------------------------------------------
204 
205 #include <iostream>
206 std::ostream& operator<<(std::ostream& stream,  const Formattable&   obj);
207 
208 #include "unicode/datefmt.h"
209 #include <stdlib.h>
210 #include <string.h>
211 
212 IntlTest&
213 operator<<( IntlTest&           stream,
214             const Formattable&  obj)
215 {
216     static DateFormat *defDateFormat = 0;
217 
218     UnicodeString buffer;
219     switch(obj.getType()) {
220         case Formattable::kDate :
221             if (defDateFormat == 0) {
222                 defDateFormat = DateFormat::createInstance();
223             }
224             defDateFormat->format(obj.getDate(), buffer);
225             stream << buffer;
226             break;
227         case Formattable::kDouble :
228             char convert[20];
229             snprintf( convert, sizeof(convert), "%lf", obj.getDouble() );
230             stream << convert << "D";
231             break;
232         case Formattable::kLong :
233             stream << obj.getLong() << "L";
234             break;
235         case Formattable::kString:
236             stream << "\"" << obj.getString(buffer) << "\"";
237             break;
238         case Formattable::kArray:
239             int32_t i, count;
240             const Formattable* array;
241             array = obj.getArray(count);
242             stream << "[";
243             for (i=0; i<count; ++i) stream << array[i] << ( (i==(count-1)) ? "" : ", " );
244             stream << "]";
245             break;
246         default:
247             stream << "INVALID_Formattable";
248     }
249     return stream;
250 }
251 #endif /* defined(_DEBUG) */
252 #endif
253 
PatternTest()254 void TestMessageFormat::PatternTest()
255 {
256     Formattable testArgs[] = {
257         Formattable(double(1)), Formattable(double(3456)),
258             Formattable("Disk"), Formattable(UDate((int32_t)1000000000L), Formattable::kIsDate)
259     };
260     UnicodeString testCases[] = {
261        "Quotes '', '{', 'a' {0} '{0}'",
262        "Quotes '', '{', 'a' {0,number} '{0}'",
263        "'{'1,number,'#',##} {1,number,'#',##}",
264        "There are {1} files on {2} at {3}.",
265        "On {2}, there are {1} files, with {0,number,currency}.",
266        "'{1,number,percent}', {1,number,percent},",
267        "'{1,date,full}', {1,date,full},",
268        "'{3,date,full}', {3,date,full},",
269        "'{1,number,#,##}' {1,number,#,##}",
270     };
271 
272     // ICU 4.8 returns the original pattern (testCases),
273     // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
274     /*UnicodeString testResultPatterns[] = {
275         "Quotes '', '{', a {0} '{'0}",
276         "Quotes '', '{', a {0,number} '{'0}",
277         "'{'1,number,#,##} {1,number,'#'#,##}",
278         "There are {1} files on {2} at {3}.",
279         "On {2}, there are {1} files, with {0,number,currency}.",
280         "'{'1,number,percent}, {1,number,percent},",
281         "'{'1,date,full}, {1,date,full},",
282         "'{'3,date,full}, {3,date,full},",
283         "'{'1,number,#,##} {1,number,#,##}"
284     };*/
285 
286     UnicodeString testResultStrings[] = {
287         u"Quotes ', {, 'a' 1 {0}",
288         u"Quotes ', {, 'a' 1 {0}",
289         u"{1,number,'#',##} #34,56",
290         u"There are 3,456 files on Disk at 1/12/70, 5:46\u202FAM.",
291         u"On Disk, there are 3,456 files, with $1.00.",
292         u"{1,number,percent}, 345,600%,",
293         u"{1,date,full}, Wednesday, December 31, 1969,",
294         u"{3,date,full}, Monday, January 12, 1970,",
295         u"{1,number,#,##} 34,56"
296     };
297 
298 
299     for (int32_t i = 0; i < 9; ++i) {
300         //it_out << "\nPat in:  " << testCases[i]);
301 
302         LocalPointer<MessageFormat> form;
303         UErrorCode success = U_ZERO_ERROR;
304         UnicodeString buffer;
305         form.adoptInstead(new MessageFormat(testCases[i], Locale::getUS(), success));
306         if (U_FAILURE(success)) {
307             dataerrln("MessageFormat creation failed.#1 - %s", u_errorName(success));
308             logln(((UnicodeString)"MessageFormat for ") + testCases[i] + " creation failed.\n");
309             continue;
310         }
311         // ICU 4.8 returns the original pattern (testCases),
312         // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
313         if (form->toPattern(buffer) != testCases[i]) {
314             // Note: An alternative test would be to build MessagePattern objects for
315             // both the input and output patterns and compare them, taking SKIP_SYNTAX etc.
316             // into account.
317             // (Too much trouble...)
318             errln(UnicodeString("TestMessageFormat::PatternTest failed test #2, i = ") + i);
319             //form->toPattern(buffer);
320             errln(((UnicodeString)" Orig: ") + testCases[i]);
321             errln(((UnicodeString)" Exp:  ") + testCases[i]);
322             errln(((UnicodeString)" Got:  ") + buffer);
323         }
324 
325         //it_out << "Pat out: " << form->toPattern(buffer));
326         UnicodeString result;
327         int32_t count = 4;
328         FieldPosition fieldpos(FieldPosition::DONT_CARE);
329         form->format(testArgs, count, result, fieldpos, success);
330         if (U_FAILURE(success)) {
331             dataerrln("MessageFormat failed test #3 - %s", u_errorName(success));
332             logln("TestMessageFormat::PatternTest failed test #3");
333             continue;
334         }
335         if (result != testResultStrings[i]) {
336             errln(UnicodeString("TestMessageFormat::PatternTest failed test #4.") +
337                 UnicodeString("\n    Result: ") + result +
338                 UnicodeString("\n  Expected: ") + testResultStrings[i]);
339             logln(UnicodeString("TestMessageFormat::PatternTest failed test #4.") +
340                 UnicodeString("\n    Result: ") + result +
341                 UnicodeString("\n  Expected: ") + testResultStrings[i]);
342         }
343 
344 
345         //it_out << "Result:  " << result);
346 #if 0
347         /* TODO: Look at this test and see if this is still a valid test */
348         logln("---------------- test parse ----------------");
349 
350         form->toPattern(buffer);
351         logln("MSG pattern for parse: " + buffer);
352 
353         int32_t parseCount = 0;
354         Formattable* values = form->parse(result, parseCount, success);
355         if (U_FAILURE(success)) {
356             errln("MessageFormat failed test #5");
357             logln(UnicodeString("MessageFormat failed test #5 with error code ")+(int32_t)success);
358         } else if (parseCount != count) {
359             errln("MSG count not %d as expected. Got %d", count, parseCount);
360         }
361         UBool failed = false;
362         for (int32_t j = 0; j < parseCount; ++j) {
363              if (values == 0 || testArgs[j] != values[j]) {
364                 errln(((UnicodeString)"MSG testargs[") + j + "]: " + toString(testArgs[j]));
365                 errln(((UnicodeString)"MSG values[") + j + "]  : " + toString(values[j]));
366                 failed = true;
367              }
368         }
369         if (failed)
370             errln("MessageFormat failed test #6");
371 #endif
372     }
373 }
374 
sample()375 void TestMessageFormat::sample()
376 {
377     MessageFormat *form = 0;
378     UnicodeString buffer1, buffer2;
379     UErrorCode success = U_ZERO_ERROR;
380     form = new MessageFormat("There are {0} files on {1}", success);
381     if (U_FAILURE(success)) {
382         errln("Err: Message format creation failed");
383         logln("Sample message format creation failed.");
384         return;
385     }
386     UnicodeString abc("abc");
387     UnicodeString def("def");
388     Formattable testArgs1[] = { abc, def };
389     FieldPosition fieldpos(FieldPosition::DONT_CARE);
390     assertEquals("format",
391                  "There are abc files on def",
392                  form->format(testArgs1, 2, buffer2, fieldpos, success));
393     assertSuccess("format", success);
394     delete form;
395 }
396 
testStaticFormat()397 void TestMessageFormat::testStaticFormat()
398 {
399     UErrorCode err = U_ZERO_ERROR;
400     Formattable arguments[] = {
401         (int32_t)7,
402         Formattable(UDate(8.71068e+011), Formattable::kIsDate),
403         "a disturbance in the Force"
404         };
405 
406     UnicodeString result;
407     result = MessageFormat::format(
408         "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
409         arguments,
410         3,
411         result,
412         err);
413 
414     if (U_FAILURE(err)) {
415         dataerrln("TestMessageFormat::testStaticFormat #1 - %s", u_errorName(err));
416         logln(UnicodeString("TestMessageFormat::testStaticFormat failed test #1 with error code ")+(int32_t)err);
417         return;
418     }
419 
420     const UnicodeString expected(
421             u"At 12:20:00\u202FPM on Aug 8, 1997, there was a disturbance in the Force on planet 7.");
422     if (result != expected) {
423         errln(UnicodeString("TestMessageFormat::testStaticFormat failed on test") +
424             UnicodeString("\n     Result: ") + result +
425             UnicodeString("\n   Expected: ") + expected );
426     }
427 }
428 
429 /* When the default locale is tr, make sure that the pattern can still be parsed. */
TestTurkishCasing()430 void TestMessageFormat::TestTurkishCasing()
431 {
432     UErrorCode err = U_ZERO_ERROR;
433     Locale  saveDefaultLocale;
434     Locale::setDefault( Locale("tr"), err );
435 
436     Formattable arguments[] = {
437         (int32_t)7,
438         Formattable(UDate(8.71068e+011), Formattable::kIsDate),
439         "a disturbance in the Force"
440         };
441 
442     UnicodeString result;
443     result = MessageFormat::format(
444         "At {1,TIME} on {1,DATE,SHORT}, there was {2} on planet {0,NUMBER,INTEGER}.",
445         arguments,
446         3,
447         result,
448         err);
449 
450     if (U_FAILURE(err)) {
451         dataerrln("TestTurkishCasing #1 with error code %s", u_errorName(err));
452         return;
453     }
454 
455     const UnicodeString expected(
456             "At 12:20:00 on 8.08.1997, there was a disturbance in the Force on planet 7.", "");
457     if (result != expected) {
458         errln("TestTurkishCasing failed on test");
459         errln( UnicodeString("     Result: ") + result );
460         errln( UnicodeString("   Expected: ") + expected );
461     }
462     Locale::setDefault( saveDefaultLocale, err );
463 }
464 
testSimpleFormat()465 void TestMessageFormat::testSimpleFormat(/* char* par */)
466 {
467     logln("running TestMessageFormat::testSimpleFormat");
468 
469     UErrorCode err = U_ZERO_ERROR;
470 
471     Formattable testArgs1[] = {(int32_t)0, "MyDisk"};
472     Formattable testArgs2[] = {(int32_t)1, "MyDisk"};
473     Formattable testArgs3[] = {(int32_t)12, "MyDisk"};
474 
475     MessageFormat* form = new MessageFormat(
476         "The disk \"{1}\" contains {0} file(s).", err);
477 
478     UnicodeString string;
479     FieldPosition ignore(FieldPosition::DONT_CARE);
480     form->format(testArgs1, 2, string, ignore, err);
481     if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 0 file(s).") {
482         dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #1 - ") + u_errorName(err));
483     }
484 
485     ignore.setField(FieldPosition::DONT_CARE);
486     string.remove();
487     form->format(testArgs2, 2, string, ignore, err);
488     if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 1 file(s).") {
489         logln(string);
490         dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #2")+string + " - " + u_errorName(err));
491     }
492 
493     ignore.setField(FieldPosition::DONT_CARE);
494     string.remove();
495     form->format(testArgs3, 2, string, ignore, err);
496     if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 12 file(s).") {
497         dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #3")+string + " - " + u_errorName(err));
498     }
499 
500     delete form;
501  }
502 
testMsgFormatChoice()503 void TestMessageFormat::testMsgFormatChoice(/* char* par */)
504 {
505     logln("running TestMessageFormat::testMsgFormatChoice");
506 
507     UErrorCode err = U_ZERO_ERROR;
508 
509     MessageFormat* form = new MessageFormat("The disk \"{1}\" contains {0}.", err);
510     double filelimits[] = {0,1,2};
511     UnicodeString filepart[] = {"no files","one file","{0,number} files"};
512     ChoiceFormat* fileform = new ChoiceFormat(filelimits, filepart, 3);
513     form->setFormat(1,*fileform); // NOT zero, see below
514         //is the format adopted?
515 
516     FieldPosition ignore(FieldPosition::DONT_CARE);
517     UnicodeString string;
518     Formattable testArgs1[] = {(int32_t)0, "MyDisk"};
519     form->format(testArgs1, 2, string, ignore, err);
520     if (string != "The disk \"MyDisk\" contains no files.") {
521         errln("TestMessageFormat::testMsgFormatChoice failed on test #1");
522     }
523 
524     ignore.setField(FieldPosition::DONT_CARE);
525     string.remove();
526     Formattable testArgs2[] = {(int32_t)1, "MyDisk"};
527     form->format(testArgs2, 2, string, ignore, err);
528     if (string != "The disk \"MyDisk\" contains one file.") {
529         errln("TestMessageFormat::testMsgFormatChoice failed on test #2");
530     }
531 
532     ignore.setField(FieldPosition::DONT_CARE);
533     string.remove();
534     Formattable testArgs3[] = {(int32_t)1273, "MyDisk"};
535     form->format(testArgs3, 2, string, ignore, err);
536     if (string != "The disk \"MyDisk\" contains 1,273 files.") {
537         dataerrln("TestMessageFormat::testMsgFormatChoice failed on test #3 - %s", u_errorName(err));
538     }
539 
540     delete form;
541     delete fileform;
542 }
543 
544 
testMsgFormatPlural()545 void TestMessageFormat::testMsgFormatPlural(/* char* par */)
546 {
547     logln("running TestMessageFormat::testMsgFormatPlural");
548 
549     UErrorCode err = U_ZERO_ERROR;
550     UnicodeString t1("{0, plural, one{C''est # fichier} other{Ce sont # fichiers}} dans la liste.");
551     UnicodeString t2("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste.");
552     UnicodeString t3("There {0, plural, one{is # zavod}few{are {0, number,###.0} zavoda} other{are # zavodov}} in the directory.");
553     UnicodeString t4("There {argument, plural, one{is # zavod}few{are {argument, number,###.0} zavoda} other{are #zavodov}} in the directory.");
554     UnicodeString t5("{0, plural, one {{0, number,C''est #,##0.0# fichier}} other {Ce sont # fichiers}} dans la liste.");
555     MessageFormat* mfNum = new MessageFormat(t1, Locale("fr"), err);
556     if (U_FAILURE(err)) {
557         dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentIndex - %s", u_errorName(err));
558         logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err);
559         return;
560     }
561     Formattable testArgs1((int32_t)0);
562     FieldPosition ignore(FieldPosition::DONT_CARE);
563     UnicodeString numResult1;
564     mfNum->format(&testArgs1, 1, numResult1, ignore, err);
565 
566     MessageFormat* mfAlpha = new MessageFormat(t2, Locale("fr"), err);
567     UnicodeString argName[] = {UnicodeString("argument")};
568     UnicodeString argNameResult;
569     mfAlpha->format(argName, &testArgs1, 1, argNameResult, err);
570     if (U_FAILURE(err)) {
571         dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentName - %s", u_errorName(err));
572         logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err);
573         delete mfNum;
574         return;
575     }
576     if ( numResult1 != argNameResult){
577         errln("TestMessageFormat::testMsgFormatPlural #1");
578         logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
579     }
580     if ( numResult1 != UnicodeString("C\'est 0 fichier dans la liste.")) {
581         errln("TestMessageFormat::testMsgFormatPlural #1");
582         logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
583     }
584     err = U_ZERO_ERROR;
585 
586     delete mfNum;
587     delete mfAlpha;
588 
589     MessageFormat* mfNum2 = new MessageFormat(t3, Locale("uk"), err);
590     numResult1.remove();
591     Formattable testArgs2((int32_t)4);
592     mfNum2->format(&testArgs2, 1, numResult1, ignore, err);
593     MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("uk"), err);
594     argNameResult.remove();
595     mfAlpha2->format(argName, &testArgs2, 1, argNameResult, err);
596 
597     if (U_FAILURE(err)) {
598         errln("TestMessageFormat::testMsgFormatPlural #2 - argumentName");
599         logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #2 with error code ")+(int32_t)err);
600         delete mfNum2;
601         return;
602     }
603     if ( numResult1 != argNameResult){
604         errln("TestMessageFormat::testMsgFormatPlural #2");
605         logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
606     }
607     if ( numResult1 != UnicodeString("There are 4,0 zavoda in the directory.")) {
608         errln("TestMessageFormat::testMsgFormatPlural #2");
609         logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
610     }
611 
612     delete mfNum2;
613     delete mfAlpha2;
614 
615     // nested formats
616     err = U_ZERO_ERROR;
617     MessageFormat* msgFmt = new MessageFormat(t5, Locale("fr"), err);
618     if (U_FAILURE(err)) {
619         errln("TestMessageFormat::test nested PluralFormat with argumentName");
620         logln(UnicodeString("TestMessageFormat::test nested PluralFormat with error code ")+(int32_t)err);
621         delete msgFmt;
622         return;
623     }
624     Formattable testArgs3((int32_t)0);
625     argNameResult.remove();
626     msgFmt->format(&testArgs3, 1, argNameResult, ignore, err);
627     if (U_FAILURE(err)) {
628         errln("TestMessageFormat::test nested PluralFormat with argumentName");
629     }
630     if ( argNameResult!= UnicodeString("C'est 0,0 fichier dans la liste.")) {
631         errln(UnicodeString("TestMessageFormat::test nested named PluralFormat: ") + argNameResult);
632         logln(UnicodeString("The unexpected nested named PluralFormat."));
633     }
634     delete msgFmt;
635 }
636 
testApostropheInPluralAndSelect()637 void TestMessageFormat::testApostropheInPluralAndSelect() {
638     UErrorCode errorCode = U_ZERO_ERROR;
639     MessageFormat msgFmt(UNICODE_STRING_SIMPLE(
640         "abc_{0,plural,other{#'#'#'{'#''}}_def_{1,select,other{sel'}'ect''}}_xyz"),
641         Locale::getEnglish(),
642         errorCode);
643     if (U_FAILURE(errorCode)) {
644         errln("MessageFormat constructor failed - %s\n", u_errorName(errorCode));
645         return;
646     }
647     UnicodeString expected = UNICODE_STRING_SIMPLE("abc_3#3{3'_def_sel}ect'_xyz");
648     Formattable args[] = { (int32_t)3, UNICODE_STRING_SIMPLE("x") };
649     internalFormat(
650         &msgFmt, args, 2, expected,
651         "MessageFormat with apostrophes in plural/select arguments failed:\n");
652 }
653 
internalFormat(MessageFormat * msgFmt,Formattable * args,int32_t numOfArgs,UnicodeString expected,const char * errMsg)654 void TestMessageFormat::internalFormat(MessageFormat* msgFmt ,
655         Formattable* args , int32_t numOfArgs ,
656         UnicodeString expected, const char* errMsg)
657 {
658         UnicodeString result;
659         FieldPosition ignore(FieldPosition::DONT_CARE);
660         UErrorCode status = U_ZERO_ERROR;
661 
662         //Format with passed arguments
663         msgFmt->format( args , numOfArgs , result, ignore, status);
664         if (U_FAILURE(status)) {
665             dataerrln( "%s error while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
666         }
667         //Compare expected with obtained result
668         if ( result!= expected ) {
669             UnicodeString err = UnicodeString(errMsg);
670             err+= UnicodeString(":Unexpected Result \n Expected: " + expected + "\n Obtained: " + result + "\n");
671             dataerrln(err);
672         }
673 }
674 
internalCreate(UnicodeString pattern,Locale locale,UErrorCode & status,char * errMsg)675 MessageFormat* TestMessageFormat::internalCreate(
676         UnicodeString pattern ,Locale locale ,UErrorCode &status ,  char* errMsg)
677 {
678     //Create the MessageFormat with simple SelectFormat
679     LocalPointer<MessageFormat> msgFmt(new MessageFormat(pattern, locale, status));
680     if (U_FAILURE(status)) {
681         dataerrln( "%s error while constructing with ErrorCode as %s" ,errMsg, u_errorName(status) );
682         logln(UnicodeString("TestMessageFormat::testMsgFormatSelect #1 with error code ")+(int32_t)status);
683         return nullptr;
684     }
685     return msgFmt.orphan();
686 }
687 
testMsgFormatSelect()688 void TestMessageFormat::testMsgFormatSelect(/* char* par */)
689 {
690     logln("running TestMessageFormat::testMsgFormatSelect");
691 
692     UErrorCode err = U_ZERO_ERROR;
693     //French Pattern
694     UnicodeString t1("{0} est {1, select, female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.");
695 
696     err = U_ZERO_ERROR;
697     //Create the MessageFormat with simple French pattern
698     MessageFormat* msgFmt1 = internalCreate(t1.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t1");
699     if (!U_FAILURE(err)) {
700         //Arguments
701         Formattable testArgs10[] = {"Kirti","female"};
702         Formattable testArgs11[] = {"Victor","other"};
703         Formattable testArgs12[] = {"Ash","unknown"};
704         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
705         UnicodeString exp[] = {
706             "Kirti est all\\u00E9e \\u00E0 Paris." ,
707             "Victor est all\\u00E9 \\u00E0 Paris.",
708             "Ash est all\\u00E9 \\u00E0 Paris."};
709         //Format
710         for( int i=0; i< 3; i++){
711             internalFormat( msgFmt1 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t1");
712         }
713     }
714     delete msgFmt1;
715 
716     //Quoted French Pattern
717     UnicodeString t2("{0} est {1, select, female {all\\u00E9e c''est} other {all\\u00E9 c''est}} \\u00E0 Paris.");
718     err = U_ZERO_ERROR;
719     //Create the MessageFormat with Quoted French pattern
720     MessageFormat* msgFmt2 = internalCreate(t2.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t2");
721     if (!U_FAILURE(err)) {
722         //Arguments
723         Formattable testArgs10[] = {"Kirti","female"};
724         Formattable testArgs11[] = {"Victor","other"};
725         Formattable testArgs12[] = {"Ash","male"};
726         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
727         UnicodeString exp[] = {
728             "Kirti est all\\u00E9e c'est \\u00E0 Paris." ,
729             "Victor est all\\u00E9 c'est \\u00E0 Paris.",
730             "Ash est all\\u00E9 c'est \\u00E0 Paris."};
731         //Format
732         for( int i=0; i< 3; i++){
733             internalFormat( msgFmt2 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t2");
734         }
735     }
736     delete msgFmt2;
737 
738     //English Pattern
739     UnicodeString t3("{0, select , male {MALE FR company} female {FEMALE FR company} other {FR otherValue}} published new books.");
740     err = U_ZERO_ERROR;
741     //Create the MessageFormat with English pattern
742     MessageFormat* msgFmt3 = internalCreate(t3, Locale("en"),err,(char*)"From TestMessageFormat::TestSelectFormat create t3");
743     if (!U_FAILURE(err)) {
744         //Arguments
745         Formattable testArgs10[] = {"female"};
746         Formattable testArgs11[] = {"other"};
747         Formattable testArgs12[] = {"male"};
748         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
749         UnicodeString exp[] = {
750             "FEMALE FR company published new books." ,
751             "FR otherValue published new books.",
752             "MALE FR company published new books."};
753         //Format
754         for( int i=0; i< 3; i++){
755             internalFormat( msgFmt3 , testArgs[i], 1, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t3");
756         }
757     }
758     delete msgFmt3;
759 
760     //Nested patterns with plural, number ,choice ,select format etc.
761     //Select Format with embedded number format
762     UnicodeString t4("{0} est {1, select, female {{2,number,integer} all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.");
763     err = U_ZERO_ERROR;
764     //Create the MessageFormat with Select Format with embedded number format (nested pattern)
765     MessageFormat* msgFmt4 = internalCreate(t4.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t4");
766     if (!U_FAILURE(err)) {
767         //Arguments
768         Formattable testArgs10[] = {"Kirti","female",(int32_t)6};
769         Formattable testArgs11[] = {"Kirti","female",100.100};
770         Formattable testArgs12[] = {"Kirti","other",(int32_t)6};
771         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
772         UnicodeString exp[] = {
773             "Kirti est 6 all\\u00E9e \\u00E0 Paris." ,
774             "Kirti est 100 all\\u00E9e \\u00E0 Paris.",
775             "Kirti est all\\u00E9 \\u00E0 Paris."};
776         //Format
777         for( int i=0; i< 3; i++){
778             internalFormat( msgFmt4 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t4");
779         }
780     }
781     delete msgFmt4;
782 
783     //Plural format with embedded select format
784     UnicodeString t5("{0} {1, plural, one {est {2, select, female {all\\u00E9e} other {all\\u00E9}}} other {sont {2, select, female {all\\u00E9es} other {all\\u00E9s}}}} \\u00E0 Paris.");
785     err = U_ZERO_ERROR;
786     //Create the MessageFormat with Plural format with embedded select format(nested pattern)
787     MessageFormat* msgFmt5 = internalCreate(t5.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t5");
788     // with no data the above should fail but it seems to construct an invalid MessageFormat with no reported error. See #13079
789     if (!U_FAILURE(err)) {
790         //Arguments
791         Formattable testArgs10[] = {"Kirti",(int32_t)6,"female"};
792         Formattable testArgs11[] = {"Kirti",(int32_t)1,"female"};
793         Formattable testArgs12[] = {"Ash",(int32_t)1,"other"};
794         Formattable testArgs13[] = {"Ash",(int32_t)5,"other"};
795         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13};
796         UnicodeString exp[] = {
797             "Kirti sont all\\u00E9es \\u00E0 Paris." ,
798             "Kirti est all\\u00E9e \\u00E0 Paris.",
799             "Ash est all\\u00E9 \\u00E0 Paris.",
800             "Ash sont all\\u00E9s \\u00E0 Paris."};
801         //Format
802         for( int i=0; i< 4; i++){
803             internalFormat( msgFmt5 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t5");
804         }
805     }
806     delete msgFmt5;
807 
808     err = U_ZERO_ERROR;
809     //Select, plural, and number formats heavily nested
810     UnicodeString t6("{0} und {1, select, female {{2, plural, one {{3, select, female {ihre Freundin} other {ihr Freund}} } other {ihre {2, number, integer} {3, select, female {Freundinnen} other {Freunde}} } }} other{{2, plural, one {{3, select, female {seine Freundin} other {sein Freund}}} other {seine {2, number, integer} {3, select, female {Freundinnen} other {Freunde}}}}} } gingen nach Paris.");
811     //Create the MessageFormat with Select, plural, and number formats heavily nested
812     LocalPointer<MessageFormat> msgFmt6(
813             internalCreate(t6, Locale("de"),err,(char*)"From TestMessageFormat::TestSelectFormat create t6"));
814     if (!U_FAILURE(err)) {
815         //Arguments
816         Formattable testArgs10[] = {"Kirti","other",(int32_t)1,"other"};
817         Formattable testArgs11[] = {"Kirti","other",(int32_t)6,"other"};
818         Formattable testArgs12[] = {"Kirti","other",(int32_t)1,"female"};
819         Formattable testArgs13[] = {"Kirti","other",(int32_t)3,"female"};
820         Formattable testArgs14[] = {"Kirti","female",(int32_t)1,"female"};
821         Formattable testArgs15[] = {"Kirti","female",(int32_t)5,"female"};
822         Formattable testArgs16[] = {"Kirti","female",(int32_t)1,"other"};
823         Formattable testArgs17[] = {"Kirti","female",(int32_t)5,"other"};
824         Formattable testArgs18[] = {"Kirti","mixed",(int32_t)1,"mixed"};
825         Formattable testArgs19[] = {"Kirti","mixed",(int32_t)1,"other"};
826         Formattable testArgs20[] = {"Kirti","female",(int32_t)1,"mixed"};
827         Formattable testArgs21[] = {"Kirti","mixed",(int32_t)5,"mixed"};
828         Formattable testArgs22[] = {"Kirti","mixed",(int32_t)5,"other"};
829         Formattable testArgs23[] = {"Kirti","female",(int32_t)5,"mixed"};
830         Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13,
831                                    testArgs14,testArgs15,testArgs16,testArgs17,
832                                    testArgs18,testArgs19,testArgs20,testArgs21,
833                                    testArgs22,testArgs23 };
834         UnicodeString exp[] = {
835             "Kirti und sein Freund gingen nach Paris." ,
836             "Kirti und seine 6 Freunde gingen nach Paris." ,
837             "Kirti und seine Freundin gingen nach Paris.",
838             "Kirti und seine 3 Freundinnen gingen nach Paris.",
839             "Kirti und ihre Freundin  gingen nach Paris.",
840             "Kirti und ihre 5 Freundinnen  gingen nach Paris.",
841             "Kirti und ihr Freund  gingen nach Paris.",
842             "Kirti und ihre 5 Freunde  gingen nach Paris.",
843             "Kirti und sein Freund gingen nach Paris.",
844             "Kirti und sein Freund gingen nach Paris.",
845             "Kirti und ihr Freund  gingen nach Paris.",
846             "Kirti und seine 5 Freunde gingen nach Paris." ,
847             "Kirti und seine 5 Freunde gingen nach Paris." ,
848             "Kirti und ihre 5 Freunde  gingen nach Paris."
849         };
850         //Format
851         for( int i=0; i< 14; i++){
852             internalFormat( msgFmt6.getAlias(), testArgs[i], 4, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t6");
853         }
854     }
855 }
856 
857 //---------------------------------
858 //  API Tests
859 //---------------------------------
860 
testCopyConstructor()861 void TestMessageFormat::testCopyConstructor()
862 {
863     UErrorCode success = U_ZERO_ERROR;
864     MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
865     MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
866     MessageFormat *y = 0;
867     y = new MessageFormat(*x);
868     if ( (*x == *y) &&
869          (*x != *z) &&
870          (*y != *z) )
871          logln("First test (operator ==): Passed!");
872     else {
873         errln("TestMessageFormat::testCopyConstructor failed #1");
874         logln("First test (operator ==): Failed!");
875     }
876     if ( ((*x == *y) && (*y == *x)) &&
877          ((*x != *z) && (*z != *x)) &&
878          ((*y != *z) && (*z != *y)) )
879         logln("Second test (equals): Passed!");
880     else {
881         errln("TestMessageFormat::testCopyConstructor failed #2");
882         logln("Second test (equals): Failed!");
883     }
884 
885     delete x;
886     delete y;
887     delete z;
888 }
889 
890 
testAssignment()891 void TestMessageFormat::testAssignment()
892 {
893     UErrorCode success = U_ZERO_ERROR;
894     MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
895     MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
896     MessageFormat *y = new MessageFormat("There are {0} files on {1} created", success);
897     *y = *x;
898     if ( (*x == *y) &&
899          (*x != *z) &&
900          (*y != *z) )
901         logln("First test (operator ==): Passed!");
902     else {
903         errln( "TestMessageFormat::testAssignment failed #1");
904         logln("First test (operator ==): Failed!");
905     }
906     if ( ((*x == *y) && (*y == *x)) &&
907          ((*x != *z) && (*z != *x)) &&
908          ((*y != *z) && (*z != *y)) )
909         logln("Second test (equals): Passed!");
910     else {
911         errln("TestMessageFormat::testAssignment failed #2");
912         logln("Second test (equals): Failed!");
913     }
914 
915     delete x;
916     delete y;
917     delete z;
918 }
919 
testClone()920 void TestMessageFormat::testClone()
921 {
922     UErrorCode success = U_ZERO_ERROR;
923     MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
924     MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
925     MessageFormat *y = 0;
926     y = x->clone();
927     if ( (*x == *y) &&
928          (*x != *z) &&
929          (*y != *z) )
930         logln("First test (operator ==): Passed!");
931     else {
932         errln("TestMessageFormat::testClone failed #1");
933         logln("First test (operator ==): Failed!");
934     }
935     if ( ((*x == *y) && (*y == *x)) &&
936          ((*x != *z) && (*z != *x)) &&
937          ((*y != *z) && (*z != *y)) )
938         logln("Second test (equals): Passed!");
939     else {
940         errln("TestMessageFormat::testClone failed #2");
941         logln("Second test (equals): Failed!");
942     }
943 
944     delete x;
945     delete y;
946     delete z;
947 }
948 
testEquals()949 void TestMessageFormat::testEquals()
950 {
951     UErrorCode success = U_ZERO_ERROR;
952     MessageFormat x("There are {0} files on {1}", success);
953     MessageFormat y("There are {0} files on {1}", success);
954     if (!(x == y)) {
955         errln( "TestMessageFormat::testEquals failed #1");
956         logln("First test (operator ==): Failed!");
957     }
958 
959 }
960 
testNotEquals()961 void TestMessageFormat::testNotEquals()
962 {
963     UErrorCode success = U_ZERO_ERROR;
964     MessageFormat x("There are {0} files on {1}", success);
965     MessageFormat y(x);
966     y.setLocale(Locale("fr"));
967     if (!(x != y)) {
968         errln( "TestMessageFormat::testEquals failed #1");
969         logln("First test (operator !=): Failed!");
970     }
971     y = x;
972     y.applyPattern("There are {0} files on {1} the disk", success);
973     if (!(x != y)) {
974         errln( "TestMessageFormat::testEquals failed #1");
975         logln("Second test (operator !=): Failed!");
976     }
977 }
978 
979 
testSetLocale()980 void TestMessageFormat::testSetLocale()
981 {
982     UErrorCode err = U_ZERO_ERROR;
983     GregorianCalendar cal(err);
984     Formattable arguments[] = {
985         456.83,
986         Formattable(UDate(8.71068e+011), Formattable::kIsDate),
987         "deposit"
988         };
989 
990     UnicodeString result;
991 
992     //UnicodeString formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
993     UnicodeString formatStr = "At <time> on {1,date}, you made a {2} of {0,number,currency}.";
994     // {sfb} to get $, would need Locale::US, not Locale::ENGLISH
995     // Just use unlocalized currency symbol.
996     //UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of $456.83.";
997     UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of ";
998     compareStrEng += (char16_t) 0x00a4;
999     compareStrEng += "456.83.";
1000     // {sfb} to get DM, would need Locale::GERMANY, not Locale::GERMAN
1001     // Just use unlocalized currency symbol.
1002     //UnicodeString compareStrGer = "At <time> on 08.08.1997, you made a deposit of 456,83 DM.";
1003     UnicodeString compareStrGer = "At <time> on 08.08.1997, you made a deposit of ";
1004     compareStrGer += "456,83";
1005     compareStrGer += (char16_t) 0x00a0;
1006     compareStrGer += "XXX.";
1007 
1008     MessageFormat msg( formatStr, err);
1009     result = "";
1010     FieldPosition pos(FieldPosition::DONT_CARE);
1011     result = msg.format(
1012         arguments,
1013         3,
1014         result,
1015         pos,
1016         err);
1017 
1018     logln(result);
1019     if (result != compareStrEng) {
1020         char bbuf[96];
1021         result.extract(0, result.length(), bbuf, sizeof(bbuf));
1022         dataerrln("***  MSG format err. - %s; result was %s", u_errorName(err), bbuf);
1023     }
1024 
1025     msg.setLocale(Locale::getEnglish());
1026     UBool getLocale_ok = true;
1027     if (msg.getLocale() != Locale::getEnglish()) {
1028         errln("*** MSG getLocale err.");
1029         getLocale_ok = false;
1030     }
1031 
1032     msg.setLocale(Locale::getGerman());
1033 
1034     if (msg.getLocale() != Locale::getGerman()) {
1035         errln("*** MSG getLocal err.");
1036         getLocale_ok = false;
1037     }
1038 
1039     msg.applyPattern( formatStr, err);
1040 
1041     pos.setField(0);
1042     result = "";
1043     result = msg.format(
1044         arguments,
1045         3,
1046         result,
1047         pos,
1048         err);
1049 
1050     logln(result);
1051     if (result == compareStrGer) {
1052         logln("MSG setLocale tested.");
1053     }else{
1054         dataerrln( "*** MSG setLocale err. - %s", u_errorName(err));
1055     }
1056 
1057     if (getLocale_ok) {
1058         logln("MSG getLocale tested.");
1059     }
1060 }
1061 
testFormat()1062 void TestMessageFormat::testFormat()
1063 {
1064     UErrorCode err = U_ZERO_ERROR;
1065     GregorianCalendar cal(err);
1066 
1067     const Formattable ftarray[] =
1068     {
1069         Formattable( UDate(8.71068e+011), Formattable::kIsDate )
1070     };
1071     const int32_t ft_cnt = UPRV_LENGTHOF(ftarray);
1072     Formattable ft_arr( ftarray, ft_cnt );
1073 
1074     Formattable* fmt = new Formattable(UDate(8.71068e+011), Formattable::kIsDate);
1075 
1076     UnicodeString result;
1077 
1078     //UnicodeString formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
1079     UnicodeString formatStr = "On {0,date}, it began.";
1080     UnicodeString compareStr = "On Aug 8, 1997, it began.";
1081 
1082     err = U_ZERO_ERROR;
1083     MessageFormat msg( formatStr, err);
1084     FieldPosition fp(FieldPosition::DONT_CARE);
1085 
1086     result = "";
1087     fp = 0;
1088     result = msg.format(
1089         *fmt,
1090         result,
1091         //FieldPosition(FieldPosition::DONT_CARE),
1092         fp,
1093         err);
1094 
1095     if (err != U_ILLEGAL_ARGUMENT_ERROR) {
1096         dataerrln("*** MSG format without expected error code. - %s", u_errorName(err));
1097     }
1098     err = U_ZERO_ERROR;
1099 
1100     result = "";
1101     fp = 0;
1102     result = msg.format(
1103         ft_arr,
1104         result,
1105         //FieldPosition(FieldPosition::DONT_CARE),
1106         fp,
1107         err);
1108 
1109     logln("MSG format( Formattable&, ... ) expected:" + compareStr);
1110     logln("MSG format( Formattable&, ... )   result:" + result);
1111     if (result != compareStr) {
1112         dataerrln("***  MSG format( Formattable&, .... ) err. - %s", u_errorName(err));
1113     }else{
1114         logln("MSG format( Formattable&, ... ) tested.");
1115     }
1116 
1117     delete fmt;
1118 
1119 }
1120 
testParse()1121 void TestMessageFormat::testParse()
1122 {
1123     UErrorCode err = U_ZERO_ERROR;
1124     int32_t count;
1125     UnicodeString msgFormatString = "{0} =sep= {1}";
1126     MessageFormat msg( msgFormatString, err);
1127     UnicodeString source = "abc =sep= def";
1128     UnicodeString tmp1, tmp2;
1129 
1130     Formattable* fmt_arr = msg.parse( source, count, err );
1131     if (U_FAILURE(err) || (!fmt_arr)) {
1132         errln("*** MSG parse (ustring, count, err) error.");
1133     }else{
1134         logln("MSG parse -- count: %d", count);
1135         if (count != 2) {
1136             errln("*** MSG parse (ustring, count, err) count err.");
1137         }else{
1138             if ((fmt_arr[0].getType() == Formattable::kString)
1139              && (fmt_arr[1].getType() == Formattable::kString)
1140              && (fmt_arr[0].getString(tmp1) == "abc")
1141              && (fmt_arr[1].getString(tmp2) == "def")) {
1142                 logln("MSG parse (ustring, count, err) tested.");
1143             }else{
1144                 errln("*** MSG parse (ustring, count, err) result err.");
1145             }
1146         }
1147     }
1148     delete[] fmt_arr;
1149 
1150     ParsePosition pp(0);
1151 
1152     fmt_arr = msg.parse( source, pp, count );
1153     if ((pp == 0) || (!fmt_arr)) {
1154         errln("*** MSG parse (ustring, parsepos., count) error.");
1155     }else{
1156         logln("MSG parse -- count: %d", count);
1157         if (count != 2) {
1158             errln("*** MSG parse (ustring, parsepos., count) count err.");
1159         }else{
1160             if ((fmt_arr[0].getType() == Formattable::kString)
1161              && (fmt_arr[1].getType() == Formattable::kString)
1162              && (fmt_arr[0].getString(tmp1) == "abc")
1163              && (fmt_arr[1].getString(tmp2) == "def")) {
1164                 logln("MSG parse (ustring, parsepos., count) tested.");
1165             }else{
1166                 errln("*** MSG parse (ustring, parsepos., count) result err.");
1167             }
1168         }
1169     }
1170     delete[] fmt_arr;
1171 
1172     pp = 0;
1173     Formattable fmta;
1174 
1175     msg.parseObject( source, fmta, pp );
1176     if (pp == 0) {
1177         errln("*** MSG parse (ustring, Formattable, parsepos ) error.");
1178     }else{
1179         logln("MSG parse -- count: %d", count);
1180         fmta.getArray(count);
1181         if (count != 2) {
1182             errln("*** MSG parse (ustring, Formattable, parsepos ) count err.");
1183         }else{
1184             if ((fmta[0].getType() == Formattable::kString)
1185              && (fmta[1].getType() == Formattable::kString)
1186              && (fmta[0].getString(tmp1) == "abc")
1187              && (fmta[1].getString(tmp2) == "def")) {
1188                 logln("MSG parse (ustring, Formattable, parsepos ) tested.");
1189             }else{
1190                 errln("*** MSG parse (ustring, Formattable, parsepos ) result err.");
1191             }
1192         }
1193     }
1194 }
1195 
1196 
testAdopt()1197 void TestMessageFormat::testAdopt()
1198 {
1199     UErrorCode err = U_ZERO_ERROR;
1200 
1201     UnicodeString formatStr("{0,date},{1},{2,number}", "");
1202     UnicodeString formatStrChange("{0,number},{1,number},{2,date}", "");
1203     err = U_ZERO_ERROR;
1204     MessageFormat msg( formatStr, err);
1205     MessageFormat msgCmp( formatStr, err);
1206     if (U_FAILURE(err)) {
1207         dataerrln("Unable to instantiate MessageFormat - %s", u_errorName(err));
1208         return;
1209     }
1210     int32_t count, countCmp;
1211     const Format** formats = msg.getFormats(count);
1212     const Format** formatsCmp = msgCmp.getFormats(countCmp);
1213     const Format** formatsChg = 0;
1214     const Format** formatsAct = 0;
1215     int32_t countAct;
1216     const Format* a;
1217     const Format* b;
1218     UnicodeString patCmp;
1219     UnicodeString patAct;
1220     Format** formatsToAdopt;
1221 
1222     if (!formats || !formatsCmp || (count <= 0) || (count != countCmp)) {
1223         dataerrln("Error getting Formats");
1224         return;
1225     }
1226 
1227     int32_t i;
1228 
1229     for (i = 0; i < count; i++) {
1230         a = formats[i];
1231         b = formatsCmp[i];
1232         if ((a != nullptr) && (b != nullptr)) {
1233             if (*a != *b) {
1234                 errln("a != b");
1235                 return;
1236             }
1237         }else if ((a != nullptr) || (b != nullptr)) {
1238             errln("(a != nullptr) || (b != nullptr)");
1239             return;
1240         }
1241     }
1242 
1243     msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1244     int32_t countChg;
1245     formatsChg = msg.getFormats(countChg); // tested function
1246     if (!formatsChg || (countChg != count)) {
1247         errln("Error getting Formats");
1248         return;
1249     }
1250 
1251     UBool diff;
1252     diff = true;
1253     for (i = 0; i < count; i++) {
1254         a = formatsChg[i];
1255         b = formatsCmp[i];
1256         if ((a != nullptr) && (b != nullptr)) {
1257             if (*a == *b) {
1258                 logln("formatsChg == formatsCmp at index %d", i);
1259                 diff = false;
1260             }
1261         }
1262     }
1263     if (!diff) {
1264         errln("*** MSG getFormats diff err.");
1265         return;
1266     }
1267 
1268     logln("MSG getFormats tested.");
1269 
1270     msg.setFormats( formatsCmp, countCmp ); //tested function
1271 
1272     formatsAct = msg.getFormats(countAct);
1273     if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1274         errln("Error getting Formats");
1275         return;
1276     }
1277 
1278     assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1279     // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1280     // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1281     msg.toPattern(patCmp.remove());
1282     if (!patCmp.isBogus()) {
1283       errln("msg.setFormat().toPattern() succeeds.");
1284     }
1285 
1286     for (i = 0; i < countAct; i++) {
1287         a = formatsAct[i];
1288         b = formatsCmp[i];
1289         if ((a != nullptr) && (b != nullptr)) {
1290             if (*a != *b) {
1291                 logln("formatsAct != formatsCmp at index %d", i);
1292                 errln("a != b");
1293                 return;
1294             }
1295         }else if ((a != nullptr) || (b != nullptr)) {
1296             errln("(a != nullptr) || (b != nullptr)");
1297             return;
1298         }
1299     }
1300     logln("MSG setFormats tested.");
1301 
1302     //----
1303 
1304     msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1305 
1306     formatsToAdopt = new Format* [countCmp];
1307     if (!formatsToAdopt) {
1308         errln("memory allocation error");
1309         return;
1310     }
1311 
1312     for (i = 0; i < countCmp; i++) {
1313         if (formatsCmp[i] == nullptr) {
1314             formatsToAdopt[i] = nullptr;
1315         }else{
1316             formatsToAdopt[i] = formatsCmp[i]->clone();
1317             if (!formatsToAdopt[i]) {
1318                 errln("Can't clone format at index %d", i);
1319                 return;
1320             }
1321         }
1322     }
1323     msg.adoptFormats( formatsToAdopt, countCmp ); // function to test
1324     delete[] formatsToAdopt;
1325 
1326     assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1327     // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1328     // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1329 
1330     formatsAct = msg.getFormats(countAct);
1331     if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1332         errln("Error getting Formats");
1333         return;
1334     }
1335 
1336     for (i = 0; i < countAct; i++) {
1337         a = formatsAct[i];
1338         b = formatsCmp[i];
1339         if ((a != nullptr) && (b != nullptr)) {
1340             if (*a != *b) {
1341                 errln("a != b");
1342                 return;
1343             }
1344         }else if ((a != nullptr) || (b != nullptr)) {
1345             errln("(a != nullptr) || (b != nullptr)");
1346             return;
1347         }
1348     }
1349     logln("MSG adoptFormats tested.");
1350 
1351     //---- adoptFormat
1352 
1353     msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1354 
1355     formatsToAdopt = new Format* [countCmp];
1356     if (!formatsToAdopt) {
1357         errln("memory allocation error");
1358         return;
1359     }
1360 
1361     for (i = 0; i < countCmp; i++) {
1362         if (formatsCmp[i] == nullptr) {
1363             formatsToAdopt[i] = nullptr;
1364         }else{
1365             formatsToAdopt[i] = formatsCmp[i]->clone();
1366             if (!formatsToAdopt[i]) {
1367                 errln("Can't clone format at index %d", i);
1368                 return;
1369             }
1370         }
1371     }
1372 
1373     for ( i = 0; i < countCmp; i++ ) {
1374         msg.adoptFormat( i, formatsToAdopt[i] ); // function to test
1375     }
1376     delete[] formatsToAdopt; // array itself not needed in this case;
1377 
1378     assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1379     // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1380     // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1381 
1382     formatsAct = msg.getFormats(countAct);
1383     if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1384         errln("Error getting Formats");
1385         return;
1386     }
1387 
1388     for (i = 0; i < countAct; i++) {
1389         a = formatsAct[i];
1390         b = formatsCmp[i];
1391         if ((a != nullptr) && (b != nullptr)) {
1392             if (*a != *b) {
1393                 errln("a != b");
1394                 return;
1395             }
1396         }else if ((a != nullptr) || (b != nullptr)) {
1397             errln("(a != nullptr) || (b != nullptr)");
1398             return;
1399         }
1400     }
1401     logln("MSG adoptFormat tested.");
1402 }
1403 
1404 // This test is a regression test for a fixed bug in the copy constructor.
1405 // It is kept as a global function rather than as a method since the test depends on memory values.
1406 // (At least before the bug was fixed, whether it showed up or not depended on memory contents,
1407 // which is probably why it didn't show up in the regular test for the copy constructor.)
1408 // For this reason, the test isn't changed even though it contains function calls whose results are
1409 // not tested and had no problems. Actually, the test failed by *crashing*.
_testCopyConstructor2()1410 static void _testCopyConstructor2()
1411 {
1412     UErrorCode status = U_ZERO_ERROR;
1413     UnicodeString formatStr("Hello World on {0,date,full}", "");
1414     UnicodeString resultStr(" ", "");
1415     UnicodeString result;
1416     FieldPosition fp(FieldPosition::DONT_CARE);
1417     UDate d = Calendar::getNow();
1418     const Formattable fargs( d, Formattable::kIsDate );
1419 
1420     MessageFormat* fmt1 = new MessageFormat( formatStr, status );
1421     MessageFormat* fmt2 = nullptr;
1422     MessageFormat* fmt3 = nullptr;
1423     MessageFormat* fmt4 = nullptr;
1424 
1425     if (fmt1 == nullptr) {
1426         it_err("testCopyConstructor2: (fmt1 != nullptr)");
1427         goto cleanup;
1428     }
1429 
1430     fmt2 = new MessageFormat( *fmt1 );
1431     result = fmt1->format( &fargs, 1, resultStr, fp, status );
1432 
1433     if (fmt2 == nullptr) {
1434         it_err("testCopyConstructor2: (fmt2 != nullptr)");
1435         goto cleanup;
1436     }
1437 
1438     fmt3 = fmt1->clone();
1439     fmt4 = fmt2->clone();
1440 
1441     if (fmt3 == nullptr) {
1442         it_err("testCopyConstructor2: (fmt3 != nullptr)");
1443         goto cleanup;
1444     }
1445     if (fmt4 == nullptr) {
1446         it_err("testCopyConstructor2: (fmt4 != nullptr)");
1447         goto cleanup;
1448     }
1449 
1450     result = fmt1->format( &fargs, 1, resultStr, fp, status );
1451     result = fmt2->format( &fargs, 1, resultStr, fp, status );
1452     result = fmt3->format( &fargs, 1, resultStr, fp, status );
1453     result = fmt4->format( &fargs, 1, resultStr, fp, status );
1454 
1455 cleanup:
1456     delete fmt1;
1457     delete fmt2;
1458     delete fmt3;
1459     delete fmt4;
1460 }
1461 
testCopyConstructor2()1462 void TestMessageFormat::testCopyConstructor2() {
1463     _testCopyConstructor2();
1464 }
1465 
1466 /**
1467  * Verify that MessageFormat accommodates more than 10 arguments and
1468  * more than 10 subformats.
1469  */
TestUnlimitedArgsAndSubformats()1470 void TestMessageFormat::TestUnlimitedArgsAndSubformats() {
1471     UErrorCode ec = U_ZERO_ERROR;
1472     const UnicodeString pattern =
1473         "On {0,date} (aka {0,date,short}, aka {0,date,long}) "
1474         "at {0,time} (aka {0,time,short}, aka {0,time,long}) "
1475         "there were {1,number} werjes "
1476         "(a {3,number,percent} increase over {2,number}) "
1477         "despite the {4}''s efforts "
1478         "and to delight of {5}, {6}, {7}, {8}, {9}, and {10} {11}.";
1479     MessageFormat msg(pattern, ec);
1480     if (U_FAILURE(ec)) {
1481         dataerrln("FAIL: constructor failed - %s", u_errorName(ec));
1482         return;
1483     }
1484 
1485     const Formattable ARGS[] = {
1486         Formattable(UDate(1e13), Formattable::kIsDate),
1487         Formattable((int32_t)1303),
1488         Formattable((int32_t)1202),
1489         Formattable(1303.0/1202 - 1),
1490         Formattable("Glimmung"),
1491         Formattable("the printers"),
1492         Formattable("Nick"),
1493         Formattable("his father"),
1494         Formattable("his mother"),
1495         Formattable("the spiddles"),
1496         Formattable("of course"),
1497         Formattable("Horace"),
1498     };
1499     const int32_t ARGS_LENGTH = UPRV_LENGTHOF(ARGS);
1500     Formattable ARGS_OBJ(ARGS, ARGS_LENGTH);
1501 
1502     UnicodeString expected =
1503         u"On Nov 20, 2286 (aka 11/20/86, aka November 20, 2286) "
1504         u"at 9:46:40\u202FAM (aka 9:46\u202FAM, aka 9:46:40\u202FAM PST) "
1505         u"there were 1,303 werjes "
1506         u"(a 8% increase over 1,202) "
1507         u"despite the Glimmung's efforts "
1508         u"and to delight of the printers, Nick, his father, "
1509         u"his mother, the spiddles, and of course Horace.";
1510     UnicodeString result;
1511     msg.format(ARGS_OBJ, result, ec);
1512     if (result == expected) {
1513         logln(result);
1514     } else {
1515         errln((UnicodeString)"FAIL: Got " + result +
1516               ", expected " + expected);
1517     }
1518 }
1519 
1520 // test RBNF extensions to message format
TestRBNF()1521 void TestMessageFormat::TestRBNF() {
1522     // WARNING: this depends on the RBNF formats for en_US
1523     Locale locale("en", "US", "");
1524 
1525     UErrorCode ec = U_ZERO_ERROR;
1526 
1527     UnicodeString values[] = {
1528         // decimal values do not format completely for ordinal or duration, and
1529         // do not always parse, so do not include them
1530         "0", "1", "12", "100", "123", "1001", "123,456", "-17",
1531     };
1532     int32_t values_count = UPRV_LENGTHOF(values);
1533 
1534     UnicodeString formats[] = {
1535         "There are {0,spellout} files to search.",
1536         "There are {0,spellout,%simplified} files to search.",
1537         "The bogus spellout {0,spellout,%BOGUS} files behaves like the default.",
1538         "This is the {0,ordinal} file to search.",
1539         "Searching this file will take {0,duration} to complete.",
1540         "Searching this file will take {0,duration,%with-words} to complete.",
1541     };
1542     int32_t formats_count = UPRV_LENGTHOF(formats);
1543 
1544     Formattable args[1];
1545 
1546     NumberFormat* numFmt = NumberFormat::createInstance(locale, ec);
1547     if (U_FAILURE(ec)) {
1548         dataerrln("Error calling NumberFormat::createInstance()");
1549         return;
1550     }
1551 
1552     for (int i = 0; i < formats_count; ++i) {
1553         MessageFormat* fmt = new MessageFormat(formats[i], locale, ec);
1554         logln((UnicodeString)"Testing format pattern: '" + formats[i] + "'");
1555 
1556         for (int j = 0; j < values_count; ++j) {
1557             ec = U_ZERO_ERROR;
1558             numFmt->parse(values[j], args[0], ec);
1559             if (U_FAILURE(ec)) {
1560                 errln((UnicodeString)"Failed to parse test argument " + values[j]);
1561             } else {
1562                 FieldPosition fp(FieldPosition::DONT_CARE);
1563                 UnicodeString result;
1564                 fmt->format(args, 1, result, fp, ec);
1565                 logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + UnicodeString(" ec: ") + u_errorName(ec));
1566 
1567                 int32_t count = 0;
1568                 Formattable* parseResult = fmt->parse(result, count, ec);
1569                 if (count != 1) {
1570                     errln((UnicodeString)"parse returned " + count + " args");
1571                 } else if (parseResult[0] != args[0]) {
1572                     errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0]));
1573                 }
1574                 delete []parseResult;
1575             }
1576         }
1577         delete fmt;
1578     }
1579     delete numFmt;
1580 }
1581 
GetPatternAndSkipSyntax(const MessagePattern & pattern)1582 UnicodeString TestMessageFormat::GetPatternAndSkipSyntax(const MessagePattern& pattern) {
1583     UnicodeString us(pattern.getPatternString());
1584     int count = pattern.countParts();
1585     for (int i = count; i > 0;) {
1586         const MessagePattern::Part& part = pattern.getPart(--i);
1587         if (part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
1588             us.remove(part.getIndex(), part.getLimit() - part.getIndex());
1589         }
1590     }
1591     return us;
1592 }
1593 
TestApostropheMode()1594 void TestMessageFormat::TestApostropheMode() {
1595     UErrorCode status = U_ZERO_ERROR;
1596     MessagePattern *ado_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_OPTIONAL, status);
1597     MessagePattern *adr_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_REQUIRED, status);
1598     if (ado_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) {
1599       errln("wrong value from ado_mp->getApostropheMode().");
1600     }
1601     if (adr_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) {
1602       errln("wrong value from adr_mp->getApostropheMode().");
1603     }
1604 
1605 
1606     UnicodeString tuples[] = {
1607         // Desired output
1608         // DOUBLE_OPTIONAL pattern
1609         // DOUBLE_REQUIRED pattern (empty=same as DOUBLE_OPTIONAL)
1610         "I see {many}", "I see '{many}'", "",
1611         "I said {'Wow!'}", "I said '{''Wow!''}'", "",
1612         "I dont know", "I dont know", "I don't know",
1613         "I don't know", "I don't know", "I don''t know",
1614         "I don't know", "I don''t know", "I don''t know"
1615     };
1616     int32_t tuples_count = UPRV_LENGTHOF(tuples);
1617 
1618     for (int i = 0; i < tuples_count; i += 3) {
1619       UnicodeString& desired = tuples[i];
1620       UnicodeString& ado_pattern = tuples[i + 1];
1621       UErrorCode status = U_ZERO_ERROR;
1622       assertEquals("DOUBLE_OPTIONAL failure",
1623                    desired,
1624                    GetPatternAndSkipSyntax(ado_mp->parse(ado_pattern, nullptr, status)));
1625       UnicodeString& adr_pattern = tuples[i + 2].isEmpty() ? ado_pattern : tuples[i + 2];
1626       assertEquals("DOUBLE_REQUIRED failure", desired,
1627           GetPatternAndSkipSyntax(adr_mp->parse(adr_pattern, nullptr, status)));
1628     }
1629     delete adr_mp;
1630     delete ado_mp;
1631 }
1632 
1633 
1634 // Compare behavior of DOUBLE_OPTIONAL (new default) and DOUBLE_REQUIRED JDK-compatibility mode.
TestCompatibleApostrophe()1635 void TestMessageFormat::TestCompatibleApostrophe() {
1636     // Message with choice argument which does not contain another argument.
1637     // The JDK performs only one apostrophe-quoting pass on this pattern.
1638     UnicodeString pattern = "ab{0,choice,0#1'2''3'''4''''.}yz";
1639 
1640     UErrorCode ec = U_ZERO_ERROR;
1641     MessageFormat compMsg("", Locale::getUS(), ec);
1642     compMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_REQUIRED, nullptr, ec);
1643     if (compMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) {
1644         errln("wrong value from  compMsg.getApostropheMode().");
1645     }
1646 
1647     MessageFormat icuMsg("", Locale::getUS(), ec);
1648     icuMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_OPTIONAL, nullptr, ec);
1649     if (icuMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) {
1650         errln("wrong value from  icuMsg.getApostropheMode().");
1651     }
1652 
1653     Formattable zero0[] = { (int32_t)0 };
1654     FieldPosition fieldpos(FieldPosition::DONT_CARE);
1655     UnicodeString buffer1, buffer2;
1656     assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1657             "ab12'3'4''.yz",
1658             compMsg.format(zero0, 1, buffer1, fieldpos, ec));
1659     assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1660             "ab1'2'3''4''.yz",
1661             icuMsg.format(zero0, 1, buffer2, fieldpos, ec));
1662 
1663     // Message with choice argument which contains a nested simple argument.
1664     // The DOUBLE_REQUIRED version performs two apostrophe-quoting passes.
1665     buffer1.remove();
1666     buffer2.remove();
1667     pattern = "ab{0,choice,0#1'2''3'''4''''.{0,number,'#x'}}yz";
1668     compMsg.applyPattern(pattern, ec);
1669     icuMsg.applyPattern(pattern, ec);
1670     if (U_FAILURE(ec)) {
1671         dataerrln("Unable to applyPattern - %s", u_errorName(ec));
1672     } else {
1673         assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1674                 "ab1234'.0xyz",
1675                 compMsg.format(zero0, 1, buffer1, fieldpos, ec));
1676         assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1677                 "ab1'2'3''4''.#x0yz",
1678                 icuMsg.format(zero0, 1, buffer2, fieldpos, ec));
1679     }
1680 
1681     // This part is copied over from Java tests but cannot be properly tested here
1682     // because we do not have a live reference implementation with JDK behavior.
1683     // The JDK ChoiceFormat itself always performs one apostrophe-quoting pass.
1684     /*
1685     ChoiceFormat choice = new ChoiceFormat("0#1'2''3'''4''''.");
1686     assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1687             "12'3'4''.",
1688             choice.format(0));
1689     choice.applyPattern("0#1'2''3'''4''''.{0,number,'#x'}");
1690     assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1691             "12'3'4''.{0,number,#x}",
1692             choice.format(0));
1693     */
1694 }
1695 
testAutoQuoteApostrophe()1696 void TestMessageFormat::testAutoQuoteApostrophe() {
1697     const char* patterns[] = { // pattern, expected pattern
1698         "'", "''",
1699         "''", "''",
1700         "'{", "'{'",
1701         "' {", "'' {",
1702         "'a", "''a",
1703         "'{'a", "'{'a",
1704         "'{a'", "'{a'",
1705         "'{}", "'{}'",
1706         "{'", "{'",
1707         "{'a", "{'a",
1708         "{'a{}'a}'a", "{'a{}'a}''a",
1709         "'}'", "'}'",
1710         "'} '{'}'", "'} '{'}''",
1711         "'} {{{''", "'} {{{'''",
1712     };
1713     int32_t pattern_count = UPRV_LENGTHOF(patterns);
1714 
1715     for (int i = 0; i < pattern_count; i += 2) {
1716         UErrorCode status = U_ZERO_ERROR;
1717         UnicodeString result = MessageFormat::autoQuoteApostrophe(patterns[i], status);
1718         UnicodeString target(patterns[i+1]);
1719         if (target != result) {
1720             const int BUF2_LEN = 64;
1721             char buf[256];
1722             char buf2[BUF2_LEN];
1723             int32_t len = result.extract(0, result.length(), buf2, BUF2_LEN);
1724             if (len >= BUF2_LEN) {
1725                 buf2[BUF2_LEN-1] = 0;
1726             }
1727             snprintf(buf, sizeof(buf), "[%2d] test \"%s\": target (\"%s\") != result (\"%s\")\n", i/2, patterns[i], patterns[i+1], buf2);
1728             errln(buf);
1729         }
1730     }
1731 }
1732 
testCoverage()1733 void TestMessageFormat::testCoverage() {
1734     UErrorCode status = U_ZERO_ERROR;
1735     UnicodeString testformat("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste.");
1736     MessageFormat *msgfmt = new MessageFormat(testformat, Locale("fr"), status);
1737     if (msgfmt == nullptr || U_FAILURE(status)) {
1738         dataerrln("FAIL: Unable to create MessageFormat.: %s", u_errorName(status));
1739         return;
1740     }
1741     if (!msgfmt->usesNamedArguments()) {
1742         errln("FAIL: Unable to detect usage of named arguments.");
1743     }
1744     const double limit[] = {0.0, 1.0, 2.0};
1745     const UnicodeString formats[] = {"0.0<=Arg<1.0",
1746                                    "1.0<=Arg<2.0",
1747                                    "2.0<-Arg"};
1748     ChoiceFormat cf(limit, formats, 3);
1749 
1750     msgfmt->setFormat("set", cf, status);
1751 
1752     StringEnumeration *en = msgfmt->getFormatNames(status);
1753     if (en == nullptr || U_FAILURE(status)) {
1754         errln("FAIL: Unable to get format names enumeration.");
1755     } else {
1756         int32_t count = 0;
1757         en->reset(status);
1758         count = en->count(status);
1759         if (U_FAILURE(status)) {
1760             errln("FAIL: Unable to get format name enumeration count.");
1761         } else {
1762             for (int32_t i = 0; i < count; i++) {
1763                 en->snext(status);
1764                 if (U_FAILURE(status)) {
1765                     errln("FAIL: Error enumerating through names.");
1766                     break;
1767                 }
1768             }
1769         }
1770     }
1771 
1772     // adoptFormat() takes ownership of the input Format object.
1773     // We need to clone the stack-allocated cf so that we do not attempt to delete cf.
1774     Format *cfClone = cf.clone();
1775     msgfmt->adoptFormat("adopt", cfClone, status);
1776 
1777     delete en;
1778     delete msgfmt;
1779 
1780     msgfmt = new MessageFormat("'", status);
1781     if (msgfmt == nullptr || U_FAILURE(status)) {
1782         errln("FAIL: Unable to create MessageFormat.");
1783         return;
1784     }
1785     if (msgfmt->usesNamedArguments()) {
1786         errln("FAIL: Unable to detect usage of named arguments.");
1787     }
1788 
1789     // Starting with ICU 4.8, we support setFormat(name, ...) and getFormatNames()
1790     // on a MessageFormat without named arguments.
1791     msgfmt->setFormat("formatName", cf, status);
1792     if (U_FAILURE(status)) {
1793         errln("FAIL: Should work to setFormat(name, ...) regardless of pattern.");
1794     }
1795     status = U_ZERO_ERROR;
1796     en = msgfmt->getFormatNames(status);
1797     if (U_FAILURE(status)) {
1798         errln("FAIL: Should work to get format names enumeration regardless of pattern.");
1799     }
1800 
1801     delete en;
1802     delete msgfmt;
1803 }
1804 
testGetFormatNames()1805 void TestMessageFormat::testGetFormatNames() {
1806     IcuTestErrorCode errorCode(*this, "testGetFormatNames");
1807     MessageFormat msgfmt("Hello, {alice,number} {oops,date,full}  {zip,spellout} World.", Locale::getRoot(), errorCode);
1808     if(errorCode.errDataIfFailureAndReset("MessageFormat() failed")) {
1809         return;
1810     }
1811     LocalPointer<StringEnumeration> names(msgfmt.getFormatNames(errorCode));
1812     if(errorCode.errIfFailureAndReset("msgfmt.getFormatNames() failed")) {
1813         return;
1814     }
1815     const UnicodeString *name;
1816     name = names->snext(errorCode);
1817     if (name == nullptr || errorCode.isFailure()) {
1818         errln("msgfmt.getFormatNames()[0] failed: %s", errorCode.errorName());
1819         errorCode.reset();
1820         return;
1821     }
1822     if (!assertEquals("msgfmt.getFormatNames()[0]", UNICODE_STRING_SIMPLE("alice"), *name)) {
1823         return;
1824     }
1825     name = names->snext(errorCode);
1826     if (name == nullptr || errorCode.isFailure()) {
1827         errln("msgfmt.getFormatNames()[1] failed: %s", errorCode.errorName());
1828         errorCode.reset();
1829         return;
1830     }
1831     if (!assertEquals("msgfmt.getFormatNames()[1]", UNICODE_STRING_SIMPLE("oops"), *name)) {
1832         return;
1833     }
1834     name = names->snext(errorCode);
1835     if (name == nullptr || errorCode.isFailure()) {
1836         errln("msgfmt.getFormatNames()[2] failed: %s", errorCode.errorName());
1837         errorCode.reset();
1838         return;
1839     }
1840     if (!assertEquals("msgfmt.getFormatNames()[2]", UNICODE_STRING_SIMPLE("zip"), *name)) {
1841         return;
1842     }
1843     name = names->snext(errorCode);
1844     if (name != nullptr) {
1845         errln(UnicodeString("msgfmt.getFormatNames()[3] should be nullptr but is: ") + *name);
1846         return;
1847     }
1848 }
1849 
TestTrimArgumentName()1850 void TestMessageFormat::TestTrimArgumentName() {
1851     // ICU 4.8 allows and ignores white space around argument names and numbers.
1852     IcuTestErrorCode errorCode(*this, "TestTrimArgumentName");
1853     MessageFormat m("a { 0 , number , '#,#'#.0 } z", Locale::getEnglish(), errorCode);
1854     if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
1855         return;
1856     }
1857     Formattable args[1] = { (int32_t)2 };
1858     FieldPosition ignore(FieldPosition::DONT_CARE);
1859     UnicodeString result;
1860     assertEquals("trim-numbered-arg format() failed", "a  #,#2.0  z",
1861                  m.format(args, 1, result, ignore, errorCode));
1862 
1863     m.applyPattern("x { _oOo_ , number , integer } y", errorCode);
1864     UnicodeString argName = UNICODE_STRING_SIMPLE("_oOo_");
1865     args[0].setLong(3);
1866     result.remove();
1867     assertEquals("trim-named-arg format() failed", "x 3 y",
1868                   m.format(&argName, args, 1, result, errorCode));
1869 }
1870 
TestSelectOrdinal()1871 void TestMessageFormat::TestSelectOrdinal() {
1872     IcuTestErrorCode errorCode(*this, "TestSelectOrdinal");
1873     // Test plural & ordinal together,
1874     // to make sure that we get the correct cached PluralSelector for each.
1875     MessageFormat m(
1876         "{0,plural,one{1 file}other{# files}}, "
1877         "{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}",
1878         Locale::getEnglish(), errorCode);
1879     if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
1880         return;
1881     }
1882     Formattable args[1] = { (int32_t)21 };
1883     FieldPosition ignore(FieldPosition::DONT_CARE);
1884     UnicodeString result;
1885     assertEquals("plural-and-ordinal format(21) failed", "21 files, 21st file",
1886                  m.format(args, 1, result, ignore, errorCode), true);
1887 
1888     args[0].setLong(2);
1889     assertEquals("plural-and-ordinal format(2) failed", "2 files, 2nd file",
1890                  m.format(args, 1, result.remove(), ignore, errorCode), true);
1891 
1892     args[0].setLong(1);
1893     assertEquals("plural-and-ordinal format(1) failed", "1 file, 1st file",
1894                  m.format(args, 1, result.remove(), ignore, errorCode), true);
1895 
1896     args[0].setLong(3);
1897     assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
1898                  m.format(args, 1, result.remove(), ignore, errorCode), true);
1899 
1900     errorCode.errDataIfFailureAndReset("");
1901 }
1902 
TestDecimals()1903 void TestMessageFormat::TestDecimals() {
1904     IcuTestErrorCode errorCode(*this, "TestDecimals");
1905     // Simple number replacement.
1906     MessageFormat m(
1907             "{0,plural,one{one meter}other{# meters}}",
1908             Locale::getEnglish(), errorCode);
1909     Formattable args[1] = { (int32_t)1 };
1910     FieldPosition ignore;
1911     UnicodeString result;
1912     assertEquals("simple format(1)", "one meter",
1913             m.format(args, 1, result, ignore, errorCode), true);
1914 
1915     args[0] = (double)1.5;
1916     result.remove();
1917     assertEquals("simple format(1.5)", "1.5 meters",
1918             m.format(args, 1, result, ignore, errorCode), true);
1919 
1920     // Simple but explicit.
1921     MessageFormat m0(
1922             "{0,plural,one{one meter}other{{0} meters}}",
1923             Locale::getEnglish(), errorCode);
1924     args[0] = (int32_t)1;
1925     result.remove();
1926     assertEquals("explicit format(1)", "one meter",
1927             m0.format(args, 1, result, ignore, errorCode), true);
1928 
1929     args[0] = (double)1.5;
1930     result.remove();
1931     assertEquals("explicit format(1.5)", "1.5 meters",
1932             m0.format(args, 1, result, ignore, errorCode), true);
1933 
1934     // With offset and specific simple format with optional decimals.
1935     MessageFormat m1(
1936             "{0,plural,offset:1 one{another meter}other{{0,number,00.#} meters}}",
1937             Locale::getEnglish(), errorCode);
1938     args[0] = (int32_t)1;
1939     result.remove();
1940     assertEquals("offset format(1)", "01 meters",
1941             m1.format(args, 1, result, ignore, errorCode), true);
1942 
1943     args[0] = (int32_t)2;
1944     result.remove();
1945     assertEquals("offset format(1)", "another meter",
1946             m1.format(args, 1, result, ignore, errorCode), true);
1947 
1948     args[0] = (double)2.5;
1949     result.remove();
1950     assertEquals("offset format(1)", "02.5 meters",
1951             m1.format(args, 1, result, ignore, errorCode), true);
1952 
1953     // With offset and specific simple format with forced decimals.
1954     MessageFormat m2(
1955             "{0,plural,offset:1 one{another meter}other{{0,number,0.0} meters}}",
1956             Locale::getEnglish(), errorCode);
1957     args[0] = (int32_t)1;
1958     result.remove();
1959     assertEquals("offset-decimals format(1)", "1.0 meters",
1960             m2.format(args, 1, result, ignore, errorCode), true);
1961 
1962     args[0] = (int32_t)2;
1963     result.remove();
1964     assertEquals("offset-decimals format(1)", "2.0 meters",
1965             m2.format(args, 1, result, ignore, errorCode), true);
1966 
1967     args[0] = (double)2.5;
1968     result.remove();
1969     assertEquals("offset-decimals format(1)", "2.5 meters",
1970             m2.format(args, 1, result, ignore, errorCode), true);
1971     errorCode.reset();
1972 }
1973 
TestArgIsPrefixOfAnother()1974 void TestMessageFormat::TestArgIsPrefixOfAnother() {
1975     IcuTestErrorCode errorCode(*this, "TestArgIsPrefixOfAnother");
1976     // Ticket #11952
1977     MessageFormat mf1("{0,select,a{A}ab{AB}abc{ABC}other{?}}", Locale::getEnglish(), errorCode);
1978     Formattable args[3];
1979     FieldPosition ignore;
1980     UnicodeString result;
1981     args[0].setString("a");
1982     assertEquals("a", "A", mf1.format(args, 1, result, ignore, errorCode));
1983     args[0].setString("ab");
1984     assertEquals("ab", "AB", mf1.format(args, 1, result.remove(), ignore, errorCode));
1985     args[0].setString("abc");
1986     assertEquals("abc", "ABC", mf1.format(args, 1, result.remove(), ignore, errorCode));
1987 
1988     // Ticket #12172
1989     MessageFormat mf2("{a} {aa} {aaa}", Locale::getEnglish(), errorCode);
1990     UnicodeString argNames[3] = { "a", "aa", "aaa" };
1991     args[0].setString("A");
1992     args[1].setString("AB");
1993     args[2].setString("ABC");
1994     assertEquals("a aa aaa", "A AB ABC", mf2.format(argNames, args, 3, result.remove(), errorCode));
1995 
1996     // Ticket #12172
1997     MessageFormat mf3("{aa} {aaa}", Locale::getEnglish(), errorCode);
1998     assertEquals("aa aaa", "AB ABC", mf3.format(argNames + 1, args + 1, 2, result.remove(), errorCode));
1999 }
2000 
TestMessageFormatNumberSkeleton()2001 void TestMessageFormat::TestMessageFormatNumberSkeleton() {
2002     IcuTestErrorCode status(*this, "TestMessageFormatNumberSkeleton");
2003 
2004     static const struct TestCase {
2005         const char16_t* messagePattern;
2006         const char* localeName;
2007         double arg;
2008         const char16_t* expected;
2009     } cases[] = {
2010             { u"{0,number,::percent}", "en", 50, u"50%" },
2011             { u"{0,number,::percent scale/100}", "en", 0.5, u"50%" },
2012             { u"{0,number,   ::   percent   scale/100   }", "en", 0.5, u"50%" },
2013             { u"{0,number,::currency/USD}", "en", 23, u"$23.00" },
2014             { u"{0,number,::precision-integer}", "en", 514.23, u"514" },
2015             { u"{0,number,::.000}", "en", 514.23, u"514.230" },
2016             { u"{0,number,::.}", "en", 514.23, u"514" },
2017             { u"{0,number,::}", "fr", 514.23, u"514,23" },
2018             { u"Cost: {0,number,::currency/EUR}.", "en", 4.3, u"Cost: €4.30." },
2019             { u"{0,number,'::'0.00}", "en", 50, u"::50.00" }, // pattern literal
2020     };
2021 
2022     for (auto& cas : cases) {
2023         status.setScope(cas.messagePattern);
2024         MessageFormat msgf(cas.messagePattern, cas.localeName, status);
2025         UnicodeString sb;
2026         FieldPosition fpos(FieldPosition::DONT_CARE);
2027         Formattable argsArray[] = {{cas.arg}};
2028         Formattable args(argsArray, 1);
2029         msgf.format(args, sb, status);
2030 
2031         assertEquals(cas.messagePattern, cas.expected, sb);
2032     }
2033 }
2034 
doTheRealDateTimeSkeletonTesting(UDate testDate,const char16_t * messagePattern,const char * localeName,const char16_t * expected,IcuTestErrorCode & status)2035 void TestMessageFormat::doTheRealDateTimeSkeletonTesting(UDate testDate,
2036         const char16_t* messagePattern, const char* localeName, const char16_t* expected,
2037         IcuTestErrorCode& status) {
2038 
2039     status.setScope(messagePattern);
2040     MessageFormat msgf(messagePattern, localeName, status);
2041     UnicodeString sb;
2042     FieldPosition fpos(FieldPosition::DONT_CARE);
2043     Formattable argsArray[] = { Formattable(testDate, Formattable::kIsDate) };
2044     Formattable args(argsArray, 1);
2045     msgf.format(args, sb, status);
2046 
2047     assertEquals(messagePattern, expected, sb);
2048 }
2049 
TestMessageFormatDateSkeleton()2050 void TestMessageFormat::TestMessageFormatDateSkeleton() {
2051     IcuTestErrorCode status(*this, "TestMessageFormatDateSkeleton");
2052 
2053     UDate date = LocaleTest::date(2021-1900, UCAL_NOVEMBER, 23, 16, 42, 55);
2054 
2055     doTheRealDateTimeSkeletonTesting(date, u"{0,date,::MMMMd}", "en", u"November 23", status);
2056     doTheRealDateTimeSkeletonTesting(date, u"{0,date,::yMMMMdjm}", "en", u"November 23, 2021 at 4:42\u202FPM", status);
2057     doTheRealDateTimeSkeletonTesting(date, u"{0,date,   ::   yMMMMd   }", "en", u"November 23, 2021", status);
2058     doTheRealDateTimeSkeletonTesting(date, u"{0,date,::yMMMMd}", "fr", u"23 novembre 2021", status);
2059     doTheRealDateTimeSkeletonTesting(date, u"Expiration: {0,date,::yMMM}!", "en", u"Expiration: Nov 2021!", status);
2060     // pattern literal
2061     doTheRealDateTimeSkeletonTesting(date, u"{0,date,'::'yMMMMd}", "en", u"::2021November23", status);
2062 }
2063 
TestMessageFormatTimeSkeleton()2064 void TestMessageFormat::TestMessageFormatTimeSkeleton() {
2065     IcuTestErrorCode status(*this, "TestMessageFormatTimeSkeleton");
2066 
2067     UDate date = LocaleTest::date(2021-1900, UCAL_NOVEMBER, 23, 16, 42, 55);
2068 
2069     doTheRealDateTimeSkeletonTesting(date, u"{0,time,::MMMMd}", "en", u"November 23", status);
2070     doTheRealDateTimeSkeletonTesting(date, u"{0,time,::yMMMMdjm}", "en", u"November 23, 2021 at 4:42\u202FPM", status);
2071     doTheRealDateTimeSkeletonTesting(date, u"{0,time,   ::   yMMMMd   }", "en", u"November 23, 2021", status);
2072     doTheRealDateTimeSkeletonTesting(date, u"{0,time,::yMMMMd}", "fr", u"23 novembre 2021", status);
2073     doTheRealDateTimeSkeletonTesting(date, u"Expiration: {0,time,::yMMM}!", "en", u"Expiration: Nov 2021!", status);
2074     // pattern literal
2075     doTheRealDateTimeSkeletonTesting(date, u"{0,time,'::'yMMMMd}", "en", u"::2021November23", status);
2076 }
2077 
2078 #endif /* #if !UCONFIG_NO_FORMATTING */
2079