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