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