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: 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 523 extern IntlTest *createSimplePatternFormatterTest() { 524 return new SimplePatternFormatterTest(); 525 } 526