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