// Copyright (C) 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2014-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * * simpleformattertest.cpp * ******************************************************************************** */ #include "unicode/msgfmt.h" #include "unicode/unistr.h" #include "cstring.h" #include "intltest.h" #include "unicode/simpleformatter.h" class SimpleFormatterTest : public IntlTest { public: SimpleFormatterTest() { } void TestNoArguments(); void TestSyntaxErrors(); void TestOneArgument(); void TestBigArgument(); void TestManyArguments(); void TestTooFewArgumentValues(); void TestBadArguments(); void TestTextWithNoArguments(); void TestFormatReplaceNoOptimization(); void TestFormatReplaceNoOptimizationLeadingText(); void TestFormatReplaceOptimization(); void TestFormatReplaceNoOptimizationLeadingArgumentUsedTwice(); void TestFormatReplaceOptimizationNoOffsets(); void TestFormatReplaceNoOptimizationNoOffsets(); void TestQuotingLikeMessageFormat(); void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0); private: void verifyOffsets( const int32_t *expected, const int32_t *actual, int32_t count); }; void SimpleFormatterTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) { TESTCASE_AUTO_BEGIN; TESTCASE_AUTO(TestNoArguments); TESTCASE_AUTO(TestSyntaxErrors); TESTCASE_AUTO(TestOneArgument); TESTCASE_AUTO(TestBigArgument); TESTCASE_AUTO(TestManyArguments); TESTCASE_AUTO(TestTooFewArgumentValues); TESTCASE_AUTO(TestBadArguments); TESTCASE_AUTO(TestTextWithNoArguments); TESTCASE_AUTO(TestFormatReplaceNoOptimization); TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingText); TESTCASE_AUTO(TestFormatReplaceOptimization); TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingArgumentUsedTwice); TESTCASE_AUTO(TestFormatReplaceOptimizationNoOffsets); TESTCASE_AUTO(TestFormatReplaceNoOptimizationNoOffsets); TESTCASE_AUTO(TestQuotingLikeMessageFormat); TESTCASE_AUTO_END; } void SimpleFormatterTest::TestNoArguments() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("This doesn''t have templates '{0}", status); assertEquals("getArgumentLimit", 0, fmt.getArgumentLimit()); UnicodeString appendTo; assertEquals( "format", "This doesn't have templates {0}", fmt.format("unused", appendTo, status)); appendTo.remove(); int32_t offsets[] = { 0 }; assertEquals( "formatAndAppend", "This doesn't have templates {0}", fmt.formatAndAppend(NULL, 0, appendTo, offsets, 1, status)); assertEquals("formatAndAppend offsets[0]", -1, offsets[0]); assertEquals( "formatAndReplace", "This doesn't have templates {0}", fmt.formatAndReplace(NULL, 0, appendTo, NULL, 0, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestSyntaxErrors() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("{}", status); assertEquals("syntax error {}", (int32_t)U_ILLEGAL_ARGUMENT_ERROR, status); status = U_ZERO_ERROR; fmt.applyPattern("{12d", status); assertEquals("syntax error {12d", (int32_t)U_ILLEGAL_ARGUMENT_ERROR, status); } void SimpleFormatterTest::TestOneArgument() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("{0} meter", status); if (!assertSuccess("Status", status)) { return; } assertEquals("getArgumentLimit", 1, fmt.getArgumentLimit()); UnicodeString appendTo; assertEquals( "format", "1 meter", fmt.format("1", appendTo, status)); // assignment SimpleFormatter s; s = fmt; appendTo.remove(); assertEquals( "Assignment", "1 meter", s.format("1", appendTo, status)); // Copy constructor SimpleFormatter r(fmt); appendTo.remove(); assertEquals( "Copy constructor", "1 meter", r.format("1", appendTo, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestBigArgument() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("a{20}c", status); if (!assertSuccess("Status", status)) { return; } assertEquals("{20} count", 21, fmt.getArgumentLimit()); UnicodeString b("b"); UnicodeString *values[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &b }; UnicodeString result; assertEquals("{20}=b", "abc", fmt.formatAndAppend(values, 21, result, NULL, 0, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestManyArguments() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern( "Templates {2}{1}{5} and {4} are out of order.", status); if (!assertSuccess("Status", status)) { return; } assertEquals("getArgumentLimit", 6, fmt.getArgumentLimit()); UnicodeString values[] = { "freddy", "tommy", "frog", "billy", "leg", "{0}"}; UnicodeString *params[] = { &values[0], &values[1], &values[2], &values[3], &values[4], &values[5]}; int32_t offsets[6]; int32_t expectedOffsets[6] = {-1, 22, 18, -1, 35, 27}; UnicodeString appendTo("Prefix: "); assertEquals( "format", "Prefix: Templates frogtommy{0} and leg are out of order.", fmt.formatAndAppend( params, UPRV_LENGTHOF(params), appendTo, offsets, UPRV_LENGTHOF(offsets), status)); if (!assertSuccess("Status", status)) { return; } verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); appendTo.remove(); // Ensure we don't write to offsets array beyond its length. status = U_ZERO_ERROR; offsets[UPRV_LENGTHOF(offsets) - 1] = 289; appendTo.remove(); fmt.formatAndAppend( params, UPRV_LENGTHOF(params), appendTo, offsets, UPRV_LENGTHOF(offsets) - 1, status); assertEquals("Offsets buffer length", 289, offsets[UPRV_LENGTHOF(offsets) - 1]); // Test assignment SimpleFormatter s; s = fmt; appendTo.remove(); assertEquals( "Assignment", "Templates frogtommy{0} and leg are out of order.", s.formatAndAppend( params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status)); // Copy constructor SimpleFormatter r(fmt); appendTo.remove(); assertEquals( "Copy constructor", "Templates frogtommy{0} and leg are out of order.", r.formatAndAppend( params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status)); r.applyPattern("{0} meter", status); assertEquals("getArgumentLimit", 1, r.getArgumentLimit()); appendTo.remove(); assertEquals( "Replace with new applyPattern", "freddy meter", r.format("freddy", appendTo, status)); r.applyPattern("{0}, {1}", status); assertEquals("getArgumentLimit", 2, r.getArgumentLimit()); appendTo.remove(); assertEquals( "2 arg", "foo, bar", r.format("foo", "bar", appendTo, status)); r.applyPattern("{0}, {1} and {2}", status); assertEquals("getArgumentLimit", 3, r.getArgumentLimit()); appendTo.remove(); assertEquals( "3 arg", "foo, bar and baz", r.format("foo", "bar", "baz", appendTo, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestTooFewArgumentValues() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("{0} and {1}", status); UnicodeString appendTo; UnicodeString firstValue; UnicodeString *params[] = {&firstValue}; fmt.format( firstValue, appendTo, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR"); } status = U_ZERO_ERROR; fmt.formatAndAppend( params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR"); } status = U_ZERO_ERROR; fmt.formatAndReplace( params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR"); } } void SimpleFormatterTest::TestBadArguments() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("pickle", status); UnicodeString appendTo; // These succeed fmt.formatAndAppend( NULL, 0, appendTo, NULL, 0, status); fmt.formatAndReplace( NULL, 0, appendTo, NULL, 0, status); assertSuccess("", status); status = U_ZERO_ERROR; // fails fmt.formatAndAppend( NULL, 1, appendTo, NULL, 0, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() values=NULL but length=1"); } status = U_ZERO_ERROR; // fails fmt.formatAndAppend( NULL, 0, appendTo, NULL, 1, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() offsets=NULL but length=1"); } status = U_ZERO_ERROR; // fails because appendTo used as a parameter value SimpleFormatter fmt2("Arguments {0} and {1}", status); UnicodeString frog("frog"); const UnicodeString *params[] = { &appendTo, &frog }; fmt2.formatAndAppend(params, 2, appendTo, NULL, 0, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() value=appendTo"); } status = U_ZERO_ERROR; // fails fmt.formatAndReplace( NULL, 1, appendTo, NULL, 0, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndReplace() values=NULL but length=1"); } status = U_ZERO_ERROR; // fails fmt.formatAndReplace( NULL, 0, appendTo, NULL, 1, status); if (status != U_ILLEGAL_ARGUMENT_ERROR) { errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndReplace() offsets=NULL but length=1"); } } void SimpleFormatterTest::TestTextWithNoArguments() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("{0} has no {1} arguments.", status); assertEquals( "", " has no arguments.", fmt.getTextWithNoArguments()); } void SimpleFormatterTest::TestFormatReplaceNoOptimization() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("{2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int32_t offsets[4]; UnicodeString freddy("freddy"); UnicodeString frog("frog"); UnicodeString by("by"); const UnicodeString *params[] = {&result, &freddy, &frog, &by}; assertEquals( "", "frog, original, freddy and by", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, offsets, UPRV_LENGTHOF(offsets), status)); if (!assertSuccess("Status", status)) { return; } int32_t expectedOffsets[] = {6, 16, 0, 27}; verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); } void SimpleFormatterTest::TestFormatReplaceNoOptimizationLeadingText() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("boo {2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int32_t offsets[4]; UnicodeString freddy("freddy"); UnicodeString frog("frog"); UnicodeString by("by"); const UnicodeString *params[] = {&freddy, &frog, &result, &by}; assertEquals( "", "boo original, freddy, frog and by", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, offsets, UPRV_LENGTHOF(offsets), status)); if (!assertSuccess("Status", status)) { return; } int32_t expectedOffsets[] = {14, 22, 4, 31}; verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); } void SimpleFormatterTest::TestFormatReplaceOptimization() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("{2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int32_t offsets[4]; UnicodeString freddy("freddy"); UnicodeString frog("frog"); UnicodeString by("by"); const UnicodeString *params[] = {&freddy, &frog, &result, &by}; assertEquals( "", "original, freddy, frog and by", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, offsets, UPRV_LENGTHOF(offsets), status)); if (!assertSuccess("Status", status)) { return; } int32_t expectedOffsets[] = {10, 18, 0, 27}; verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); } void SimpleFormatterTest::TestFormatReplaceNoOptimizationLeadingArgumentUsedTwice() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("{2}, {0}, {1} and {3} {2}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int32_t offsets[4]; UnicodeString freddy("freddy"); UnicodeString frog("frog"); UnicodeString by("by"); const UnicodeString *params[] = {&freddy, &frog, &result, &by}; assertEquals( "", "original, freddy, frog and by original", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, offsets, UPRV_LENGTHOF(offsets), status)); if (!assertSuccess("Status", status)) { return; } int32_t expectedOffsets[] = {10, 18, 30, 27}; verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); } void SimpleFormatterTest::TestFormatReplaceOptimizationNoOffsets() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt; fmt.applyPattern("{2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); UnicodeString freddy("freddy"); UnicodeString frog("frog"); UnicodeString by("by"); const UnicodeString *params[] = {&freddy, &frog, &result, &by}; assertEquals( "", "original, freddy, frog and by", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, NULL, 0, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestFormatReplaceNoOptimizationNoOffsets() { UErrorCode status = U_ZERO_ERROR; SimpleFormatter fmt("Arguments {0} and {1}", status); UnicodeString result("previous:"); UnicodeString frog("frog"); const UnicodeString *params[] = {&result, &frog}; assertEquals( "", "Arguments previous: and frog", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, NULL, 0, status)); assertSuccess("Status", status); } void SimpleFormatterTest::TestQuotingLikeMessageFormat() { #if !UCONFIG_NO_FORMATTING UErrorCode status = U_ZERO_ERROR; UnicodeString pattern = "{0} don't can''t '{5}''}{a' again '}'{1} to the '{end"; SimpleFormatter spf(pattern, status); MessageFormat mf(pattern, Locale::getRoot(), status); UnicodeString expected = "X don't can't {5}'}{a again }Y to the {end"; UnicodeString x("X"), y("Y"); Formattable values[] = { x, y }; UnicodeString result; FieldPosition ignore(FieldPosition::DONT_CARE); assertEquals("MessageFormat", expected, mf.format(values, 2, result, ignore, status)); assertEquals("SimpleFormatter", expected, spf.format(x, y, result.remove(), status)); #endif /* !UCONFIG_NO_FORMATTING */ } void SimpleFormatterTest::verifyOffsets( const int32_t *expected, const int32_t *actual, int32_t count) { for (int32_t i = 0; i < count; ++i) { if (expected[i] != actual[i]) { errln("Expected %d, got %d", expected[i], actual[i]); } } } extern IntlTest *createSimpleFormatterTest() { return new SimpleFormatterTest(); }