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