format_tests.c (15574B)
1 #include "format.h" 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <time.h> 6 7 #include "utest.h" 8 9 UTEST(print_time, PositiveOffset) { 10 char* buf = NULL; 11 size_t size = 0; 12 FILE* out = open_memstream(&buf, &size); 13 ASSERT_NE(NULL, out); 14 15 /* Test time: 2023-12-08 10:30:00 UTC */ 16 time_t test_time = 1702031400; 17 int timezone_offset = 540; /* +09:00 */ 18 19 print_time(out, test_time, timezone_offset); 20 fclose(out); 21 22 EXPECT_STREQ("Fri, 8 Dec 2023 19:30:00 +0900", buf); 23 24 free(buf); 25 } 26 27 UTEST(print_time, NegativeOffset) { 28 char* buf = NULL; 29 size_t size = 0; 30 FILE* out = open_memstream(&buf, &size); 31 ASSERT_NE(NULL, out); 32 33 /* Test time: 2023-12-08 10:30:00 UTC */ 34 time_t test_time = 1702031400; 35 int timezone_offset = -300; /* -05:00 */ 36 37 print_time(out, test_time, timezone_offset); 38 fclose(out); 39 40 EXPECT_STREQ("Fri, 8 Dec 2023 05:30:00 -0500", buf); 41 42 free(buf); 43 } 44 45 UTEST(print_time, ZeroOffset) { 46 char* buf = NULL; 47 size_t size = 0; 48 FILE* out = open_memstream(&buf, &size); 49 ASSERT_NE(NULL, out); 50 51 /* Test time: 2023-12-08 10:30:00 UTC */ 52 time_t test_time = 1702031400; 53 int timezone_offset = 0; /* UTC */ 54 55 print_time(out, test_time, timezone_offset); 56 fclose(out); 57 58 EXPECT_STREQ("Fri, 8 Dec 2023 10:30:00 +0000", buf); 59 60 free(buf); 61 } 62 63 UTEST(print_time, IllegalOffset) { 64 char* buf = NULL; 65 size_t size = 0; 66 FILE* out = open_memstream(&buf, &size); 67 ASSERT_NE(NULL, out); 68 69 /* Test time: 2023-12-08 10:30:00 UTC */ 70 time_t test_time = 1702031400; 71 int timezone_offset = 1441; /* 24 hours + 1 minute */ 72 73 print_time(out, test_time, timezone_offset); 74 fclose(out); 75 76 EXPECT_STREQ("", buf); 77 78 free(buf); 79 } 80 81 UTEST(print_time_z, Basic) { 82 char* buf = NULL; 83 size_t size = 0; 84 FILE* out = open_memstream(&buf, &size); 85 ASSERT_NE(NULL, out); 86 87 /* Test time: 2023-12-08 10:30:00 UTC */ 88 time_t test_time = 1702031400; 89 90 print_time_z(out, test_time); 91 fclose(out); 92 93 EXPECT_STREQ("2023-12-08T10:30:00Z", buf); 94 95 free(buf); 96 } 97 98 UTEST(print_time_short, Basic) { 99 char* buf = NULL; 100 size_t size = 0; 101 FILE* out = open_memstream(&buf, &size); 102 ASSERT_NE(NULL, out); 103 104 /* Test time: 2023-12-08 10:30:00 UTC */ 105 time_t test_time = 1702031400; 106 107 print_time_short(out, test_time); 108 fclose(out); 109 110 EXPECT_STREQ("2023-12-08 10:30", buf); 111 112 free(buf); 113 } 114 115 UTEST(print_percent_encoded, NoEncoding) { 116 char* buf = NULL; 117 size_t size = 0; 118 FILE* out = open_memstream(&buf, &size); 119 ASSERT_NE(NULL, out); 120 121 print_percent_encoded(out, "abcdef-1234/.,"); 122 fclose(out); 123 124 EXPECT_STREQ("abcdef-1234/.,", buf); 125 126 free(buf); 127 } 128 129 UTEST(print_percent_encoded, SimpleEncoding) { 130 char* buf = NULL; 131 size_t size = 0; 132 FILE* out = open_memstream(&buf, &size); 133 ASSERT_NE(NULL, out); 134 135 print_percent_encoded(out, "hello world"); 136 fclose(out); 137 138 EXPECT_STREQ("hello%20world", buf); 139 140 free(buf); 141 } 142 143 UTEST(print_percent_encoded, ReservedChars) { 144 char* buf = NULL; 145 size_t size = 0; 146 FILE* out = open_memstream(&buf, &size); 147 ASSERT_NE(NULL, out); 148 149 print_percent_encoded(out, " \"%[]:?@"); 150 fclose(out); 151 152 EXPECT_STREQ("%20%22%25%5B%5D%3A%3F%40", buf); 153 154 free(buf); 155 } 156 157 UTEST(print_percent_encoded, EmptyString) { 158 char* buf = NULL; 159 size_t size = 0; 160 FILE* out = open_memstream(&buf, &size); 161 ASSERT_NE(NULL, out); 162 163 print_percent_encoded(out, ""); 164 fclose(out); 165 166 EXPECT_STREQ("", buf); 167 168 free(buf); 169 } 170 171 UTEST(print_xml_encoded_len, FullEncoding) { 172 char* buf = NULL; 173 size_t size = 0; 174 FILE* out = open_memstream(&buf, &size); 175 ASSERT_NE(NULL, out); 176 177 const char* test_str = "<tag attr='value'> & \"text\""; 178 print_xml_encoded_len(out, test_str, -1, true); 179 fclose(out); 180 181 EXPECT_STREQ("<tag attr='value'> & "text"", buf); 182 183 free(buf); 184 } 185 186 UTEST(print_xml_encoded_len, PartialEncoding) { 187 char* buf = NULL; 188 size_t size = 0; 189 FILE* out = open_memstream(&buf, &size); 190 ASSERT_NE(NULL, out); 191 192 const char* test_str = "<tag>hello</tag>"; 193 print_xml_encoded_len(out, test_str, 5, true); 194 fclose(out); 195 196 EXPECT_STREQ("<tag>", buf); 197 198 free(buf); 199 } 200 201 UTEST(print_xml_encoded_len, WithCrlf) { 202 char* buf = NULL; 203 size_t size = 0; 204 FILE* out = open_memstream(&buf, &size); 205 ASSERT_NE(NULL, out); 206 207 const char* test_str = "line1\r\nline2"; 208 print_xml_encoded_len(out, test_str, -1, true); 209 fclose(out); 210 211 EXPECT_STREQ("line1\r\nline2", buf); 212 213 free(buf); 214 } 215 216 UTEST(print_xml_encoded_len, WithoutCrlf) { 217 char* buf = NULL; 218 size_t size = 0; 219 FILE* out = open_memstream(&buf, &size); 220 ASSERT_NE(NULL, out); 221 222 const char* test_str = "line1\r\nline2"; 223 print_xml_encoded_len(out, test_str, -1, false); 224 fclose(out); 225 226 EXPECT_STREQ("line1line2", buf); 227 228 free(buf); 229 } 230 231 UTEST(print_xml_encoded_len, EmptyString) { 232 char* buf = NULL; 233 size_t size = 0; 234 FILE* out = open_memstream(&buf, &size); 235 ASSERT_NE(NULL, out); 236 237 print_xml_encoded_len(out, "", -1, true); 238 fclose(out); 239 240 EXPECT_STREQ("", buf); 241 242 free(buf); 243 } 244 245 UTEST(print_xml_encoded_len, ZeroLength) { 246 char* buf = NULL; 247 size_t size = 0; 248 FILE* out = open_memstream(&buf, &size); 249 ASSERT_NE(NULL, out); 250 251 print_xml_encoded_len(out, "some text", 0, true); 252 fclose(out); 253 254 EXPECT_STREQ("", buf); 255 256 free(buf); 257 } 258 259 UTEST(print_xml_encoded_len, ControlCharacters) { 260 char* buf = NULL; 261 size_t size = 0; 262 FILE* out = open_memstream(&buf, &size); 263 ASSERT_NE(NULL, out); 264 265 /* Tab (\t), LF (\n), CR (\r) should be preserved. 266 * Other control characters like \x01 (SOH) should be filtered. */ 267 const char* test_str = 268 "a\t\n\r\x01" 269 "b"; 270 print_xml_encoded_len(out, test_str, -1, true); 271 fclose(out); 272 273 EXPECT_STREQ("a\t\n\rb", buf); 274 275 free(buf); 276 } 277 278 UTEST(print_xml_encoded_len, MultiByteUtf8) { 279 char* buf = NULL; 280 size_t size = 0; 281 FILE* out = open_memstream(&buf, &size); 282 ASSERT_NE(NULL, out); 283 284 /* UTF-8 'é' is 0xC3 0xA9. These should be preserved as-is. */ 285 const char* test_str = "caf\xC3\xA9"; 286 print_xml_encoded_len(out, test_str, -1, true); 287 fclose(out); 288 289 EXPECT_STREQ("caf\xC3\xA9", buf); 290 291 free(buf); 292 } 293 294 UTEST(print_xml_encoded_len, EmbeddedNull) { 295 char* buf = NULL; 296 size_t size = 0; 297 FILE* out = open_memstream(&buf, &size); 298 ASSERT_NE(NULL, out); 299 300 /* The function should stop at \0 even if str_len is larger. */ 301 const char* test_str = "abc\0def"; 302 print_xml_encoded_len(out, test_str, 10, true); 303 fclose(out); 304 305 EXPECT_STREQ("abc", buf); 306 307 free(buf); 308 } 309 310 UTEST(print_gopher_text_len, BasicWithLf) { 311 char* buf = NULL; 312 size_t size = 0; 313 FILE* out = open_memstream(&buf, &size); 314 ASSERT_NE(NULL, out); 315 316 print_gopher_text_len(out, "hello\nworld", -1, true); 317 fclose(out); 318 319 EXPECT_STREQ("hello\nworld", buf); 320 321 free(buf); 322 } 323 324 UTEST(print_gopher_text_len, BasicNoLf) { 325 char* buf = NULL; 326 size_t size = 0; 327 FILE* out = open_memstream(&buf, &size); 328 ASSERT_NE(NULL, out); 329 330 print_gopher_text_len(out, "hello\nworld\r", -1, false); 331 fclose(out); 332 333 EXPECT_STREQ("helloworld", buf); 334 335 free(buf); 336 } 337 338 UTEST(print_gopher_text_len, EscapesWithLf) { 339 char* buf = NULL; 340 size_t size = 0; 341 FILE* out = open_memstream(&buf, &size); 342 ASSERT_NE(NULL, out); 343 344 print_gopher_text_len(out, "[hello]\n[world]", -1, true); 345 fclose(out); 346 347 EXPECT_STREQ("[|hello]\n[|world]", buf); 348 349 free(buf); 350 } 351 352 UTEST(print_gopher_text_len, DoesNotEscapeIfBracketNotImmediatelyAfterLf) { 353 char* buf = NULL; 354 size_t size = 0; 355 FILE* out = open_memstream(&buf, &size); 356 ASSERT_NE(NULL, out); 357 358 print_gopher_text_len(out, "[hello]\n\t[world]", -1, true); 359 fclose(out); 360 361 EXPECT_STREQ("[|hello]\n [world]", buf); 362 363 free(buf); 364 } 365 366 UTEST(print_gopher_text_len, NoEscapesWithoutLf) { 367 char* buf = NULL; 368 size_t size = 0; 369 FILE* out = open_memstream(&buf, &size); 370 ASSERT_NE(NULL, out); 371 372 print_gopher_text_len(out, "[hello]\n\t[world]", -1, false); 373 fclose(out); 374 375 EXPECT_STREQ("[hello] [world]", buf); 376 377 free(buf); 378 } 379 380 UTEST(print_gopher_text_len, PartialLen) { 381 char* buf = NULL; 382 size_t size = 0; 383 FILE* out = open_memstream(&buf, &size); 384 ASSERT_NE(NULL, out); 385 386 print_gopher_text_len(out, "[hello]\n\t[world]", 8, true); 387 fclose(out); 388 389 EXPECT_STREQ("[|hello]\n", buf); 390 391 free(buf); 392 } 393 394 UTEST(print_gopher_text_len, EmptyString) { 395 char* buf = NULL; 396 size_t size = 0; 397 FILE* out = open_memstream(&buf, &size); 398 ASSERT_NE(NULL, out); 399 400 print_gopher_text_len(out, "", -1, true); 401 fclose(out); 402 403 EXPECT_STREQ("", buf); 404 405 free(buf); 406 } 407 408 UTEST(print_gopher_link, SpecialChars) { 409 char* buf = NULL; 410 size_t size = 0; 411 FILE* out = open_memstream(&buf, &size); 412 ASSERT_NE(NULL, out); 413 414 print_gopher_link(out, "|\t\r\n"); 415 fclose(out); 416 417 EXPECT_STREQ("\\| ", buf); 418 419 free(buf); 420 } 421 422 UTEST(print_gopher_link, MixedContent) { 423 char* buf = NULL; 424 size_t size = 0; 425 FILE* out = open_memstream(&buf, &size); 426 ASSERT_NE(NULL, out); 427 428 print_gopher_link(out, "hello|world\t"); 429 fclose(out); 430 431 EXPECT_STREQ("hello\\|world ", buf); 432 433 free(buf); 434 } 435 436 UTEST(print_gopher_link, EmptyString) { 437 char* buf = NULL; 438 size_t size = 0; 439 FILE* out = open_memstream(&buf, &size); 440 ASSERT_NE(NULL, out); 441 442 print_gopher_link(out, ""); 443 fclose(out); 444 445 EXPECT_STREQ("", buf); 446 447 free(buf); 448 } 449 450 UTEST(print_gopher_link_padded, BasicPadding) { 451 char* buf = NULL; 452 size_t size = 0; 453 FILE* out = open_memstream(&buf, &size); 454 ASSERT_NE(NULL, out); 455 456 print_gopher_link_padded(out, "hello", 10, ' '); 457 fclose(out); 458 459 EXPECT_STREQ("hello ", buf); 460 461 free(buf); 462 } 463 464 UTEST(print_gopher_link_padded, NoPadding) { 465 char* buf = NULL; 466 size_t size = 0; 467 FILE* out = open_memstream(&buf, &size); 468 ASSERT_NE(NULL, out); 469 470 print_gopher_link_padded(out, "hello", 5, ' '); 471 fclose(out); 472 473 EXPECT_STREQ("hello", buf); 474 475 free(buf); 476 } 477 478 UTEST(print_gopher_link_padded, Truncation) { 479 char* buf = NULL; 480 size_t size = 0; 481 FILE* out = open_memstream(&buf, &size); 482 ASSERT_NE(NULL, out); 483 484 print_gopher_link_padded(out, "hello world", 10, ' '); 485 fclose(out); 486 487 EXPECT_STREQ("hello wor…", buf); 488 489 free(buf); 490 } 491 492 UTEST(print_gopher_link_padded, SpecialChars) { 493 char* buf = NULL; 494 size_t size = 0; 495 FILE* out = open_memstream(&buf, &size); 496 ASSERT_NE(NULL, out); 497 498 print_gopher_link_padded(out, "a|\t\r\nb", 20, '.'); 499 fclose(out); 500 501 EXPECT_STREQ("a\\| b.........", buf); 502 503 free(buf); 504 } 505 506 UTEST(print_gopher_link_padded, MultiByte) { 507 char* buf = NULL; 508 size_t size = 0; 509 FILE* out = open_memstream(&buf, &size); 510 ASSERT_NE(NULL, out); 511 512 print_gopher_link_padded(out, "こんにちは", 20, ' '); 513 fclose(out); 514 515 EXPECT_STREQ("こんにちは ", buf); 516 517 free(buf); 518 } 519 520 UTEST(print_gopher_link_padded, ZeroWidth) { 521 char* buf = NULL; 522 size_t size = 0; 523 FILE* out = open_memstream(&buf, &size); 524 ASSERT_NE(NULL, out); 525 526 print_gopher_link_padded(out, "hello", 0, ' '); 527 fclose(out); 528 529 EXPECT_STREQ("", buf); 530 531 free(buf); 532 } 533 534 UTEST(print_gopher_link_padded, NoPadChar) { 535 char* buf = NULL; 536 size_t size = 0; 537 FILE* out = open_memstream(&buf, &size); 538 ASSERT_NE(NULL, out); 539 540 print_gopher_link_padded(out, "hello", 10, '\0'); 541 fclose(out); 542 543 EXPECT_STREQ("hello", buf); 544 545 free(buf); 546 } 547 548 UTEST(print_gopher_link_padded, ComplexEmoji) { 549 char* buf = NULL; 550 size_t size = 0; 551 FILE* out = open_memstream(&buf, &size); 552 ASSERT_NE(NULL, out); 553 554 /* UTF-8 encoding of Unicode extended grapheme cluster for woman health worker 555 with medium-dark skin tone. This emoji is a Unicode emoji ZWJ sequence 556 composed of 5 code points. */ 557 const char* emoji = // 👩🏾⚕️ 558 "\xF0\x9F\x91\xA9" // U+1F469: Woman 👩 559 "\xF0\x9F\x8F\xBE" // U+1F3FE: Emoji modifier Fitzpatrick type 5 🏾 560 "\xE2\x80\x8D" // U+200D: Zero-width joiner 561 "\xE2\x9A\x95" // U+2695: Staff of Aesculapius ⚕ 562 "\xEF\xB8\x8F"; // U+FE0F: Variation selector 16 (colour emoji) 563 print_gopher_link_padded(out, emoji, 10, ' '); 564 fclose(out); 565 566 /* Expect 1 emoji, 8 spaces. */ 567 EXPECT_STREQ("👩🏾⚕️ ", buf); 568 569 free(buf); 570 } 571 572 UTEST(print_gopher_link_padded, MultiColumnTruncation) { 573 { 574 char* buf = NULL; 575 size_t size = 0; 576 FILE* out = open_memstream(&buf, &size); 577 ASSERT_NE(NULL, out); 578 579 /* "あいう" (6 columns). Width 4. Should be "あ…" + 1 space. */ 580 print_gopher_link_padded(out, "あいう", 4, ' '); 581 fclose(out); 582 EXPECT_STREQ("あ… ", buf); 583 free(buf); 584 } 585 586 { 587 char* buf = NULL; 588 size_t size = 0; 589 FILE* out = open_memstream(&buf, &size); 590 ASSERT_NE(NULL, out); 591 592 /* "あいう" (6 columns). Width 5. Should be "あい…" (5 columns). */ 593 print_gopher_link_padded(out, "あいう", 5, ' '); 594 fclose(out); 595 EXPECT_STREQ("あい…", buf); 596 free(buf); 597 } 598 } 599 600 UTEST(print_utf8_padded, Basic) { 601 char* buf = NULL; 602 size_t size = 0; 603 FILE* out = open_memstream(&buf, &size); 604 ASSERT_NE(NULL, out); 605 606 print_utf8_padded(out, "hello", 10, ' '); 607 fclose(out); 608 609 EXPECT_STREQ("hello ", buf); 610 611 free(buf); 612 } 613 614 UTEST(print_utf8_padded, MultiByte) { 615 char* buf = NULL; 616 size_t size = 0; 617 FILE* out = open_memstream(&buf, &size); 618 ASSERT_NE(NULL, out); 619 620 /* "こんにちは" is 10 columns. */ 621 print_utf8_padded(out, "こんにちは", 15, ' '); 622 fclose(out); 623 624 EXPECT_STREQ("こんにちは ", buf); 625 626 free(buf); 627 } 628 629 UTEST(print_utf8_padded, Truncation) { 630 char* buf = NULL; 631 size_t size = 0; 632 FILE* out = open_memstream(&buf, &size); 633 ASSERT_NE(NULL, out); 634 635 /* "hello world" is 11 columns. Width 10. */ 636 print_utf8_padded(out, "hello world", 10, ' '); 637 fclose(out); 638 639 EXPECT_STREQ("hello wor…", buf); 640 641 free(buf); 642 } 643 644 UTEST(print_utf8_padded, MultiColumnTruncation) { 645 { 646 char* buf = NULL; 647 size_t size = 0; 648 FILE* out = open_memstream(&buf, &size); 649 ASSERT_NE(NULL, out); 650 651 /* "あいう" (6 columns). Width 4. Should be "あ…" + 1 space. */ 652 print_utf8_padded(out, "あいう", 4, ' '); 653 fclose(out); 654 EXPECT_STREQ("あ… ", buf); 655 free(buf); 656 } 657 658 { 659 char* buf = NULL; 660 size_t size = 0; 661 FILE* out = open_memstream(&buf, &size); 662 ASSERT_NE(NULL, out); 663 664 /* "あいう" (6 columns). Width 5. Should be "あい…" (5 columns). */ 665 print_utf8_padded(out, "あいう", 5, ' '); 666 fclose(out); 667 EXPECT_STREQ("あい…", buf); 668 free(buf); 669 } 670 } 671 672 UTEST(print_utf8_padded, ComplexEmoji) { 673 char* buf = NULL; 674 size_t size = 0; 675 FILE* out = open_memstream(&buf, &size); 676 ASSERT_NE(NULL, out); 677 678 /* UTF-8 encoding of Unicode extended grapheme cluster for woman health worker 679 with medium-dark skin tone. This emoji is a Unicode emoji ZWJ sequence 680 composed of 5 code points. */ 681 const char* emoji = // 👩🏾⚕️ 682 "\xF0\x9F\x91\xA9" // U+1F469: Woman 👩 683 "\xF0\x9F\x8F\xBE" // U+1F3FE: Emoji modifier Fitzpatrick type 5 🏾 684 "\xE2\x80\x8D" // U+200D: Zero-width joiner 685 "\xE2\x9A\x95" // U+2695: Staff of Aesculapius ⚕ 686 "\xEF\xB8\x8F"; // U+FE0F: Variation selector 16 (colour emoji) 687 print_utf8_padded(out, emoji, 10, ' '); 688 fclose(out); 689 690 EXPECT_STREQ("👩🏾⚕️ ", buf); 691 692 free(buf); 693 } 694 695 UTEST(print_utf8_padded, NoPadChar) { 696 char* buf = NULL; 697 size_t size = 0; 698 FILE* out = open_memstream(&buf, &size); 699 ASSERT_NE(NULL, out); 700 701 print_utf8_padded(out, "hello", 10, '\0'); 702 fclose(out); 703 704 EXPECT_STREQ("hello", buf); 705 706 free(buf); 707 } 708 709 UTEST(print_utf8_padded, ZeroWidth) { 710 char* buf = NULL; 711 size_t size = 0; 712 FILE* out = open_memstream(&buf, &size); 713 ASSERT_NE(NULL, out); 714 715 print_utf8_padded(out, "hello", 0, ' '); 716 fclose(out); 717 718 EXPECT_STREQ("", buf); 719 720 free(buf); 721 }