/*
*******************************************************************************
* 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();
}