1 /*
2 *******************************************************************************
3 * Copyright (C) 2014-2016, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 *
7 * File SIMPLEPATTERNFORMATTERTEST.CPP
8 *
9 ********************************************************************************
10 */
11 
12 #include "unicode/msgfmt.h"
13 #include "unicode/unistr.h"
14 #include "cstring.h"
15 #include "intltest.h"
16 #include "simplepatternformatter.h"
17 
18 class SimplePatternFormatterTest : public IntlTest {
19 public:
SimplePatternFormatterTest()20     SimplePatternFormatterTest() {
21     }
22     void TestNoPlaceholders();
23     void TestSyntaxErrors();
24     void TestOnePlaceholder();
25     void TestBigPlaceholder();
26     void TestManyPlaceholders();
27     void TestTooFewPlaceholderValues();
28     void TestBadArguments();
29     void TestTextWithNoPlaceholders();
30     void TestFormatReplaceNoOptimization();
31     void TestFormatReplaceNoOptimizationLeadingText();
32     void TestFormatReplaceOptimization();
33     void TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice();
34     void TestFormatReplaceOptimizationNoOffsets();
35     void TestFormatReplaceNoOptimizationNoOffsets();
36     void TestQuotingLikeMessageFormat();
37     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
38 private:
39     void verifyOffsets(
40             const int32_t *expected,
41             const int32_t *actual,
42             int32_t count);
43 };
44 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)45 void SimplePatternFormatterTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
46   TESTCASE_AUTO_BEGIN;
47   TESTCASE_AUTO(TestNoPlaceholders);
48   TESTCASE_AUTO(TestSyntaxErrors);
49   TESTCASE_AUTO(TestOnePlaceholder);
50   TESTCASE_AUTO(TestBigPlaceholder);
51   TESTCASE_AUTO(TestManyPlaceholders);
52   TESTCASE_AUTO(TestTooFewPlaceholderValues);
53   TESTCASE_AUTO(TestBadArguments);
54   TESTCASE_AUTO(TestTextWithNoPlaceholders);
55   TESTCASE_AUTO(TestFormatReplaceNoOptimization);
56   TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingText);
57   TESTCASE_AUTO(TestFormatReplaceOptimization);
58   TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice);
59   TESTCASE_AUTO(TestFormatReplaceOptimizationNoOffsets);
60   TESTCASE_AUTO(TestFormatReplaceNoOptimizationNoOffsets);
61   TESTCASE_AUTO(TestQuotingLikeMessageFormat);
62   TESTCASE_AUTO_END;
63 }
64 
TestNoPlaceholders()65 void SimplePatternFormatterTest::TestNoPlaceholders() {
66     UErrorCode status = U_ZERO_ERROR;
67     SimplePatternFormatter fmt("This doesn''t have templates '{0}", status);
68     assertEquals("getPlaceholderCount", 0, fmt.getPlaceholderCount());
69     UnicodeString appendTo;
70     assertEquals(
71             "format",
72             "This doesn't have templates {0}",
73             fmt.format("unused", appendTo, status));
74     appendTo.remove();
75     int32_t offsets[] = { 0 };
76     assertEquals(
77             "formatAndAppend",
78             "This doesn't have templates {0}",
79             fmt.formatAndAppend(NULL, 0, appendTo, offsets, 1, status));
80     assertEquals("formatAndAppend offsets[0]", -1, offsets[0]);
81     assertEquals(
82             "formatAndReplace",
83             "This doesn't have templates {0}",
84             fmt.formatAndReplace(NULL, 0, appendTo, NULL, 0, status));
85     assertSuccess("Status", status);
86 }
87 
TestSyntaxErrors()88 void SimplePatternFormatterTest::TestSyntaxErrors() {
89     UErrorCode status = U_ZERO_ERROR;
90     SimplePatternFormatter fmt("{}", status);
91     assertEquals("syntax error {}", U_ILLEGAL_ARGUMENT_ERROR, status);
92     status = U_ZERO_ERROR;
93     fmt.compile("{12d", status);
94     assertEquals("syntax error {12d", U_ILLEGAL_ARGUMENT_ERROR, status);
95 }
96 
TestOnePlaceholder()97 void SimplePatternFormatterTest::TestOnePlaceholder() {
98     UErrorCode status = U_ZERO_ERROR;
99     SimplePatternFormatter fmt;
100     fmt.compile("{0} meter", status);
101     if (!assertSuccess("Status", status)) {
102         return;
103     }
104     assertEquals("PlaceholderCount", 1, fmt.getPlaceholderCount());
105     UnicodeString appendTo;
106     assertEquals(
107             "format",
108             "1 meter",
109             fmt.format("1", appendTo, status));
110 
111     // assignment
112     SimplePatternFormatter s;
113     s = fmt;
114     appendTo.remove();
115     assertEquals(
116             "Assignment",
117             "1 meter",
118             s.format("1", appendTo, status));
119 
120     // Copy constructor
121     SimplePatternFormatter r(fmt);
122     appendTo.remove();
123     assertEquals(
124             "Copy constructor",
125             "1 meter",
126             r.format("1", appendTo, status));
127     assertSuccess("Status", status);
128 }
129 
TestBigPlaceholder()130 void SimplePatternFormatterTest::TestBigPlaceholder() {
131     UErrorCode status = U_ZERO_ERROR;
132     SimplePatternFormatter fmt("a{20}c", status);
133     if (!assertSuccess("Status", status)) {
134         return;
135     }
136     assertEquals("{20} count", 21, fmt.getPlaceholderCount());
137     UnicodeString b("b");
138     UnicodeString *values[] = {
139         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
140         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
141         &b
142     };
143     UnicodeString result;
144     assertEquals("{20}=b", "abc", fmt.formatAndAppend(values, 21, result, NULL, 0, status));
145     assertSuccess("Status", status);
146 }
147 
TestManyPlaceholders()148 void SimplePatternFormatterTest::TestManyPlaceholders() {
149     UErrorCode status = U_ZERO_ERROR;
150     SimplePatternFormatter fmt;
151     fmt.compile(
152             "Templates {2}{1}{5} and {4} are out of order.", status);
153     if (!assertSuccess("Status", status)) {
154         return;
155     }
156     assertEquals("PlaceholderCount", 6, fmt.getPlaceholderCount());
157     UnicodeString values[] = {
158             "freddy", "tommy", "frog", "billy", "leg", "{0}"};
159     UnicodeString *params[] = {
160            &values[0], &values[1], &values[2], &values[3], &values[4], &values[5]};
161     int32_t offsets[6];
162     int32_t expectedOffsets[6] = {-1, 22, 18, -1, 35, 27};
163     UnicodeString appendTo("Prefix: ");
164     assertEquals(
165             "format",
166             "Prefix: Templates frogtommy{0} and leg are out of order.",
167             fmt.formatAndAppend(
168                     params,
169                     UPRV_LENGTHOF(params),
170                     appendTo,
171                     offsets,
172                     UPRV_LENGTHOF(offsets),
173                     status));
174     if (!assertSuccess("Status", status)) {
175         return;
176     }
177     verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
178     appendTo.remove();
179 
180     // Ensure we don't write to offsets array beyond its length.
181     status = U_ZERO_ERROR;
182     offsets[UPRV_LENGTHOF(offsets) - 1] = 289;
183     appendTo.remove();
184     fmt.formatAndAppend(
185             params,
186             UPRV_LENGTHOF(params),
187             appendTo,
188             offsets,
189             UPRV_LENGTHOF(offsets) - 1,
190             status);
191     assertEquals("Offsets buffer length", 289, offsets[UPRV_LENGTHOF(offsets) - 1]);
192 
193     // Test assignment
194     SimplePatternFormatter s;
195     s = fmt;
196     appendTo.remove();
197     assertEquals(
198             "Assignment",
199             "Templates frogtommy{0} and leg are out of order.",
200             s.formatAndAppend(
201                     params,
202                     UPRV_LENGTHOF(params),
203                     appendTo,
204                     NULL,
205                     0,
206                     status));
207 
208     // Copy constructor
209     SimplePatternFormatter r(fmt);
210     appendTo.remove();
211     assertEquals(
212             "Copy constructor",
213             "Templates frogtommy{0} and leg are out of order.",
214             r.formatAndAppend(
215                     params,
216                     UPRV_LENGTHOF(params),
217                     appendTo,
218                     NULL,
219                     0,
220                     status));
221     r.compile("{0} meter", status);
222     assertEquals("PlaceholderCount", 1, r.getPlaceholderCount());
223     appendTo.remove();
224     assertEquals(
225             "Replace with new compile",
226             "freddy meter",
227             r.format("freddy", appendTo, status));
228     r.compile("{0}, {1}", status);
229     assertEquals("PlaceholderCount", 2, r.getPlaceholderCount());
230     appendTo.remove();
231     assertEquals(
232             "2 arg",
233             "foo, bar",
234             r.format("foo", "bar", appendTo, status));
235     r.compile("{0}, {1} and {2}", status);
236     assertEquals("PlaceholderCount", 3, r.getPlaceholderCount());
237     appendTo.remove();
238     assertEquals(
239             "3 arg",
240             "foo, bar and baz",
241             r.format("foo", "bar", "baz", appendTo, status));
242     assertSuccess("Status", status);
243 }
244 
TestTooFewPlaceholderValues()245 void SimplePatternFormatterTest::TestTooFewPlaceholderValues() {
246     UErrorCode status = U_ZERO_ERROR;
247     SimplePatternFormatter fmt("{0} and {1}", status);
248     UnicodeString appendTo;
249     UnicodeString firstValue;
250     UnicodeString *params[] = {&firstValue};
251 
252     fmt.format(
253             firstValue, appendTo, status);
254     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
255         errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
256     }
257 
258     status = U_ZERO_ERROR;
259     fmt.formatAndAppend(
260             params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status);
261     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
262         errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
263     }
264 
265     status = U_ZERO_ERROR;
266     fmt.formatAndReplace(
267             params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status);
268     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
269         errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
270     }
271 }
272 
TestBadArguments()273 void SimplePatternFormatterTest::TestBadArguments() {
274     UErrorCode status = U_ZERO_ERROR;
275     SimplePatternFormatter fmt("pickle", status);
276     UnicodeString appendTo;
277 
278     // These succeed
279     fmt.formatAndAppend(
280             NULL, 0, appendTo, NULL, 0, status);
281     fmt.formatAndReplace(
282             NULL, 0, appendTo, NULL, 0, status);
283     assertSuccess("", status);
284     status = U_ZERO_ERROR;
285 
286     // fails
287     fmt.formatAndAppend(
288             NULL, 1, appendTo, NULL, 0, status);
289     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
290         errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() values=NULL but length=1");
291     }
292     status = U_ZERO_ERROR;
293 
294     // fails
295     fmt.formatAndAppend(
296             NULL, 0, appendTo, NULL, 1, status);
297     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
298         errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() offsets=NULL but length=1");
299     }
300     status = U_ZERO_ERROR;
301 
302     // fails because appendTo used as a parameter value
303     SimplePatternFormatter fmt2("Placeholders {0} and {1}", status);
304     UnicodeString frog("frog");
305     const UnicodeString *params[] = { &appendTo, &frog };
306     fmt2.formatAndAppend(params, 2, appendTo, NULL, 0, status);
307     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
308         errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndAppend() value=appendTo");
309     }
310     status = U_ZERO_ERROR;
311 
312 
313     // fails
314     fmt.formatAndReplace(
315             NULL, 1, appendTo, NULL, 0, status);
316     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
317         errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndReplace() values=NULL but length=1");
318     }
319     status = U_ZERO_ERROR;
320 
321     // fails
322     fmt.formatAndReplace(
323             NULL, 0, appendTo, NULL, 1, status);
324     if (status != U_ILLEGAL_ARGUMENT_ERROR) {
325         errln("Expected U_ILLEGAL_ARGUMENT_ERROR: formatAndReplace() offsets=NULL but length=1");
326     }
327 }
328 
TestTextWithNoPlaceholders()329 void SimplePatternFormatterTest::TestTextWithNoPlaceholders() {
330     UErrorCode status = U_ZERO_ERROR;
331     SimplePatternFormatter fmt("{0} has no {1} placeholders.", status);
332     assertEquals(
333             "", " has no  placeholders.", fmt.getTextWithNoPlaceholders());
334 }
335 
TestFormatReplaceNoOptimization()336 void SimplePatternFormatterTest::TestFormatReplaceNoOptimization() {
337     UErrorCode status = U_ZERO_ERROR;
338     SimplePatternFormatter fmt;
339     fmt.compile("{2}, {0}, {1} and {3}", status);
340     if (!assertSuccess("Status", status)) {
341         return;
342     }
343     UnicodeString result("original");
344     int offsets[4];
345     UnicodeString freddy("freddy");
346     UnicodeString frog("frog");
347     UnicodeString by("by");
348     const UnicodeString *params[] = {&result, &freddy, &frog, &by};
349     assertEquals(
350             "",
351             "frog, original, freddy and by",
352             fmt.formatAndReplace(
353                     params,
354                     UPRV_LENGTHOF(params),
355                     result,
356                     offsets,
357                     UPRV_LENGTHOF(offsets),
358                     status));
359     if (!assertSuccess("Status", status)) {
360         return;
361     }
362     int32_t expectedOffsets[] = {6, 16, 0, 27};
363     verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
364 }
365 
TestFormatReplaceNoOptimizationLeadingText()366 void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingText() {
367     UErrorCode status = U_ZERO_ERROR;
368     SimplePatternFormatter fmt;
369     fmt.compile("boo {2}, {0}, {1} and {3}", status);
370     if (!assertSuccess("Status", status)) {
371         return;
372     }
373     UnicodeString result("original");
374     int offsets[4];
375     UnicodeString freddy("freddy");
376     UnicodeString frog("frog");
377     UnicodeString by("by");
378     const UnicodeString *params[] = {&freddy, &frog, &result, &by};
379     assertEquals(
380             "",
381             "boo original, freddy, frog and by",
382             fmt.formatAndReplace(
383                     params,
384                     UPRV_LENGTHOF(params),
385                     result,
386                     offsets,
387                     UPRV_LENGTHOF(offsets),
388                     status));
389     if (!assertSuccess("Status", status)) {
390         return;
391     }
392     int32_t expectedOffsets[] = {14, 22, 4, 31};
393     verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
394 }
395 
TestFormatReplaceOptimization()396 void SimplePatternFormatterTest::TestFormatReplaceOptimization() {
397     UErrorCode status = U_ZERO_ERROR;
398     SimplePatternFormatter fmt;
399     fmt.compile("{2}, {0}, {1} and {3}", status);
400     if (!assertSuccess("Status", status)) {
401         return;
402     }
403     UnicodeString result("original");
404     int offsets[4];
405     UnicodeString freddy("freddy");
406     UnicodeString frog("frog");
407     UnicodeString by("by");
408     const UnicodeString *params[] = {&freddy, &frog, &result, &by};
409     assertEquals(
410             "",
411             "original, freddy, frog and by",
412             fmt.formatAndReplace(
413                     params,
414                     UPRV_LENGTHOF(params),
415                     result,
416                     offsets,
417                     UPRV_LENGTHOF(offsets),
418                     status));
419     if (!assertSuccess("Status", status)) {
420         return;
421     }
422     int32_t expectedOffsets[] = {10, 18, 0, 27};
423     verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
424 }
425 
TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice()426 void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice() {
427     UErrorCode status = U_ZERO_ERROR;
428     SimplePatternFormatter fmt;
429     fmt.compile("{2}, {0}, {1} and {3} {2}", status);
430     if (!assertSuccess("Status", status)) {
431         return;
432     }
433     UnicodeString result("original");
434     int offsets[4];
435     UnicodeString freddy("freddy");
436     UnicodeString frog("frog");
437     UnicodeString by("by");
438     const UnicodeString *params[] = {&freddy, &frog, &result, &by};
439     assertEquals(
440             "",
441             "original, freddy, frog and by original",
442             fmt.formatAndReplace(
443                     params,
444                     UPRV_LENGTHOF(params),
445                     result,
446                     offsets,
447                     UPRV_LENGTHOF(offsets),
448                     status));
449     if (!assertSuccess("Status", status)) {
450         return;
451     }
452     int32_t expectedOffsets[] = {10, 18, 30, 27};
453     verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
454 }
455 
TestFormatReplaceOptimizationNoOffsets()456 void SimplePatternFormatterTest::TestFormatReplaceOptimizationNoOffsets() {
457     UErrorCode status = U_ZERO_ERROR;
458     SimplePatternFormatter fmt;
459     fmt.compile("{2}, {0}, {1} and {3}", status);
460     if (!assertSuccess("Status", status)) {
461         return;
462     }
463     UnicodeString result("original");
464     UnicodeString freddy("freddy");
465     UnicodeString frog("frog");
466     UnicodeString by("by");
467     const UnicodeString *params[] = {&freddy, &frog, &result, &by};
468     assertEquals(
469             "",
470             "original, freddy, frog and by",
471             fmt.formatAndReplace(
472                     params,
473                     UPRV_LENGTHOF(params),
474                     result,
475                     NULL,
476                     0,
477                     status));
478     assertSuccess("Status", status);
479 }
480 
TestFormatReplaceNoOptimizationNoOffsets()481 void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationNoOffsets() {
482     UErrorCode status = U_ZERO_ERROR;
483     SimplePatternFormatter fmt("Placeholders {0} and {1}", status);
484     UnicodeString result("previous:");
485     UnicodeString frog("frog");
486     const UnicodeString *params[] = {&result, &frog};
487     assertEquals(
488             "",
489             "Placeholders previous: and frog",
490             fmt.formatAndReplace(
491                     params,
492                     UPRV_LENGTHOF(params),
493                     result,
494                     NULL,
495                     0,
496                     status));
497     assertSuccess("Status", status);
498 }
499 
TestQuotingLikeMessageFormat()500 void SimplePatternFormatterTest::TestQuotingLikeMessageFormat() {
501     UErrorCode status = U_ZERO_ERROR;
502     UnicodeString pattern = "{0} don't can''t '{5}''}{a' again '}'{1} to the '{end";
503     SimplePatternFormatter spf(pattern, status);
504     MessageFormat mf(pattern, Locale::getRoot(), status);
505     UnicodeString expected = "X don't can't {5}'}{a again }Y to the {end";
506     UnicodeString x("X"), y("Y");
507     Formattable values[] = { x, y };
508     UnicodeString result;
509     FieldPosition ignore(FieldPosition::DONT_CARE);
510     assertEquals("MessageFormat", expected, mf.format(values, 2, result, ignore, status));
511     assertEquals("SimplePatternFormatter", expected, spf.format(x, y, result.remove(), status));
512 }
513 
verifyOffsets(const int32_t * expected,const int32_t * actual,int32_t count)514 void SimplePatternFormatterTest::verifyOffsets(
515         const int32_t *expected, const int32_t *actual, int32_t count) {
516     for (int32_t i = 0; i < count; ++i) {
517         if (expected[i] != actual[i]) {
518             errln("Expected %d, got %d", expected[i], actual[i]);
519         }
520     }
521 }
522 
createSimplePatternFormatterTest()523 extern IntlTest *createSimplePatternFormatterTest() {
524     return new SimplePatternFormatterTest();
525 }
526