]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/intltest/tmsgfmt.cpp
ICU-62123.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / tmsgfmt.cpp
index d7c6d64034e9530d46e5a17812f52cb52a340f28..2a8107b3e6c5db769d25696b2955d928fe51aa26 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /********************************************************************
  * COPYRIGHT: 
- * Copyright (c) 1997-2012, International Business Machines Corporation and
+ * Copyright (c) 1997-2016, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************
  * File TMSGFMT.CPP
@@ -18,6 +20,7 @@
 #if !UCONFIG_NO_FORMATTING
 
 #include "tmsgfmt.h"
+#include "cmemory.h"
 
 #include "unicode/format.h"
 #include "unicode/decimfmt.h"
 #include "unicode/messagepattern.h"
 #include "unicode/selfmt.h"
 #include "unicode/gregocal.h"
+#include "unicode/strenum.h"
 #include <stdio.h>
 
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-
 void
 TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
                                   const char* &name, char* /*par*/) {
@@ -67,6 +69,9 @@ TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
     TESTCASE_AUTO(testGetFormatNames);
     TESTCASE_AUTO(TestTrimArgumentName);
     TESTCASE_AUTO(TestSelectOrdinal);
+    TESTCASE_AUTO(TestDecimals);
+    TESTCASE_AUTO(TestArgIsPrefixOfAnother);
+    TESTCASE_AUTO(TestMessageFormatNumberSkeleton);
     TESTCASE_AUTO_END;
 }
 
@@ -138,7 +143,7 @@ void TestMessageFormat::testBug3()
             continue;
         }
         Formattable result;
-        FieldPosition pos(0);
+        FieldPosition pos(FieldPosition::DONT_CARE);
         buffer.remove();
         form->format(myNumber, buffer, pos);
         success = U_ZERO_ERROR;
@@ -160,7 +165,7 @@ void TestMessageFormat::testBug1()
                                "1.0<=Arg<2.0",
                                "2.0<-Arg"};
     ChoiceFormat *cf = new ChoiceFormat(limit, formats, 3);
-    FieldPosition status(0);
+    FieldPosition status(FieldPosition::DONT_CARE);
     UnicodeString toAppendTo;
     cf->format((int32_t)1, toAppendTo, status);
     if (toAppendTo != "1.0<=Arg<2.0") {
@@ -190,7 +195,7 @@ void TestMessageFormat::testBug2()
 }
 
 #if 0
-#if defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711
+#if defined(_DEBUG)
 //----------------------------------------------------
 // console I/O
 //----------------------------------------------------
@@ -241,7 +246,7 @@ operator<<( IntlTest&           stream,
     }
     return stream;
 }
-#endif /* defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711 */
+#endif /* defined(_DEBUG) */
 #endif
 
 void TestMessageFormat::PatternTest() 
@@ -318,7 +323,7 @@ void TestMessageFormat::PatternTest()
         //it_out << "Pat out: " << form->toPattern(buffer));
         UnicodeString result;
         int32_t count = 4;
-        FieldPosition fieldpos(0);
+        FieldPosition fieldpos(FieldPosition::DONT_CARE);
         form->format(testArgs, count, result, fieldpos, success);
         if (U_FAILURE(success)) {
             dataerrln("MessageFormat failed test #3 - %s", u_errorName(success));
@@ -378,7 +383,7 @@ void TestMessageFormat::sample()
     UnicodeString abc("abc");
     UnicodeString def("def");
     Formattable testArgs1[] = { abc, def };
-    FieldPosition fieldpos(0);
+    FieldPosition fieldpos(FieldPosition::DONT_CARE);
     assertEquals("format",
                  "There are abc files on def",
                  form->format(testArgs1, 2, buffer2, fieldpos, success));
@@ -578,11 +583,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);
 
