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: 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); 40 private: 41 void verifyOffsets( 42 const int32_t *expected, 43 const int32_t *actual, 44 int32_t count); 45 }; 46 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 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 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 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 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 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 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 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 331 void SimpleFormatterTest::TestTextWithNoArguments() { 332 UErrorCode status = U_ZERO_ERROR; 333 SimpleFormatter fmt("{0} has no {1} arguments.", status); 334 assertEquals( 335 "", " has no arguments.", fmt.getTextWithNoArguments()); 336 } 337 338 void SimpleFormatterTest::TestFormatReplaceNoOptimization() { 339 UErrorCode status = U_ZERO_ERROR; 340 SimpleFormatter fmt; 341 fmt.applyPattern("{2}, {0}, {1} and {3}", status); 342 if (!assertSuccess("Status", status)) { 343 return; 344 } 345 UnicodeString result("original"); 346 int32_t offsets[4]; 347 UnicodeString freddy("freddy"); 348 UnicodeString frog("frog"); 349 UnicodeString by("by"); 350 const UnicodeString *params[] = {&result, &freddy, &frog, &by}; 351 assertEquals( 352 "", 353 "frog, original, freddy and by", 354 fmt.formatAndReplace( 355 params, 356 UPRV_LENGTHOF(params), 357 result, 358 offsets, 359 UPRV_LENGTHOF(offsets), 360 status)); 361 if (!assertSuccess("Status", status)) { 362 return; 363 } 364 int32_t expectedOffsets[] = {6, 16, 0, 27}; 365 verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); 366 } 367 368 void SimpleFormatterTest::TestFormatReplaceNoOptimizationLeadingText() { 369 UErrorCode status = U_ZERO_ERROR; 370 SimpleFormatter fmt; 371 fmt.applyPattern("boo {2}, {0}, {1} and {3}", status); 372 if (!assertSuccess("Status", status)) { 373 return; 374 } 375 UnicodeString result("original"); 376 int32_t offsets[4]; 377 UnicodeString freddy("freddy"); 378 UnicodeString frog("frog"); 379 UnicodeString by("by"); 380 const UnicodeString *params[] = {&freddy, &frog, &result, &by}; 381 assertEquals( 382 "", 383 "boo original, freddy, frog and by", 384 fmt.formatAndReplace( 385 params, 386 UPRV_LENGTHOF(params), 387 result, 388 offsets, 389 UPRV_LENGTHOF(offsets), 390 status)); 391 if (!assertSuccess("Status", status)) { 392 return; 393 } 394 int32_t expectedOffsets[] = {14, 22, 4, 31}; 395 verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); 396 } 397 398 void SimpleFormatterTest::TestFormatReplaceOptimization() { 399 UErrorCode status = U_ZERO_ERROR; 400 SimpleFormatter fmt; 401 fmt.applyPattern("{2}, {0}, {1} and {3}", status); 402 if (!assertSuccess("Status", status)) { 403 return; 404 } 405 UnicodeString result("original"); 406 int32_t offsets[4]; 407 UnicodeString freddy("freddy"); 408 UnicodeString frog("frog"); 409 UnicodeString by("by"); 410 const UnicodeString *params[] = {&freddy, &frog, &result, &by}; 411 assertEquals( 412 "", 413 "original, freddy, frog and by", 414 fmt.formatAndReplace( 415 params, 416 UPRV_LENGTHOF(params), 417 result, 418 offsets, 419 UPRV_LENGTHOF(offsets), 420 status)); 421 if (!assertSuccess("Status", status)) { 422 return; 423 } 424 int32_t expectedOffsets[] = {10, 18, 0, 27}; 425 verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); 426 } 427 428 void SimpleFormatterTest::TestFormatReplaceNoOptimizationLeadingArgumentUsedTwice() { 429 UErrorCode status = U_ZERO_ERROR; 430 SimpleFormatter fmt; 431 fmt.applyPattern("{2}, {0}, {1} and {3} {2}", status); 432 if (!assertSuccess("Status", status)) { 433 return; 434 } 435 UnicodeString result("original"); 436 int32_t offsets[4]; 437 UnicodeString freddy("freddy"); 438 UnicodeString frog("frog"); 439 UnicodeString by("by"); 440 const UnicodeString *params[] = {&freddy, &frog, &result, &by}; 441 assertEquals( 442 "", 443 "original, freddy, frog and by original", 444 fmt.formatAndReplace( 445 params, 446 UPRV_LENGTHOF(params), 447 result, 448 offsets, 449 UPRV_LENGTHOF(offsets), 450 status)); 451 if (!assertSuccess("Status", status)) { 452 return; 453 } 454 int32_t expectedOffsets[] = {10, 18, 30, 27}; 455 verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets)); 456 } 457 458 void SimpleFormatterTest::TestFormatReplaceOptimizationNoOffsets() { 459 UErrorCode status = U_ZERO_ERROR; 460 SimpleFormatter fmt; 461 fmt.applyPattern("{2}, {0}, {1} and {3}", status); 462 if (!assertSuccess("Status", status)) { 463 return; 464 } 465 UnicodeString result("original"); 466 UnicodeString freddy("freddy"); 467 UnicodeString frog("frog"); 468 UnicodeString by("by"); 469 const UnicodeString *params[] = {&freddy, &frog, &result, &by}; 470 assertEquals( 471 "", 472 "original, freddy, frog and by", 473 fmt.formatAndReplace( 474 params, 475 UPRV_LENGTHOF(params), 476 result, 477 NULL, 478 0, 479 status)); 480 assertSuccess("Status", status); 481 } 482 483 void SimpleFormatterTest::TestFormatReplaceNoOptimizationNoOffsets() { 484 UErrorCode status = U_ZERO_ERROR; 485 SimpleFormatter fmt("Arguments {0} and {1}", status); 486 UnicodeString result("previous:"); 487 UnicodeString frog("frog"); 488 const UnicodeString *params[] = {&result, &frog}; 489 assertEquals( 490 "", 491 "Arguments previous: and frog", 492 fmt.formatAndReplace( 493 params, 494 UPRV_LENGTHOF(params), 495 result, 496 NULL, 497 0, 498 status)); 499 assertSuccess("Status", status); 500 } 501 502 void SimpleFormatterTest::TestQuotingLikeMessageFormat() { 503 #if !UCONFIG_NO_FORMATTING 504 UErrorCode status = U_ZERO_ERROR; 505 UnicodeString pattern = "{0} don't can''t '{5}''}{a' again '}'{1} to the '{end"; 506 SimpleFormatter spf(pattern, status); 507 MessageFormat mf(pattern, Locale::getRoot(), status); 508 UnicodeString expected = "X don't can't {5}'}{a again }Y to the {end"; 509 UnicodeString x("X"), y("Y"); 510 Formattable values[] = { x, y }; 511 UnicodeString result; 512 FieldPosition ignore(FieldPosition::DONT_CARE); 513 assertEquals("MessageFormat", expected, mf.format(values, 2, result, ignore, status)); 514 assertEquals("SimpleFormatter", expected, spf.format(x, y, result.remove(), status)); 515 #endif /* !UCONFIG_NO_FORMATTING */ 516 } 517 518 void SimpleFormatterTest::verifyOffsets( 519 const int32_t *expected, const int32_t *actual, int32_t count) { 520 for (int32_t i = 0; i < count; ++i) { 521 if (expected[i] != actual[i]) { 522 errln("Expected %d, got %d", expected[i], actual[i]); 523 } 524 } 525 } 526 527 extern IntlTest *createSimpleFormatterTest() { 528 return new SimpleFormatterTest(); 529 } 530