+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/***********************************************************************
- * COPYRIGHT:
- * Copyright (c) 1997-2008, International Business Machines Corporation
+ * COPYRIGHT:
+ * Copyright (c) 1997-2016, International Business Machines Corporation
* and others. All Rights Reserved.
***********************************************************************/
-
+
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/numfmt.h"
#include "unicode/choicfmt.h"
#include "unicode/gregocal.h"
+#include "cmemory.h"
#include "putilimp.h"
// *****************************************************************************
#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break;
-void
+void
MessageFormatRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
{
- // if (exec) logln((UnicodeString)"TestSuite MessageFormatRegressionTest");
- switch (index) {
- CASE(0,Test4074764)
- CASE(1,Test4058973)
- CASE(2,Test4031438)
- CASE(3,Test4052223)
- CASE(4,Test4104976)
- CASE(5,Test4106659)
- CASE(6,Test4106660)
- CASE(7,Test4111739)
- CASE(8,Test4114743)
- CASE(9,Test4116444)
- CASE(10,Test4114739)
- CASE(11,Test4113018)
- CASE(12,Test4106661)
- CASE(13,Test4094906)
- CASE(14,Test4118592)
- CASE(15,Test4118594)
- CASE(16,Test4105380)
- CASE(17,Test4120552)
- CASE(18,Test4142938)
- CASE(19,TestChoicePatternQuote)
- CASE(20,Test4112104)
-
- default: name = ""; break;
- }
+ TESTCASE_AUTO_BEGIN;
+ TESTCASE_AUTO(Test4074764)
+ //TESTCASE_AUTO(Test4058973) -- disabled/obsolete in ICU 4.8
+ TESTCASE_AUTO(Test4031438)
+ TESTCASE_AUTO(Test4052223)
+ TESTCASE_AUTO(Test4104976)
+ TESTCASE_AUTO(Test4106659)
+ TESTCASE_AUTO(Test4106660)
+ TESTCASE_AUTO(Test4111739)
+ TESTCASE_AUTO(Test4114743)
+ TESTCASE_AUTO(Test4116444)
+ TESTCASE_AUTO(Test4114739)
+ TESTCASE_AUTO(Test4113018)
+ TESTCASE_AUTO(Test4106661)
+ TESTCASE_AUTO(Test4094906)
+ TESTCASE_AUTO(Test4118592)
+ TESTCASE_AUTO(Test4118594)
+ TESTCASE_AUTO(Test4105380)
+ TESTCASE_AUTO(Test4120552)
+ TESTCASE_AUTO(Test4142938)
+ TESTCASE_AUTO(TestChoicePatternQuote)
+ TESTCASE_AUTO(Test4112104)
+ TESTCASE_AUTO(TestICU12584)
+ TESTCASE_AUTO(TestAPI)
+ TESTCASE_AUTO_END;
}
-UBool
-MessageFormatRegressionTest::failure(UErrorCode status, const char* msg)
+UBool
+MessageFormatRegressionTest::failure(UErrorCode status, const char* msg, UBool possibleDataError)
{
if(U_FAILURE(status)) {
- errln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status));
+ if (possibleDataError) {
+ dataerrln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status));
+ } else {
+ errln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status));
+ }
return TRUE;
}
failure(status, "messageFormat->applyPattern");
//Object[] params = {new UnicodeString("BUG"), new Date()};
Formattable params [] = {
- Formattable(UnicodeString("BUG")),
+ Formattable(UnicodeString("BUG")),
Formattable(0, Formattable::kIsDate)
};
UnicodeString tempBuffer;
//Apply pattern without param and print the result
messageFormatter->applyPattern(pattern[0], status);
failure(status, "messageFormatter->applyPattern");
-
+
// {sfb} how much does this apply in C++?
// do we want to verify that the Formattable* array is not NULL,
// or is that the user's responsibility?
// additionally, what should be the item count?
// for bug testing purposes, assume that something was set to
// NULL by mistake, and that the length should be non-zero
-
+
//tempBuffer = messageFormatter->format(NULL, 1, tempBuffer, FieldPosition(FieldPosition::DONT_CARE), status);
tempBuffer.remove();
tempBuffer = messageFormatter->format(NULL, 0, tempBuffer, pos, status);
/* @bug 4058973
* MessageFormat.toPattern has weird rounding behavior.
+ *
+ * ICU 4.8: This test is commented out because toPattern() has been changed to return
+ * the original pattern string, rather than reconstituting a new (equivalent) one.
+ * This trivially eliminates issues with rounding or any other pattern string differences.
*/
-void MessageFormatRegressionTest::Test4058973()
+/*
+void MessageFormatRegressionTest::Test4058973()
{
UErrorCode status = U_ZERO_ERROR;
MessageFormat *fmt = new MessageFormat("{0,choice,0#no files|1#one file|1< {0,number,integer} files}", status);
}
delete fmt;
-}
+}*/
/* @bug 4031438
* More robust message formats.
*/
-void MessageFormatRegressionTest::Test4031438()
+void MessageFormatRegressionTest::Test4031438()
{
UErrorCode status = U_ZERO_ERROR;
-
+
UnicodeString pattern1("Impossible {1} has occurred -- status code is {0} and message is {2}.");
UnicodeString pattern2("Double '' Quotes {0} test and quoted '{1}' test plus 'other {2} stuff'.");
MessageFormat *messageFormatter = new MessageFormat("", status);
failure(status, "new MessageFormat");
+ const UBool possibleDataError = TRUE;
+
//try {
logln("Apply with pattern : " + pattern1);
messageFormatter->applyPattern(pattern1, status);
FieldPosition pos(FieldPosition::DONT_CARE);
tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status);
if(tempBuffer != "Impossible {1} has occurred -- status code is 7 and message is {2}." || failure(status, "MessageFormat::format"))
- errln("Tests arguments < substitution failed");
+ dataerrln("Tests arguments < substitution failed");
logln("Formatted with 7 : " + tempBuffer);
ParsePosition pp(0);
int32_t count = 0;
NumberFormat *fmt = 0;
UnicodeString temp, temp1;
-
+
for (int i = 0; i < count; i++) {
-
+
// convert to string if not already
Formattable obj = objs[i];
temp.remove();
//if (objs[i] != NULL && objs[i].getString(temp1) != params[i].getString(temp2)) {
if (temp != temp1) {
errln("Parse failed on object " + objs[i].getString(temp1) + " at index : " + i);
- }
+ }
}
delete fmt;
delete [] objs;
- // {sfb} does this apply? no way to really pass a null Formattable,
+ // {sfb} does this apply? no way to really pass a null Formattable,
// only a null array
/*tempBuffer = messageFormatter->format(null, tempBuffer, FieldPosition(FieldPosition::DONT_CARE), status);
logln("Formatted with null : " + tempBuffer);*/
logln("Apply with pattern : " + pattern2);
messageFormatter->applyPattern(pattern2, status);
- failure(status, "messageFormatter->applyPattern");
+ failure(status, "messageFormatter->applyPattern", possibleDataError);
tempBuffer.remove();
tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status);
- if (tempBuffer != "Double ' Quotes 7 test and quoted {1} test plus other {2} stuff.")
- errln("quote format test (w/ params) failed.");
+ if (tempBuffer != "Double ' Quotes 7 test and quoted {1} test plus 'other {2} stuff'.")
+ dataerrln("quote format test (w/ params) failed. - %s", u_errorName(status));
logln("Formatted with params : " + tempBuffer);
-
+
/*tempBuffer = messageFormatter->format(null);
if (!tempBuffer.equals("Double ' Quotes {0} test and quoted {1} test plus other {2} stuff."))
errln("quote format test (w/ null) failed.");
MessageFormat *fmt = new MessageFormat("There are {0} apples growing on the {1} tree.", status);
failure(status, "new MessageFormat");
UnicodeString str("There is one apple growing on the peach tree.");
-
+
int32_t count = 0;
fmt->parse(str, pos, count);
pos.setErrorIndex(4);
if (pos.getErrorIndex() != 4)
errln(UnicodeString("setErrorIndex failed, got ") + pos.getErrorIndex() + " instead of 4");
-
+
ChoiceFormat *f = new ChoiceFormat(
"-1#are negative|0#are no or fraction|1#is one|1.0<is 1+|2#are two|2<are more than 2.", status);
failure(status, "new ChoiceFormat");
- pos.setIndex(0);
+ pos.setIndex(0);
pos.setErrorIndex(-1);
Formattable obj;
f->parse("are negative", obj, pos);
if (pos.getErrorIndex() != -1 && obj.getDouble() == -1.0)
errln(UnicodeString("Parse with \"are negative\" failed, at ") + pos.getErrorIndex());
- pos.setIndex(0);
+ pos.setIndex(0);
pos.setErrorIndex(-1);
f->parse("are no or fraction ", obj, pos);
if (pos.getErrorIndex() != -1 && obj.getDouble() == 0.0)
errln(UnicodeString("Parse with \"are no or fraction\" failed, at ") + pos.getErrorIndex());
- pos.setIndex(0);
+ pos.setIndex(0);
pos.setErrorIndex(-1);
f->parse("go postal", obj, pos);
if (pos.getErrorIndex() == -1 && ! uprv_isNaN(obj.getDouble()))
errln(UnicodeString("Parse with \"go postal\" failed, at ") + pos.getErrorIndex());
-
+
delete fmt;
delete f;
}
{
double limits [] = {1, 20};
UnicodeString formats [] = {
- UnicodeString("xyz"),
+ UnicodeString("xyz"),
UnicodeString("abc")
};
- int32_t formats_length = (int32_t)(sizeof(formats)/sizeof(formats[0]));
+ int32_t formats_length = UPRV_LENGTHOF(formats);
UErrorCode status = U_ZERO_ERROR;
ChoiceFormat *cf = new ChoiceFormat(limits, formats, formats_length);
failure(status, "new ChoiceFormat");
// logln("ChoiceFormat constructor should check for the array lengths");
// cf = null;
//}
- //if (cf != null)
+ //if (cf != null)
// errln(cf->format(5));
//
delete cf;
{
double limits [] = {3, 1, 2};
UnicodeString formats [] = {
- UnicodeString("Three"),
- UnicodeString("One"),
+ UnicodeString("Three"),
+ UnicodeString("One"),
UnicodeString("Two")
};
ChoiceFormat *cf = new ChoiceFormat(limits, formats, 3);
void MessageFormatRegressionTest::Test4116444()
{
UnicodeString patterns [] = {
- (UnicodeString)"",
- (UnicodeString)"one",
+ (UnicodeString)"",
+ (UnicodeString)"one",
(UnicodeString) "{0,date,short}"
};
-
- UErrorCode status = U_ZERO_ERROR;
+
+ UErrorCode status = U_ZERO_ERROR;
MessageFormat *mf = new MessageFormat("", status);
failure(status, "new MessageFormat");
for (int i = 0; i < 3; i++) {
UnicodeString pattern = patterns[i];
mf->applyPattern(pattern, status);
- failure(status, "mf->applyPattern");
+ failure(status, "mf->applyPattern", TRUE);
//try {
- int32_t count = 0;
+ int32_t count = 0;
ParsePosition pp(0);
Formattable *array = mf->parse(UnicodeString(""), pp, count);
logln("pattern: \"" + pattern + "\"");
for (int j = 0; j < count; j++) {
//if (array[j] != null)
UnicodeString dummy;
- err("\"" + array[j].getString(dummy) + "\"");
+ dataerrln("\"" + array[j].getString(dummy) + "\"");
//else
// log("null");
- if (j < count- 1)
+ if (j < count- 1)
log(",");
}
log("}") ;
void MessageFormatRegressionTest::Test4114739()
{
- UErrorCode status = U_ZERO_ERROR;
+ UErrorCode status = U_ZERO_ERROR;
MessageFormat *mf = new MessageFormat("<{0}>", status);
failure(status, "new MessageFormat");
UnicodeString pat;
logln(UnicodeString("") + i + ". pattern :\"" + mf->toPattern(pat) + "\"");
log(" \"" + formatted + "\" parsed as ");
- if (objs == NULL)
+ if (objs == NULL)
logln(" null");
else {
UnicodeString temp;
void MessageFormatRegressionTest::Test4118594()
{
UErrorCode status = U_ZERO_ERROR;
+ const UBool possibleDataError = TRUE;
MessageFormat *mf = new MessageFormat("{0}, {0}, {0}", status);
failure(status, "new MessageFormat");
UnicodeString forParsing("x, y, z");
if (objs[0].getString(str) != "z")
errln("argument0: \"" + objs[0].getString(str) + "\"");
mf->applyPattern("{0,number,#.##}, {0,number,#.#}", status);
- failure(status, "mf->applyPattern");
+ failure(status, "mf->applyPattern", possibleDataError);
//Object[] oldobjs = {new Double(3.1415)};
Formattable oldobjs [] = {Formattable(3.1415)};
UnicodeString result;
FieldPosition pos(FieldPosition::DONT_CARE);
result = mf->format( oldobjs, 1, result, pos, status );
- failure(status, "mf->format");
+ failure(status, "mf->format", possibleDataError);
pat.remove();
logln("pattern: \"" + mf->toPattern(pat) + "\"");
logln("text for parsing: \"" + result + "\"");
// result now equals "3.14, 3.1"
if (result != "3.14, 3.1")
- errln("result = " + result);
+ dataerrln("result = " + result + " - " + u_errorName(status));
//Object[] newobjs = mf.parse(result, new ParsePosition(0));
int32_t count1 = 0;
pp.setIndex(0);
UnicodeString patternText1("The disk \"{1}\" contains {0}.");
UnicodeString patternText2("There are {0} on the disk \"{1}\"");
UErrorCode status = U_ZERO_ERROR;
+ const UBool possibleDataError = TRUE;
MessageFormat *form1 = new MessageFormat(patternText1, status);
failure(status, "new MessageFormat");
MessageFormat *form2 = new MessageFormat(patternText2, status);
form2->setFormat(0, *fileform);
//Object[] testArgs = {new Long(12373), "MyDisk"};
Formattable testArgs [] = {
- Formattable((int32_t)12373),
+ Formattable((int32_t)12373),
Formattable((UnicodeString)"MyDisk")
};
-
+
FieldPosition bogus(FieldPosition::DONT_CARE);
UnicodeString result;
logln(form1->format(testArgs, 2, result, bogus, status));
- failure(status, "form1->format");
+ failure(status, "form1->format", possibleDataError);
result.remove();
logln(form2->format(testArgs, 2, result, bogus, status));
- failure(status, "form1->format");
+ failure(status, "form1->format", possibleDataError);
delete form1;
delete form2;
MessageFormat *mf = new MessageFormat("pattern", status);
failure(status, "new MessageFormat");
UnicodeString texts[] = {
- (UnicodeString)"pattern",
- (UnicodeString)"pat",
+ (UnicodeString)"pattern",
+ (UnicodeString)"pat",
(UnicodeString)"1234"
};
UnicodeString pat;
* This is actually a problem in ChoiceFormat; it doesn't
* understand single quotes.
*/
-void MessageFormatRegressionTest::Test4142938()
+void MessageFormatRegressionTest::Test4142938()
{
UnicodeString pat = CharsToUnicodeString("''Vous'' {0,choice,0#n''|1#}avez s\\u00E9lectionn\\u00E9 "
"{0,choice,0#aucun|1#{0}} client{0,choice,0#s|1#|2#s} "
CharsToUnicodeString("'Vous' n'avez s\\u00E9lectionn\\u00E9 aucun clients personnels."),
CharsToUnicodeString("'Vous' avez s\\u00E9lectionn\\u00E9 "),
CharsToUnicodeString("'Vous' avez s\\u00E9lectionn\\u00E9 ")
- };
+ };
UnicodeString SUFFIX [] = {
UnicodeString(),
UNICODE_STRING(" client personnel.", 18),
};
FieldPosition pos(FieldPosition::DONT_CARE);
out = mf->format(objs, 1, out, pos, status);
- failure(status, "mf->format");
- if (SUFFIX[i] == "") {
- if (out != PREFIX[i])
- errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"");
- }
- else {
- if (!out.startsWith(PREFIX[i]) ||
- !out.endsWith(SUFFIX[i]))
- errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"...\"" +
- SUFFIX[i] + "\"");
+ if (!failure(status, "mf->format", TRUE)) {
+ if (SUFFIX[i] == "") {
+ if (out != PREFIX[i])
+ errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"");
+ }
+ else {
+ if (!out.startsWith(PREFIX[i]) ||
+ !out.endsWith(SUFFIX[i]))
+ errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"...\"" +
+ SUFFIX[i] + "\"");
+ }
}
}
* pattern characters '|', '#', '<', and '\u2264'. Two quotes in a row
* is a quote literal.
*/
-void MessageFormatRegressionTest::TestChoicePatternQuote()
+void MessageFormatRegressionTest::TestChoicePatternQuote()
{
+ // ICU 4.8 ChoiceFormat (like PluralFormat & SelectFormat)
+ // returns the chosen string unmodified, so that it is usable in a MessageFormat.
+ // We modified the test strings accordingly.
+ // Note: Without further formatting/trimming/etc., it is not possible
+ // to get a single apostrophe as the last character of a non-final choice sub-message
+ // because the single apostrophe before the pipe '|' would start quoted text.
+ // Normally, ChoiceFormat is used inside a MessageFormat, where a double apostrophe
+ // can be used in that case and will be formatted as a single one.
+ // (Better: Use a "real" apostrophe, U+2019.)
UnicodeString DATA [] = {
// Pattern 0 value 1 value
// {sfb} hacked - changed \u2264 to = (copied from Character Map)
- (UnicodeString)"0#can''t|1#can", (UnicodeString)"can't", (UnicodeString)"can",
- (UnicodeString)"0#'pound(#)=''#'''|1#xyz", (UnicodeString)"pound(#)='#'", (UnicodeString)"xyz",
- (UnicodeString)"0#'1<2 | 1=1'|1#''", (UnicodeString)"1<2 | 1=1", (UnicodeString)"'",
+ "0#can't|1#can", "can't", "can",
+ "0#pound(#)='#''|1#xyz", "pound(#)='#''", "xyz",
+ "0#1<2 '| 1=1'|1#'", "1<2 '| 1=1'", "'",
};
for (int i=0; i<9; i+=3) {
//try {
out = cf->format((double)j, out, pos);
if (out != DATA[i+1+j])
errln("Fail: Pattern \"" + DATA[i] + "\" x "+j+" -> " +
- out + "; want \"" + DATA[i+1+j] + '"');
+ out + "; want \"" + DATA[i+1+j] + "\"");
}
UnicodeString pat;
pat = cf->toPattern(pat);
ChoiceFormat *cf2 = new ChoiceFormat(pat, status);
pat2 = cf2->toPattern(pat2);
if (pat != pat2)
- errln("Fail: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + '"');
+ errln("Fail: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + "\"");
else
- logln("Ok: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + '"');
+ logln("Ok: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + "\"");
/*}
catch (IllegalArgumentException e) {
errln("Fail: Pattern \"" + DATA[i] + "\" -> " + e);
}*/
-
+
delete cf;
delete cf2;
}
* MessageFormat.equals(null) throws a NullPointerException. The JLS states
* that it should return false.
*/
-void MessageFormatRegressionTest::Test4112104()
+void MessageFormatRegressionTest::Test4112104()
{
UErrorCode status = U_ZERO_ERROR;
MessageFormat *format = new MessageFormat("", status);
delete format;
}
+void MessageFormatRegressionTest::TestICU12584() {
+ // We were able to handle only 10 due to the bug ICU-12584.
+ UnicodeString pattern(
+ u"{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}"
+ u"{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}{1}{2}{3}");
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ MessageFormat msg(pattern, status);
+ failure(status, "Flat message");
+
+ int32_t count;
+ msg.getFormats(count);
+ assertEquals("Plain placeholder match", 42, count);
+
+ // Make sure we iterate only over top level arguments (so we don't over allocate).
+ UnicodeString inner_pattern("{1}{1,select,fem {1} masc {2} other {3}}{2}");
+ status = U_ZERO_ERROR;
+ MessageFormat inner_msg(inner_pattern, status);
+ failure(status, "Inner message");
+
+ count = 0;
+ inner_msg.getFormats(count);
+ assertEquals("Inner placeholder match", 3, count);
+}
+
+void MessageFormatRegressionTest::TestAPI() {
+ UErrorCode status = U_ZERO_ERROR;
+ MessageFormat *format = new MessageFormat("", status);
+ failure(status, "new MessageFormat");
+
+ // Test adoptFormat
+ MessageFormat *fmt = new MessageFormat("",status);
+ format->adoptFormat("some_name",fmt,status); // Must at least pass a valid identifier.
+ failure(status, "adoptFormat");
+
+ // Test getFormat
+ format->setFormat((int32_t)0,*fmt);
+ format->getFormat("some_other_name",status); // Must at least pass a valid identifier.
+ failure(status, "getFormat");
+ delete format;
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */