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