X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..a62d09fcbc8ca9da27887e04112ec143e19b1caf:/icuSources/test/intltest/tmsgfmt.cpp diff --git a/icuSources/test/intltest/tmsgfmt.cpp b/icuSources/test/intltest/tmsgfmt.cpp index 8e8e934a..df018940 100644 --- a/icuSources/test/intltest/tmsgfmt.cpp +++ b/icuSources/test/intltest/tmsgfmt.cpp @@ -1,6 +1,7 @@ /******************************************************************** - * Copyright (c) 1997-2008, International Business Machines - * Corporation and others. All Rights Reserved. + * COPYRIGHT: + * Copyright (c) 1997-2016, International Business Machines Corporation and + * others. All Rights Reserved. ******************************************************************** * File TMSGFMT.CPP * @@ -17,44 +18,57 @@ #if !UCONFIG_NO_FORMATTING #include "tmsgfmt.h" +#include "cmemory.h" #include "unicode/format.h" #include "unicode/decimfmt.h" +#include "unicode/localpointer.h" #include "unicode/locid.h" #include "unicode/msgfmt.h" #include "unicode/numfmt.h" #include "unicode/choicfmt.h" +#include "unicode/messagepattern.h" +#include "unicode/selfmt.h" #include "unicode/gregocal.h" #include void TestMessageFormat::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) { - switch (index) { - TESTCASE(0,testBug1); - TESTCASE(1,testBug2); - TESTCASE(2,sample); - TESTCASE(3,PatternTest); - TESTCASE(4,testStaticFormat); - TESTCASE(5,testSimpleFormat); - TESTCASE(6,testMsgFormatChoice); - TESTCASE(7,testCopyConstructor); - TESTCASE(8,testAssignment); - TESTCASE(9,testClone); - TESTCASE(10,testEquals); - TESTCASE(11,testNotEquals); - TESTCASE(12,testSetLocale); - TESTCASE(13,testFormat); - TESTCASE(14,testParse); - TESTCASE(15,testAdopt); - TESTCASE(16,testCopyConstructor2); - TESTCASE(17,TestUnlimitedArgsAndSubformats); - TESTCASE(18,TestRBNF); - TESTCASE(19,TestTurkishCasing); - TESTCASE(20,testAutoQuoteApostrophe); - TESTCASE(21,testMsgFormatPlural); - default: name = ""; break; - } + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO(testBug1); + TESTCASE_AUTO(testBug2); + TESTCASE_AUTO(sample); + TESTCASE_AUTO(PatternTest); + TESTCASE_AUTO(testStaticFormat); + TESTCASE_AUTO(testSimpleFormat); + TESTCASE_AUTO(testMsgFormatChoice); + TESTCASE_AUTO(testCopyConstructor); + TESTCASE_AUTO(testAssignment); + TESTCASE_AUTO(testClone); + TESTCASE_AUTO(testEquals); + TESTCASE_AUTO(testNotEquals); + TESTCASE_AUTO(testSetLocale); + TESTCASE_AUTO(testFormat); + TESTCASE_AUTO(testParse); + TESTCASE_AUTO(testAdopt); + TESTCASE_AUTO(testCopyConstructor2); + TESTCASE_AUTO(TestUnlimitedArgsAndSubformats); + TESTCASE_AUTO(TestRBNF); + TESTCASE_AUTO(TestTurkishCasing); + TESTCASE_AUTO(testAutoQuoteApostrophe); + TESTCASE_AUTO(testMsgFormatPlural); + TESTCASE_AUTO(testMsgFormatSelect); + TESTCASE_AUTO(testApostropheInPluralAndSelect); + TESTCASE_AUTO(TestApostropheMode); + TESTCASE_AUTO(TestCompatibleApostrophe); + TESTCASE_AUTO(testCoverage); + TESTCASE_AUTO(testGetFormatNames); + TESTCASE_AUTO(TestTrimArgumentName); + TESTCASE_AUTO(TestSelectOrdinal); + TESTCASE_AUTO(TestDecimals); + TESTCASE_AUTO(TestArgIsPrefixOfAnother); + TESTCASE_AUTO_END; } void TestMessageFormat::testBug3() @@ -166,7 +180,7 @@ void TestMessageFormat::testBug2() logln("The input pattern : " + pattern); MessageFormat *fmt = new MessageFormat(pattern, status); if (U_FAILURE(status)) { - errln("MessageFormat pattern creation failed."); + dataerrln("MessageFormat pattern creation failed. - %s", u_errorName(status)); return; } logln("The output pattern is : " + fmt->toPattern(result)); @@ -177,18 +191,13 @@ void TestMessageFormat::testBug2() } #if 0 -#if defined(_DEBUG) && U_IOSTREAM_SOURCE!=0 +#if defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711 //---------------------------------------------------- // console I/O //---------------------------------------------------- -#if U_IOSTREAM_SOURCE >= 199711 -# include - std::ostream& operator<<(std::ostream& stream, const Formattable& obj); -#elif U_IOSTREAM_SOURCE >= 198506 -# include - ostream& operator<<(ostream& stream, const Formattable& obj); -#endif +#include +std::ostream& operator<<(std::ostream& stream, const Formattable& obj); #include "unicode/datefmt.h" #include @@ -233,7 +242,7 @@ operator<<( IntlTest& stream, } return stream; } -#endif /* defined(_DEBUG) && U_IOSTREAM_SOURCE!=0 */ +#endif /* defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711 */ #endif void TestMessageFormat::PatternTest() @@ -254,7 +263,9 @@ void TestMessageFormat::PatternTest() "'{1,number,#,##}' {1,number,#,##}", }; - UnicodeString testResultPatterns[] = { + // ICU 4.8 returns the original pattern (testCases), + // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns). + /*UnicodeString testResultPatterns[] = { "Quotes '', '{', a {0} '{'0}", "Quotes '', '{', a {0,number} '{'0}", "'{'1,number,#,##} {1,number,'#'#,##}", @@ -264,13 +275,13 @@ void TestMessageFormat::PatternTest() "'{'1,date,full}, {1,date,full},", "'{'3,date,full}, {3,date,full},", "'{'1,number,#,##} {1,number,#,##}" - }; + };*/ UnicodeString testResultStrings[] = { - "Quotes ', {, a 1 {0}", - "Quotes ', {, a 1 {0}", - "{1,number,#,##} #34,56", - "There are 3,456 files on Disk at 1/12/70 5:46 AM.", + "Quotes ', {, 'a' 1 {0}", + "Quotes ', {, 'a' 1 {0}", + "{1,number,'#',##} #34,56", + "There are 3,456 files on Disk at 1/12/70, 5:46 AM.", "On Disk, there are 3,456 files, with $1.00.", "{1,number,percent}, 345,600%,", "{1,date,full}, Wednesday, December 31, 1969,", @@ -287,15 +298,21 @@ void TestMessageFormat::PatternTest() UnicodeString buffer; form = new MessageFormat(testCases[i], Locale::getUS(), success); if (U_FAILURE(success)) { - errln("MessageFormat creation failed.#1"); + dataerrln("MessageFormat creation failed.#1 - %s", u_errorName(success)); logln(((UnicodeString)"MessageFormat for ") + testCases[i] + " creation failed.\n"); continue; } - if (form->toPattern(buffer) != testResultPatterns[i]) { + // ICU 4.8 returns the original pattern (testCases), + // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns). + if (form->toPattern(buffer) != testCases[i]) { + // Note: An alternative test would be to build MessagePattern objects for + // both the input and output patterns and compare them, taking SKIP_SYNTAX etc. + // into account. + // (Too much trouble...) errln(UnicodeString("TestMessageFormat::PatternTest failed test #2, i = ") + i); //form->toPattern(buffer); errln(((UnicodeString)" Orig: ") + testCases[i]); - errln(((UnicodeString)" Exp: ") + testResultPatterns[i]); + errln(((UnicodeString)" Exp: ") + testCases[i]); errln(((UnicodeString)" Got: ") + buffer); } @@ -305,7 +322,7 @@ void TestMessageFormat::PatternTest() FieldPosition fieldpos(0); form->format(testArgs, count, result, fieldpos, success); if (U_FAILURE(success)) { - errln("MessageFormat failed test #3"); + dataerrln("MessageFormat failed test #3 - %s", u_errorName(success)); logln("TestMessageFormat::PatternTest failed test #3"); continue; } @@ -315,7 +332,7 @@ void TestMessageFormat::PatternTest() logln(UnicodeString(" Result: ") + result ); logln(UnicodeString(" Expected: ") + testResultStrings[i] ); } - + //it_out << "Result: " << result); #if 0 @@ -388,7 +405,7 @@ void TestMessageFormat::testStaticFormat() err); if (U_FAILURE(err)) { - errln("TestMessageFormat::testStaticFormat #1"); + dataerrln("TestMessageFormat::testStaticFormat #1 - %s", u_errorName(err)); logln(UnicodeString("TestMessageFormat::testStaticFormat failed test #1 with error code ")+(int32_t)err); return; } @@ -424,12 +441,12 @@ void TestMessageFormat::TestTurkishCasing() err); if (U_FAILURE(err)) { - errln("TestTurkishCasing #1 with error code %s", u_errorName(err)); + dataerrln("TestTurkishCasing #1 with error code %s", u_errorName(err)); return; } const UnicodeString expected( - "At 12:20:00 on 08.08.1997, there was a disturbance in the Force on planet 7.", ""); + "At 12:20:00 on 8.08.1997, there was a disturbance in the Force on planet 7.", ""); if (result != expected) { errln("TestTurkishCasing failed on test"); errln( UnicodeString(" Result: ") + result ); @@ -455,7 +472,7 @@ void TestMessageFormat::testSimpleFormat(/* char* par */) FieldPosition ignore(FieldPosition::DONT_CARE); form->format(testArgs1, 2, string, ignore, err); if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 0 file(s).") { - errln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #1")); + dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #1 - ") + u_errorName(err)); } ignore.setField(FieldPosition::DONT_CARE); @@ -463,14 +480,14 @@ void TestMessageFormat::testSimpleFormat(/* char* par */) form->format(testArgs2, 2, string, ignore, err); if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 1 file(s).") { logln(string); - errln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #2")+string); + dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #2")+string + " - " + u_errorName(err)); } ignore.setField(FieldPosition::DONT_CARE); string.remove(); form->format(testArgs3, 2, string, ignore, err); if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 12 file(s).") { - errln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #3")+string); + dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #3")+string + " - " + u_errorName(err)); } delete form; @@ -510,7 +527,7 @@ void TestMessageFormat::testMsgFormatChoice(/* char* par */) Formattable testArgs3[] = {(int32_t)1273, "MyDisk"}; form->format(testArgs3, 2, string, ignore, err); if (string != "The disk \"MyDisk\" contains 1,273 files.") { - errln("TestMessageFormat::testMsgFormatChoice failed on test #3"); + dataerrln("TestMessageFormat::testMsgFormatChoice failed on test #3 - %s", u_errorName(err)); } delete form; @@ -527,10 +544,10 @@ void TestMessageFormat::testMsgFormatPlural(/* char* par */) UnicodeString t2("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste."); UnicodeString t3("There {0, plural, one{is # zavod}few{are {0, number,###.0} zavoda} other{are # zavodov}} in the directory."); UnicodeString t4("There {argument, plural, one{is # zavod}few{are {argument, number,###.0} zavoda} other{are #zavodov}} in the directory."); - UnicodeString t5("{0, plural, one {{0, number,C''''est #,##0.0# fichier}} other {Ce sont # fichiers}} dans la liste."); + UnicodeString t5("{0, plural, one {{0, number,C''est #,##0.0# fichier}} other {Ce sont # fichiers}} dans la liste."); MessageFormat* mfNum = new MessageFormat(t1, Locale("fr"), err); if (U_FAILURE(err)) { - errln("TestMessageFormat::testMsgFormatPlural #1 - argumentIndex"); + dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentIndex - %s", u_errorName(err)); logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err); return; } @@ -544,7 +561,7 @@ void TestMessageFormat::testMsgFormatPlural(/* char* par */) UnicodeString argNameResult; mfAlpha->format(argName, &testArgs1, 1, argNameResult, err); if (U_FAILURE(err)) { - errln("TestMessageFormat::testMsgFormatPlural #1 - argumentName"); + dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentName - %s", u_errorName(err)); logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err); delete mfNum; return; @@ -562,11 +579,11 @@ void TestMessageFormat::testMsgFormatPlural(/* char* par */) delete mfNum; delete mfAlpha; - MessageFormat* mfNum2 = new MessageFormat(t3, Locale("ru"), err); + MessageFormat* mfNum2 = new MessageFormat(t3, Locale("uk"), err); numResult1.remove(); Formattable testArgs2((int32_t)4); mfNum2->format(&testArgs2, 1, numResult1, ignore, err); - MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("ru"), err); + MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("uk"), err); argNameResult.remove(); mfAlpha2->format(argName, &testArgs2, 1, argNameResult, err); @@ -604,12 +621,230 @@ void TestMessageFormat::testMsgFormatPlural(/* char* par */) errln("TestMessageFormat::test nested PluralFormat with argumentName"); } if ( argNameResult!= UnicodeString("C'est 0,0 fichier dans la liste.")) { - errln(UnicodeString("TestMessageFormat::test nested named PluralFormat.")); + errln(UnicodeString("TestMessageFormat::test nested named PluralFormat: ") + argNameResult); logln(UnicodeString("The unexpected nested named PluralFormat.")); } delete msgFmt; } +void TestMessageFormat::testApostropheInPluralAndSelect() { + UErrorCode errorCode = U_ZERO_ERROR; + MessageFormat msgFmt(UNICODE_STRING_SIMPLE( + "abc_{0,plural,other{#'#'#'{'#''}}_def_{1,select,other{sel'}'ect''}}_xyz"), + Locale::getEnglish(), + errorCode); + if (U_FAILURE(errorCode)) { + errln("MessageFormat constructor failed - %s\n", u_errorName(errorCode)); + return; + } + UnicodeString expected = UNICODE_STRING_SIMPLE("abc_3#3{3'_def_sel}ect'_xyz"); + Formattable args[] = { (int32_t)3, UNICODE_STRING_SIMPLE("x") }; + internalFormat( + &msgFmt, args, 2, expected, + "MessageFormat with apostrophes in plural/select arguments failed:\n"); +} + +void TestMessageFormat::internalFormat(MessageFormat* msgFmt , + Formattable* args , int32_t numOfArgs , + UnicodeString expected, const char* errMsg) +{ + UnicodeString result; + FieldPosition ignore(FieldPosition::DONT_CARE); + UErrorCode status = U_ZERO_ERROR; + + //Format with passed arguments + msgFmt->format( args , numOfArgs , result, ignore, status); + if (U_FAILURE(status)) { + dataerrln( "%s error while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) ); + } + //Compare expected with obtained result + if ( result!= expected ) { + UnicodeString err = UnicodeString(errMsg); + err+= UnicodeString(":Unexpected Result \n Expected: " + expected + "\n Obtained: " + result + "\n"); + dataerrln(err); + } +} + +MessageFormat* TestMessageFormat::internalCreate( + UnicodeString pattern ,Locale locale ,UErrorCode &status , char* errMsg) +{ + //Create the MessageFormat with simple SelectFormat + MessageFormat* msgFmt = new MessageFormat(pattern, locale, status); + if (U_FAILURE(status)) { + dataerrln( "%s error while constructing with ErrorCode as %s" ,errMsg, u_errorName(status) ); + logln(UnicodeString("TestMessageFormat::testMsgFormatSelect #1 with error code ")+(int32_t)status); + return NULL; + } + return msgFmt; +} + +void TestMessageFormat::testMsgFormatSelect(/* char* par */) +{ + logln("running TestMessageFormat::testMsgFormatSelect"); + + UErrorCode err = U_ZERO_ERROR; + //French Pattern + UnicodeString t1("{0} est {1, select, female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris."); + + err = U_ZERO_ERROR; + //Create the MessageFormat with simple French pattern + MessageFormat* msgFmt1 = internalCreate(t1.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t1"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"Kirti","female"}; + Formattable testArgs11[] = {"Victor","other"}; + Formattable testArgs12[] = {"Ash","unknown"}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12}; + UnicodeString exp[] = { + "Kirti est all\\u00E9e \\u00E0 Paris." , + "Victor est all\\u00E9 \\u00E0 Paris.", + "Ash est all\\u00E9 \\u00E0 Paris."}; + //Format + for( int i=0; i< 3; i++){ + internalFormat( msgFmt1 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t1"); + } + } + delete msgFmt1; + + //Quoted French Pattern + UnicodeString t2("{0} est {1, select, female {all\\u00E9e c''est} other {all\\u00E9 c''est}} \\u00E0 Paris."); + err = U_ZERO_ERROR; + //Create the MessageFormat with Quoted French pattern + MessageFormat* msgFmt2 = internalCreate(t2.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t2"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"Kirti","female"}; + Formattable testArgs11[] = {"Victor","other"}; + Formattable testArgs12[] = {"Ash","male"}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12}; + UnicodeString exp[] = { + "Kirti est all\\u00E9e c'est \\u00E0 Paris." , + "Victor est all\\u00E9 c'est \\u00E0 Paris.", + "Ash est all\\u00E9 c'est \\u00E0 Paris."}; + //Format + for( int i=0; i< 3; i++){ + internalFormat( msgFmt2 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t2"); + } + } + delete msgFmt2; + + //English Pattern + UnicodeString t3("{0, select , male {MALE FR company} female {FEMALE FR company} other {FR otherValue}} published new books."); + err = U_ZERO_ERROR; + //Create the MessageFormat with English pattern + MessageFormat* msgFmt3 = internalCreate(t3, Locale("en"),err,(char*)"From TestMessageFormat::TestSelectFormat create t3"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"female"}; + Formattable testArgs11[] = {"other"}; + Formattable testArgs12[] = {"male"}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12}; + UnicodeString exp[] = { + "FEMALE FR company published new books." , + "FR otherValue published new books.", + "MALE FR company published new books."}; + //Format + for( int i=0; i< 3; i++){ + internalFormat( msgFmt3 , testArgs[i], 1, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t3"); + } + } + delete msgFmt3; + + //Nested patterns with plural, number ,choice ,select format etc. + //Select Format with embedded number format + UnicodeString t4("{0} est {1, select, female {{2,number,integer} all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris."); + err = U_ZERO_ERROR; + //Create the MessageFormat with Select Format with embedded number format (nested pattern) + MessageFormat* msgFmt4 = internalCreate(t4.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t4"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"Kirti","female",(int32_t)6}; + Formattable testArgs11[] = {"Kirti","female",100.100}; + Formattable testArgs12[] = {"Kirti","other",(int32_t)6}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12}; + UnicodeString exp[] = { + "Kirti est 6 all\\u00E9e \\u00E0 Paris." , + "Kirti est 100 all\\u00E9e \\u00E0 Paris.", + "Kirti est all\\u00E9 \\u00E0 Paris."}; + //Format + for( int i=0; i< 3; i++){ + internalFormat( msgFmt4 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t4"); + } + } + delete msgFmt4; + + //Plural format with embedded select format + 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."); + err = U_ZERO_ERROR; + //Create the MessageFormat with Plural format with embedded select format(nested pattern) + MessageFormat* msgFmt5 = internalCreate(t5.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t5"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"Kirti",(int32_t)6,"female"}; + Formattable testArgs11[] = {"Kirti",(int32_t)1,"female"}; + Formattable testArgs12[] = {"Ash",(int32_t)1,"other"}; + Formattable testArgs13[] = {"Ash",(int32_t)5,"other"}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13}; + UnicodeString exp[] = { + "Kirti sont all\\u00E9es \\u00E0 Paris." , + "Kirti est all\\u00E9e \\u00E0 Paris.", + "Ash est all\\u00E9 \\u00E0 Paris.", + "Ash sont all\\u00E9s \\u00E0 Paris."}; + //Format + for( int i=0; i< 4; i++){ + internalFormat( msgFmt5 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t5"); + } + } + delete msgFmt5; + + err = U_ZERO_ERROR; + //Select, plural, and number formats heavily nested + 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."); + //Create the MessageFormat with Select, plural, and number formats heavily nested + MessageFormat* msgFmt6 = internalCreate(t6, Locale("de"),err,(char*)"From TestMessageFormat::TestSelectFormat create t6"); + if (!U_FAILURE(err)) { + //Arguments + Formattable testArgs10[] = {"Kirti","other",(int32_t)1,"other"}; + Formattable testArgs11[] = {"Kirti","other",(int32_t)6,"other"}; + Formattable testArgs12[] = {"Kirti","other",(int32_t)1,"female"}; + Formattable testArgs13[] = {"Kirti","other",(int32_t)3,"female"}; + Formattable testArgs14[] = {"Kirti","female",(int32_t)1,"female"}; + Formattable testArgs15[] = {"Kirti","female",(int32_t)5,"female"}; + Formattable testArgs16[] = {"Kirti","female",(int32_t)1,"other"}; + Formattable testArgs17[] = {"Kirti","female",(int32_t)5,"other"}; + Formattable testArgs18[] = {"Kirti","mixed",(int32_t)1,"mixed"}; + Formattable testArgs19[] = {"Kirti","mixed",(int32_t)1,"other"}; + Formattable testArgs20[] = {"Kirti","female",(int32_t)1,"mixed"}; + Formattable testArgs21[] = {"Kirti","mixed",(int32_t)5,"mixed"}; + Formattable testArgs22[] = {"Kirti","mixed",(int32_t)5,"other"}; + Formattable testArgs23[] = {"Kirti","female",(int32_t)5,"mixed"}; + Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13, + testArgs14,testArgs15,testArgs16,testArgs17, + testArgs18,testArgs19,testArgs20,testArgs21, + testArgs22,testArgs23 }; + UnicodeString exp[] = { + "Kirti und sein Freund gingen nach Paris." , + "Kirti und seine 6 Freunde gingen nach Paris." , + "Kirti und seine Freundin gingen nach Paris.", + "Kirti und seine 3 Freundinnen gingen nach Paris.", + "Kirti und ihre Freundin gingen nach Paris.", + "Kirti und ihre 5 Freundinnen gingen nach Paris.", + "Kirti und ihr Freund gingen nach Paris.", + "Kirti und ihre 5 Freunde gingen nach Paris.", + "Kirti und sein Freund gingen nach Paris.", + "Kirti und sein Freund gingen nach Paris.", + "Kirti und ihr Freund gingen nach Paris.", + "Kirti und seine 5 Freunde gingen nach Paris." , + "Kirti und seine 5 Freunde gingen nach Paris." , + "Kirti und ihre 5 Freunde gingen nach Paris." + }; + //Format + for( int i=0; i< 14; i++){ + internalFormat( msgFmt6 , testArgs[i], 4, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t6"); + } + } + delete msgFmt6; +} //--------------------------------- // API Tests @@ -775,7 +1010,7 @@ void TestMessageFormat::testSetLocale() logln(result); if (result != compareStrEng) { - errln("*** MSG format err."); + dataerrln("*** MSG format err. - %s", u_errorName(err)); } msg.setLocale(Locale::getEnglish()); @@ -807,7 +1042,7 @@ void TestMessageFormat::testSetLocale() if (result == compareStrGer) { logln("MSG setLocale tested."); }else{ - errln( "*** MSG setLocale err."); + dataerrln( "*** MSG setLocale err. - %s", u_errorName(err)); } if (getLocale_ok) { @@ -824,7 +1059,7 @@ void TestMessageFormat::testFormat() { Formattable( UDate(8.71068e+011), Formattable::kIsDate ) }; - const int32_t ft_cnt = sizeof(ftarray) / sizeof(Formattable); + const int32_t ft_cnt = UPRV_LENGTHOF(ftarray); Formattable ft_arr( ftarray, ft_cnt ); Formattable* fmt = new Formattable(UDate(8.71068e+011), Formattable::kIsDate); @@ -849,7 +1084,7 @@ void TestMessageFormat::testFormat() err); if (err != U_ILLEGAL_ARGUMENT_ERROR) { - errln("*** MSG format without expected error code."); + dataerrln("*** MSG format without expected error code. - %s", u_errorName(err)); } err = U_ZERO_ERROR; @@ -865,7 +1100,7 @@ void TestMessageFormat::testFormat() logln("MSG format( Formattable&, ... ) expected:" + compareStr); logln("MSG format( Formattable&, ... ) result:" + result); if (result != compareStr) { - errln("*** MSG format( Formattable&, .... ) err."); + dataerrln("*** MSG format( Formattable&, .... ) err. - %s", u_errorName(err)); }else{ logln("MSG format( Formattable&, ... ) tested."); } @@ -959,6 +1194,10 @@ void TestMessageFormat::testAdopt() err = U_ZERO_ERROR; MessageFormat msg( formatStr, err); MessageFormat msgCmp( formatStr, err); + if (U_FAILURE(err)) { + dataerrln("Unable to instantiate MessageFormat - %s", u_errorName(err)); + return; + } int32_t count, countCmp; const Format** formats = msg.getFormats(count); const Format** formatsCmp = msgCmp.getFormats(countCmp); @@ -972,7 +1211,7 @@ void TestMessageFormat::testAdopt() Format** formatsToAdopt; if (!formats || !formatsCmp || (count <= 0) || (count != countCmp)) { - errln("Error getting Formats"); + dataerrln("Error getting Formats"); return; } @@ -1028,7 +1267,12 @@ void TestMessageFormat::testAdopt() } assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove())); - assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); + // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.). + // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); + msg.toPattern(patCmp.remove()); + if (!patCmp.isBogus()) { + errln("msg.setFormat().toPattern() succeeds."); + } for (i = 0; i < countAct; i++) { a = formatsAct[i]; @@ -1071,7 +1315,8 @@ void TestMessageFormat::testAdopt() delete[] formatsToAdopt; assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove())); - assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); + // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.). + // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); formatsAct = msg.getFormats(countAct); if (!formatsAct || (countAct <=0) || (countAct != countCmp)) { @@ -1122,7 +1367,8 @@ void TestMessageFormat::testAdopt() delete[] formatsToAdopt; // array itself not needed in this case; assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove())); - assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); + // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.). + // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove())); formatsAct = msg.getFormats(countAct); if (!formatsAct || (countAct <=0) || (countAct != countCmp)) { @@ -1163,26 +1409,41 @@ static void _testCopyConstructor2() const Formattable fargs( d, Formattable::kIsDate ); MessageFormat* fmt1 = new MessageFormat( formatStr, status ); - MessageFormat* fmt2 = new MessageFormat( *fmt1 ); - MessageFormat* fmt3; - MessageFormat* fmt4; + MessageFormat* fmt2 = NULL; + MessageFormat* fmt3 = NULL; + MessageFormat* fmt4 = NULL; - if (fmt1 == NULL) it_err("testCopyConstructor2: (fmt1 != NULL)"); + if (fmt1 == NULL) { + it_err("testCopyConstructor2: (fmt1 != NULL)"); + goto cleanup; + } + fmt2 = new MessageFormat( *fmt1 ); result = fmt1->format( &fargs, 1, resultStr, fp, status ); - if (fmt2 == NULL) it_err("testCopyConstructor2: (fmt2 != NULL)"); + if (fmt2 == NULL) { + it_err("testCopyConstructor2: (fmt2 != NULL)"); + goto cleanup; + } fmt3 = (MessageFormat*) fmt1->clone(); fmt4 = (MessageFormat*) fmt2->clone(); - if (fmt3 == NULL) it_err("testCopyConstructor2: (fmt3 != NULL)"); - if (fmt4 == NULL) it_err("testCopyConstructor2: (fmt4 != NULL)"); + if (fmt3 == NULL) { + it_err("testCopyConstructor2: (fmt3 != NULL)"); + goto cleanup; + } + if (fmt4 == NULL) { + it_err("testCopyConstructor2: (fmt4 != NULL)"); + goto cleanup; + } result = fmt1->format( &fargs, 1, resultStr, fp, status ); result = fmt2->format( &fargs, 1, resultStr, fp, status ); result = fmt3->format( &fargs, 1, resultStr, fp, status ); result = fmt4->format( &fargs, 1, resultStr, fp, status ); + +cleanup: delete fmt1; delete fmt2; delete fmt3; @@ -1208,7 +1469,7 @@ void TestMessageFormat::TestUnlimitedArgsAndSubformats() { "and to delight of {5}, {6}, {7}, {8}, {9}, and {10} {11}."; MessageFormat msg(pattern, ec); if (U_FAILURE(ec)) { - errln("FAIL: constructor failed"); + dataerrln("FAIL: constructor failed - %s", u_errorName(ec)); return; } @@ -1226,7 +1487,7 @@ void TestMessageFormat::TestUnlimitedArgsAndSubformats() { Formattable("of course"), Formattable("Horace"), }; - const int32_t ARGS_LENGTH = sizeof(ARGS) / sizeof(ARGS[0]); + const int32_t ARGS_LENGTH = UPRV_LENGTHOF(ARGS); Formattable ARGS_OBJ(ARGS, ARGS_LENGTH); UnicodeString expected = @@ -1259,17 +1520,17 @@ void TestMessageFormat::TestRBNF(void) { // do not always parse, so do not include them "0", "1", "12", "100", "123", "1001", "123,456", "-17", }; - int32_t values_count = sizeof(values)/sizeof(values[0]); + int32_t values_count = UPRV_LENGTHOF(values); UnicodeString formats[] = { "There are {0,spellout} files to search.", "There are {0,spellout,%simplified} files to search.", "The bogus spellout {0,spellout,%BOGUS} files behaves like the default.", - "This is the {0,ordinal} file to search.", // TODO fix bug, ordinal does not parse + "This is the {0,ordinal} file to search.", "Searching this file will take {0,duration} to complete.", "Searching this file will take {0,duration,%with-words} to complete.", }; - int32_t formats_count = sizeof(formats)/sizeof(formats[0]); + int32_t formats_count = UPRV_LENGTHOF(formats); Formattable args[1]; @@ -1294,16 +1555,14 @@ void TestMessageFormat::TestRBNF(void) { fmt->format(args, 1, result, fp, ec); logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + UnicodeString(" ec: ") + u_errorName(ec)); - if (i != 3) { // TODO: fix this, for now skip ordinal parsing (format string at index 3) - int32_t count = 0; - Formattable* parseResult = fmt->parse(result, count, ec); - if (count != 1) { - errln((UnicodeString)"parse returned " + count + " args"); - } else if (parseResult[0] != args[0]) { - errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0])); - } - delete []parseResult; + int32_t count = 0; + Formattable* parseResult = fmt->parse(result, count, ec); + if (count != 1) { + errln((UnicodeString)"parse returned " + count + " args"); + } else if (parseResult[0] != args[0]) { + errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0])); } + delete []parseResult; } } delete fmt; @@ -1311,6 +1570,120 @@ void TestMessageFormat::TestRBNF(void) { delete numFmt; } +UnicodeString TestMessageFormat::GetPatternAndSkipSyntax(const MessagePattern& pattern) { + UnicodeString us(pattern.getPatternString()); + int count = pattern.countParts(); + for (int i = count; i > 0;) { + const MessagePattern::Part& part = pattern.getPart(--i); + if (part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { + us.remove(part.getIndex(), part.getLimit() - part.getIndex()); + } + } + return us; +} + +void TestMessageFormat::TestApostropheMode() { + UErrorCode status = U_ZERO_ERROR; + MessagePattern *ado_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_OPTIONAL, status); + MessagePattern *adr_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_REQUIRED, status); + if (ado_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) { + errln("wrong value from ado_mp->getApostropheMode()."); + } + if (adr_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) { + errln("wrong value from adr_mp->getApostropheMode()."); + } + + + UnicodeString tuples[] = { + // Desired output + // DOUBLE_OPTIONAL pattern + // DOUBLE_REQUIRED pattern (empty=same as DOUBLE_OPTIONAL) + "I see {many}", "I see '{many}'", "", + "I said {'Wow!'}", "I said '{''Wow!''}'", "", + "I dont know", "I dont know", "I don't know", + "I don't know", "I don't know", "I don''t know", + "I don't know", "I don''t know", "I don''t know" + }; + int32_t tuples_count = UPRV_LENGTHOF(tuples); + + for (int i = 0; i < tuples_count; i += 3) { + UnicodeString& desired = tuples[i]; + UnicodeString& ado_pattern = tuples[i + 1]; + UErrorCode status = U_ZERO_ERROR; + assertEquals("DOUBLE_OPTIONAL failure", + desired, + GetPatternAndSkipSyntax(ado_mp->parse(ado_pattern, NULL, status))); + UnicodeString& adr_pattern = tuples[i + 2].isEmpty() ? ado_pattern : tuples[i + 2]; + assertEquals("DOUBLE_REQUIRED failure", desired, + GetPatternAndSkipSyntax(adr_mp->parse(adr_pattern, NULL, status))); + } + delete adr_mp; + delete ado_mp; +} + + +// Compare behavior of DOUBLE_OPTIONAL (new default) and DOUBLE_REQUIRED JDK-compatibility mode. +void TestMessageFormat::TestCompatibleApostrophe() { + // Message with choice argument which does not contain another argument. + // The JDK performs only one apostrophe-quoting pass on this pattern. + UnicodeString pattern = "ab{0,choice,0#1'2''3'''4''''.}yz"; + + UErrorCode ec = U_ZERO_ERROR; + MessageFormat compMsg("", Locale::getUS(), ec); + compMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, ec); + if (compMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) { + errln("wrong value from compMsg.getApostropheMode()."); + } + + MessageFormat icuMsg("", Locale::getUS(), ec); + icuMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_OPTIONAL, NULL, ec); + if (icuMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) { + errln("wrong value from icuMsg.getApostropheMode()."); + } + + Formattable zero0[] = { (int32_t)0 }; + FieldPosition fieldpos(0); + UnicodeString buffer1, buffer2; + assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior", + "ab12'3'4''.yz", + compMsg.format(zero0, 1, buffer1, fieldpos, ec)); + assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior", + "ab1'2'3''4''.yz", + icuMsg.format(zero0, 1, buffer2, fieldpos, ec)); + + // Message with choice argument which contains a nested simple argument. + // The DOUBLE_REQUIRED version performs two apostrophe-quoting passes. + buffer1.remove(); + buffer2.remove(); + pattern = "ab{0,choice,0#1'2''3'''4''''.{0,number,'#x'}}yz"; + compMsg.applyPattern(pattern, ec); + icuMsg.applyPattern(pattern, ec); + if (U_FAILURE(ec)) { + dataerrln("Unable to applyPattern - %s", u_errorName(ec)); + } else { + assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior", + "ab1234'.0xyz", + compMsg.format(zero0, 1, buffer1, fieldpos, ec)); + assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior", + "ab1'2'3''4''.#x0yz", + icuMsg.format(zero0, 1, buffer2, fieldpos, ec)); + } + + // This part is copied over from Java tests but cannot be properly tested here + // because we do not have a live reference implementation with JDK behavior. + // The JDK ChoiceFormat itself always performs one apostrophe-quoting pass. + /* + ChoiceFormat choice = new ChoiceFormat("0#1'2''3'''4''''."); + assertEquals("unexpected JDK ChoiceFormat apostrophe behavior", + "12'3'4''.", + choice.format(0)); + choice.applyPattern("0#1'2''3'''4''''.{0,number,'#x'}"); + assertEquals("unexpected JDK ChoiceFormat apostrophe behavior", + "12'3'4''.{0,number,#x}", + choice.format(0)); + */ +} + void TestMessageFormat::testAutoQuoteApostrophe(void) { const char* patterns[] = { // pattern, expected pattern "'", "''", @@ -1328,7 +1701,7 @@ void TestMessageFormat::testAutoQuoteApostrophe(void) { "'} '{'}'", "'} '{'}''", "'} {{{''", "'} {{{'''", }; - int32_t pattern_count = sizeof(patterns)/sizeof(patterns[0]); + int32_t pattern_count = UPRV_LENGTHOF(patterns); for (int i = 0; i < pattern_count; i += 2) { UErrorCode status = U_ZERO_ERROR; @@ -1348,4 +1721,272 @@ void TestMessageFormat::testAutoQuoteApostrophe(void) { } } +void TestMessageFormat::testCoverage(void) { + UErrorCode status = U_ZERO_ERROR; + UnicodeString testformat("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste."); + MessageFormat *msgfmt = new MessageFormat(testformat, Locale("fr"), status); + if (msgfmt == NULL || U_FAILURE(status)) { + dataerrln("FAIL: Unable to create MessageFormat.: %s", u_errorName(status)); + return; + } + if (!msgfmt->usesNamedArguments()) { + errln("FAIL: Unable to detect usage of named arguments."); + } + const double limit[] = {0.0, 1.0, 2.0}; + const UnicodeString formats[] = {"0.0<=Arg<1.0", + "1.0<=Arg<2.0", + "2.0<-Arg"}; + ChoiceFormat cf(limit, formats, 3); + + msgfmt->setFormat("set", cf, status); + + StringEnumeration *en = msgfmt->getFormatNames(status); + if (en == NULL || U_FAILURE(status)) { + errln("FAIL: Unable to get format names enumeration."); + } else { + int32_t count = 0; + en->reset(status); + count = en->count(status); + if (U_FAILURE(status)) { + errln("FAIL: Unable to get format name enumeration count."); + } else { + for (int32_t i = 0; i < count; i++) { + en->snext(status); + if (U_FAILURE(status)) { + errln("FAIL: Error enumerating through names."); + break; + } + } + } + } + + // adoptFormat() takes ownership of the input Format object. + // We need to clone the stack-allocated cf so that we do not attempt to delete cf. + Format *cfClone = cf.clone(); + msgfmt->adoptFormat("adopt", cfClone, status); + + delete en; + delete msgfmt; + + msgfmt = new MessageFormat("'", status); + if (msgfmt == NULL || U_FAILURE(status)) { + errln("FAIL: Unable to create MessageFormat."); + return; + } + if (msgfmt->usesNamedArguments()) { + errln("FAIL: Unable to detect usage of named arguments."); + } + + // Starting with ICU 4.8, we support setFormat(name, ...) and getFormatNames() + // on a MessageFormat without named arguments. + msgfmt->setFormat("formatName", cf, status); + if (U_FAILURE(status)) { + errln("FAIL: Should work to setFormat(name, ...) regardless of pattern."); + } + status = U_ZERO_ERROR; + en = msgfmt->getFormatNames(status); + if (U_FAILURE(status)) { + errln("FAIL: Should work to get format names enumeration regardless of pattern."); + } + + delete en; + delete msgfmt; +} + +void TestMessageFormat::testGetFormatNames() { + IcuTestErrorCode errorCode(*this, "testGetFormatNames"); + MessageFormat msgfmt("Hello, {alice,number} {oops,date,full} {zip,spellout} World.", Locale::getRoot(), errorCode); + if(errorCode.logDataIfFailureAndReset("MessageFormat() failed")) { + return; + } + LocalPointer names(msgfmt.getFormatNames(errorCode)); + if(errorCode.logIfFailureAndReset("msgfmt.getFormatNames() failed")) { + return; + } + const UnicodeString *name; + name = names->snext(errorCode); + if (name == NULL || errorCode.isFailure()) { + errln("msgfmt.getFormatNames()[0] failed: %s", errorCode.errorName()); + errorCode.reset(); + return; + } + if (!assertEquals("msgfmt.getFormatNames()[0]", UNICODE_STRING_SIMPLE("alice"), *name)) { + return; + } + name = names->snext(errorCode); + if (name == NULL || errorCode.isFailure()) { + errln("msgfmt.getFormatNames()[1] failed: %s", errorCode.errorName()); + errorCode.reset(); + return; + } + if (!assertEquals("msgfmt.getFormatNames()[1]", UNICODE_STRING_SIMPLE("oops"), *name)) { + return; + } + name = names->snext(errorCode); + if (name == NULL || errorCode.isFailure()) { + errln("msgfmt.getFormatNames()[2] failed: %s", errorCode.errorName()); + errorCode.reset(); + return; + } + if (!assertEquals("msgfmt.getFormatNames()[2]", UNICODE_STRING_SIMPLE("zip"), *name)) { + return; + } + name = names->snext(errorCode); + if (name != NULL) { + errln(UnicodeString("msgfmt.getFormatNames()[3] should be NULL but is: ") + *name); + return; + } +} + +void TestMessageFormat::TestTrimArgumentName() { + // ICU 4.8 allows and ignores white space around argument names and numbers. + IcuTestErrorCode errorCode(*this, "TestTrimArgumentName"); + MessageFormat m("a { 0 , number , '#,#'#.0 } z", Locale::getEnglish(), errorCode); + if (errorCode.logDataIfFailureAndReset("Unable to instantiate MessageFormat")) { + return; + } + Formattable args[1] = { (int32_t)2 }; + FieldPosition ignore(0); + UnicodeString result; + assertEquals("trim-numbered-arg format() failed", "a #,#2.0 z", + m.format(args, 1, result, ignore, errorCode)); + + m.applyPattern("x { _oOo_ , number , integer } y", errorCode); + UnicodeString argName = UNICODE_STRING_SIMPLE("_oOo_"); + args[0].setLong(3); + result.remove(); + assertEquals("trim-named-arg format() failed", "x 3 y", + m.format(&argName, args, 1, result, errorCode)); +} + +void TestMessageFormat::TestSelectOrdinal() { + IcuTestErrorCode errorCode(*this, "TestSelectOrdinal"); + // Test plural & ordinal together, + // to make sure that we get the correct cached PluralSelector for each. + MessageFormat m( + "{0,plural,one{1 file}other{# files}}, " + "{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}", + Locale::getEnglish(), errorCode); + if (errorCode.logDataIfFailureAndReset("Unable to instantiate MessageFormat")) { + return; + } + Formattable args[1] = { (int32_t)21 }; + FieldPosition ignore(0); + UnicodeString result; + assertEquals("plural-and-ordinal format(21) failed", "21 files, 21st file", + m.format(args, 1, result, ignore, errorCode), TRUE); + + args[0].setLong(2); + assertEquals("plural-and-ordinal format(2) failed", "2 files, 2nd file", + m.format(args, 1, result.remove(), ignore, errorCode), TRUE); + + args[0].setLong(1); + assertEquals("plural-and-ordinal format(1) failed", "1 file, 1st file", + m.format(args, 1, result.remove(), ignore, errorCode), TRUE); + + args[0].setLong(3); + assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file", + m.format(args, 1, result.remove(), ignore, errorCode), TRUE); + + errorCode.logDataIfFailureAndReset(""); +} + +void TestMessageFormat::TestDecimals() { + IcuTestErrorCode errorCode(*this, "TestDecimals"); + // Simple number replacement. + MessageFormat m( + "{0,plural,one{one meter}other{# meters}}", + Locale::getEnglish(), errorCode); + Formattable args[1] = { (int32_t)1 }; + FieldPosition ignore; + UnicodeString result; + assertEquals("simple format(1)", "one meter", + m.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (double)1.5; + result.remove(); + assertEquals("simple format(1.5)", "1.5 meters", + m.format(args, 1, result, ignore, errorCode), TRUE); + + // Simple but explicit. + MessageFormat m0( + "{0,plural,one{one meter}other{{0} meters}}", + Locale::getEnglish(), errorCode); + args[0] = (int32_t)1; + result.remove(); + assertEquals("explicit format(1)", "one meter", + m0.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (double)1.5; + result.remove(); + assertEquals("explicit format(1.5)", "1.5 meters", + m0.format(args, 1, result, ignore, errorCode), TRUE); + + // With offset and specific simple format with optional decimals. + MessageFormat m1( + "{0,plural,offset:1 one{another meter}other{{0,number,00.#} meters}}", + Locale::getEnglish(), errorCode); + args[0] = (int32_t)1; + result.remove(); + assertEquals("offset format(1)", "01 meters", + m1.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (int32_t)2; + result.remove(); + assertEquals("offset format(1)", "another meter", + m1.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (double)2.5; + result.remove(); + assertEquals("offset format(1)", "02.5 meters", + m1.format(args, 1, result, ignore, errorCode), TRUE); + + // With offset and specific simple format with forced decimals. + MessageFormat m2( + "{0,plural,offset:1 one{another meter}other{{0,number,0.0} meters}}", + Locale::getEnglish(), errorCode); + args[0] = (int32_t)1; + result.remove(); + assertEquals("offset-decimals format(1)", "1.0 meters", + m2.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (int32_t)2; + result.remove(); + assertEquals("offset-decimals format(1)", "2.0 meters", + m2.format(args, 1, result, ignore, errorCode), TRUE); + + args[0] = (double)2.5; + result.remove(); + assertEquals("offset-decimals format(1)", "2.5 meters", + m2.format(args, 1, result, ignore, errorCode), TRUE); + errorCode.reset(); +} + +void TestMessageFormat::TestArgIsPrefixOfAnother() { + IcuTestErrorCode errorCode(*this, "TestArgIsPrefixOfAnother"); + // Ticket #11952 + MessageFormat mf1("{0,select,a{A}ab{AB}abc{ABC}other{?}}", Locale::getEnglish(), errorCode); + Formattable args[3]; + FieldPosition ignore; + UnicodeString result; + args[0].setString("a"); + assertEquals("a", "A", mf1.format(args, 1, result, ignore, errorCode)); + args[0].setString("ab"); + assertEquals("ab", "AB", mf1.format(args, 1, result.remove(), ignore, errorCode)); + args[0].setString("abc"); + assertEquals("abc", "ABC", mf1.format(args, 1, result.remove(), ignore, errorCode)); + + // Ticket #12172 + MessageFormat mf2("{a} {aa} {aaa}", Locale::getEnglish(), errorCode); + UnicodeString argNames[3] = { "a", "aa", "aaa" }; + args[0].setString("A"); + args[1].setString("AB"); + args[2].setString("ABC"); + assertEquals("a aa aaa", "A AB ABC", mf2.format(argNames, args, 3, result.remove(), errorCode)); + + // Ticket #12172 + MessageFormat mf3("{aa} {aaa}", Locale::getEnglish(), errorCode); + assertEquals("aa aaa", "AB ABC", mf3.format(argNames + 1, args + 1, 2, result.remove(), errorCode)); +} + #endif /* #if !UCONFIG_NO_FORMATTING */