/* ******************************************************************************* * Copyright (C) 2014-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * * File SIMPLEPATTERNFORMATTERTEST.CPP * ******************************************************************************** */ #include "unicode/msgfmt.h" #include "unicode/unistr.h" #include "cstring.h" #include "intltest.h" #include "simplepatternformatter.h" class SimplePatternFormatterTest : public IntlTest { public: SimplePatternFormatterTest() { } void TestNoPlaceholders(); void TestSyntaxErrors(); void TestOnePlaceholder(); void TestBigPlaceholder(); void TestManyPlaceholders(); void TestTooFewPlaceholderValues(); void TestBadArguments(); void TestTextWithNoPlaceholders(); void TestFormatReplaceNoOptimization(); void TestFormatReplaceNoOptimizationLeadingText(); void TestFormatReplaceOptimization(); void TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice(); 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 SimplePatternFormatterTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) { TESTCASE_AUTO_BEGIN; TESTCASE_AUTO(TestNoPlaceholders); TESTCASE_AUTO(TestSyntaxErrors); TESTCASE_AUTO(TestOnePlaceholder); TESTCASE_AUTO(TestBigPlaceholder); TESTCASE_AUTO(TestManyPlaceholders); TESTCASE_AUTO(TestTooFewPlaceholderValues); TESTCASE_AUTO(TestBadArguments); TESTCASE_AUTO(TestTextWithNoPlaceholders); TESTCASE_AUTO(TestFormatReplaceNoOptimization); TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingText); TESTCASE_AUTO(TestFormatReplaceOptimization); TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice); TESTCASE_AUTO(TestFormatReplaceOptimizationNoOffsets); TESTCASE_AUTO(TestFormatReplaceNoOptimizationNoOffsets); TESTCASE_AUTO(TestQuotingLikeMessageFormat); TESTCASE_AUTO_END; } void SimplePatternFormatterTest::TestNoPlaceholders() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt("This doesn''t have templates '{0}", status); assertEquals("getPlaceholderCount", 0, fmt.getPlaceholderCount()); 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 SimplePatternFormatterTest::TestSyntaxErrors() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt("{}", status); assertEquals("syntax error {}", U_ILLEGAL_ARGUMENT_ERROR, status); status = U_ZERO_ERROR; fmt.compile("{12d", status); assertEquals("syntax error {12d", U_ILLEGAL_ARGUMENT_ERROR, status); } void SimplePatternFormatterTest::TestOnePlaceholder() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("{0} meter", status); if (!assertSuccess("Status", status)) { return; } assertEquals("PlaceholderCount", 1, fmt.getPlaceholderCount()); UnicodeString appendTo; assertEquals( "format", "1 meter", fmt.format("1", appendTo, status)); // assignment SimplePatternFormatter s; s = fmt; appendTo.remove(); assertEquals( "Assignment", "1 meter", s.format("1", appendTo, status)); // Copy constructor SimplePatternFormatter r(fmt); appendTo.remove(); assertEquals( "Copy constructor", "1 meter", r.format("1", appendTo, status)); assertSuccess("Status", status); } void SimplePatternFormatterTest::TestBigPlaceholder() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt("a{20}c", status); if (!assertSuccess("Status", status)) { return; } assertEquals("{20} count", 21, fmt.getPlaceholderCount()); 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 SimplePatternFormatterTest::TestManyPlaceholders() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile( "Templates {2}{1}{5} and {4} are out of order.", status); if (!assertSuccess("Status", status)) { return; } assertEquals("PlaceholderCount", 6, fmt.getPlaceholderCount()); 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 SimplePatternFormatter 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 SimplePatternFormatter 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.compile("{0} meter", status); assertEquals("PlaceholderCount", 1, r.getPlaceholderCount()); appendTo.remove(); assertEquals( "Replace with new compile", "freddy meter", r.format("freddy", appendTo, status)); r.compile("{0}, {1}", status); assertEquals("PlaceholderCount", 2, r.getPlaceholderCount()); appendTo.remove(); assertEquals( "2 arg", "foo, bar", r.format("foo", "bar", appendTo, status)); r.compile("{0}, {1} and {2}", status); assertEquals("PlaceholderCount", 3, r.getPlaceholderCount()); appendTo.remove(); assertEquals( "3 arg", "foo, bar and baz", r.format("foo", "bar", "baz", appendTo, status)); assertSuccess("Status", status); } void SimplePatternFormatterTest::TestTooFewPlaceholderValues() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter 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 SimplePatternFormatterTest::TestBadArguments() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter 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 SimplePatternFormatter fmt2("Placeholders {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 SimplePatternFormatterTest::TestTextWithNoPlaceholders() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt("{0} has no {1} placeholders.", status); assertEquals( "", " has no placeholders.", fmt.getTextWithNoPlaceholders()); } void SimplePatternFormatterTest::TestFormatReplaceNoOptimization() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("{2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int 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 SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingText() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("boo {2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int 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 SimplePatternFormatterTest::TestFormatReplaceOptimization() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("{2}, {0}, {1} and {3}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int 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 SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("{2}, {0}, {1} and {3} {2}", status); if (!assertSuccess("Status", status)) { return; } UnicodeString result("original"); int 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 SimplePatternFormatterTest::TestFormatReplaceOptimizationNoOffsets() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt; fmt.compile("{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 SimplePatternFormatterTest::TestFormatReplaceNoOptimizationNoOffsets() { UErrorCode status = U_ZERO_ERROR; SimplePatternFormatter fmt("Placeholders {0} and {1}", status); UnicodeString result("previous:"); UnicodeString frog("frog"); const UnicodeString *params[] = {&result, &frog}; assertEquals( "", "Placeholders previous: and frog", fmt.formatAndReplace( params, UPRV_LENGTHOF(params), result, NULL, 0, status)); assertSuccess("Status", status); } void SimplePatternFormatterTest::TestQuotingLikeMessageFormat() { UErrorCode status = U_ZERO_ERROR; UnicodeString pattern = "{0} don't can''t '{5}''}{a' again '}'{1} to the '{end"; SimplePatternFormatter 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("SimplePatternFormatter", expected, spf.format(x, y, result.remove(), status)); } void SimplePatternFormatterTest::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 *createSimplePatternFormatterTest() { return new SimplePatternFormatterTest(); }