@@ -654,7 +659,7 @@ void TestMessageFormat::internalFormat(MessageFormat* msgFmt ,
         //Format with passed arguments
         msgFmt->format( args , numOfArgs , result, ignore, status);
         if (U_FAILURE(status)) {
-            dataerrln( "%serror while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
+            dataerrln( "%s error while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
         }
         //Compare expected with obtained result
         if ( result!= expected ) {
@@ -670,7 +675,7 @@ MessageFormat* TestMessageFormat::internalCreate(
     //Create the MessageFormat with simple SelectFormat
     MessageFormat* msgFmt = new MessageFormat(pattern, locale, status);
     if (U_FAILURE(status)) {
-        dataerrln( "%serror while constructing with ErrorCode as %s" ,errMsg, u_errorName(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;
     }
@@ -752,6 +757,7 @@ void TestMessageFormat::testMsgFormatSelect(/* char* par */)
     //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)) {
@@ -771,12 +777,12 @@ void TestMessageFormat::testMsgFormatSelect(/* char* par */)
     }
     delete msgFmt4;
 
-    err = U_ZERO_ERROR;
     //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");
+    // with no data the above should fail but it seems to construct an invalid MessageFormat with no reported error. See #13079
     if (!U_FAILURE(err)) {
         //Arguments 
         Formattable testArgs10[] = {"Kirti",(int32_t)6,"female"};  
@@ -999,7 +1005,7 @@ void TestMessageFormat::testSetLocale()
 
     MessageFormat msg( formatStr, err);
     result = "";
-    FieldPosition pos(0);
+    FieldPosition pos(FieldPosition::DONT_CARE);
     result = msg.format(
         arguments,
         3,
@@ -1058,7 +1064,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);
@@ -1071,7 +1077,7 @@ void TestMessageFormat::testFormat()
 
     err = U_ZERO_ERROR;
     MessageFormat msg( formatStr, err);
-    FieldPosition fp(0);
+    FieldPosition fp(FieldPosition::DONT_CARE);
 
     result = "";
     fp = 0;
@@ -1403,7 +1409,7 @@ static void _testCopyConstructor2()
     UnicodeString formatStr("Hello World on {0,date,full}", "");
     UnicodeString resultStr(" ", "");
     UnicodeString result;
-    FieldPosition fp(0);
+    FieldPosition fp(FieldPosition::DONT_CARE);
     UDate d = Calendar::getNow();
     const Formattable fargs( d, Formattable::kIsDate );
 
@@ -1486,7 +1492,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 =
@@ -1519,17 +1525,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];
 
@@ -1549,21 +1555,19 @@ void TestMessageFormat::TestRBNF(void) {
             if (U_FAILURE(ec)) {
                 errln((UnicodeString)"Failed to parse test argument " + values[j]);
             } else {
-                FieldPosition fp(0);
+                FieldPosition fp(FieldPosition::DONT_CARE);
                 UnicodeString result;
                 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;
@@ -1605,7 +1609,7 @@ void TestMessageFormat::TestApostropheMode() {
         "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 = LENGTHOF(tuples);
+    int32_t tuples_count = UPRV_LENGTHOF(tuples);
 
     for (int i = 0; i < tuples_count; i += 3) {
       UnicodeString& desired = tuples[i];
@@ -1643,7 +1647,7 @@ void TestMessageFormat::TestCompatibleApostrophe() {
     }
 
     Formattable zero0[] = { (int32_t)0 };
-    FieldPosition fieldpos(0);
+    FieldPosition fieldpos(FieldPosition::DONT_CARE);
     UnicodeString buffer1, buffer2;
     assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
             "ab12'3'4''.yz",
@@ -1702,7 +1706,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;
@@ -1797,11 +1801,11 @@ void TestMessageFormat::testCoverage(void) {
 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")) {
+    if(errorCode.errDataIfFailureAndReset("MessageFormat() failed")) {
         return;
     }
     LocalPointer<StringEnumeration> names(msgfmt.getFormatNames(errorCode));
-    if(errorCode.logIfFailureAndReset("msgfmt.getFormatNames() failed")) {
+    if(errorCode.errIfFailureAndReset("msgfmt.getFormatNames() failed")) {
         return;
     }
     const UnicodeString *name;
@@ -1843,11 +1847,11 @@ 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")) {
+    if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
         return;
     }
     Formattable args[1] = { (int32_t)2 };
-    FieldPosition ignore(0);
+    FieldPosition ignore(FieldPosition::DONT_CARE);
     UnicodeString result;
     assertEquals("trim-numbered-arg format() failed", "a  #,#2.0  z",
                  m.format(args, 1, result, ignore, errorCode));
@@ -1868,11 +1872,11 @@ void TestMessageFormat::TestSelectOrdinal() {
         "{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")) {
+    if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
         return;
     }
     Formattable args[1] = { (int32_t)21 };
-    FieldPosition ignore(0);
+    FieldPosition ignore(FieldPosition::DONT_CARE);
     UnicodeString result;
     assertEquals("plural-and-ordinal format(21) failed", "21 files, 21st file",
                  m.format(args, 1, result, ignore, errorCode), TRUE);
@@ -1889,7 +1893,139 @@ void TestMessageFormat::TestSelectOrdinal() {
     assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
                  m.format(args, 1, result.remove(), ignore, errorCode), TRUE);
 
-    errorCode.logDataIfFailureAndReset("");
+    errorCode.errDataIfFailureAndReset("");
+}
+
+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));
+}
+
+void TestMessageFormat::TestMessageFormatNumberSkeleton() {
+    IcuTestErrorCode status(*this, "TestMessageFormatNumberSkeleton");
+
+    static const struct TestCase {
+        const char16_t* messagePattern;
+        const char* localeName;
+        double arg;
+        const char16_t* expected;
+    } cases[] = {
+            { u"{0,number,::percent}", "en", 50, u"50%" },
+            { u"{0,number,::percent scale/100}", "en", 0.5, u"50%" },
+            { u"{0,number,   ::   percent   scale/100   }", "en", 0.5, u"50%" },
+            { u"{0,number,::currency/USD}", "en", 23, u"$23.00" },
+            { u"{0,number,::precision-integer}", "en", 514.23, u"514" },
+            { u"{0,number,::.000}", "en", 514.23, u"514.230" },
+            { u"{0,number,::.}", "en", 514.23, u"514" },
+            { u"{0,number,::}", "fr", 514.23, u"514,23" },
+            { u"Cost: {0,number,::currency/EUR}.", "en", 4.3, u"Cost: €4.30." },
+            { u"{0,number,'::'0.00}", "en", 50, u"::50.00" }, // pattern literal
+    };
+
+    for (auto& cas : cases) {
+        status.setScope(cas.messagePattern);
+        MessageFormat msgf(cas.messagePattern, cas.localeName, status);
+        UnicodeString sb;
+        FieldPosition fpos(0);
+        Formattable argsArray[] = {{cas.arg}};
+        Formattable args(argsArray, 1);
+        msgf.format(args, sb, status);
+
+        assertEquals(cas.messagePattern, cas.expected, sb);
+    }
 }
 
 #endif /* #if !UCONFIG_NO_FORMATTING */