decode_string_test.go (19423B)
1 package gojay 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 ) 11 12 func TestDecoderString(t *testing.T) { 13 testCases := []struct { 14 name string 15 json string 16 expectedResult string 17 err bool 18 errType interface{} 19 }{ 20 { 21 name: "basic-string", 22 json: `"string"`, 23 expectedResult: "string", 24 err: false, 25 }, 26 { 27 name: "string-solidus", 28 json: `"\/"`, 29 expectedResult: "/", 30 err: false, 31 }, 32 { 33 name: "basic-string", 34 json: ``, 35 expectedResult: "", 36 err: false, 37 }, 38 { 39 name: "basic-string", 40 json: `""`, 41 expectedResult: "", 42 err: false, 43 }, 44 { 45 name: "basic-string2", 46 json: `"hello world!"`, 47 expectedResult: "hello world!", 48 err: false, 49 }, 50 { 51 name: "escape-control-char", 52 json: `"\n"`, 53 expectedResult: "\n", 54 err: false, 55 }, 56 { 57 name: "escape-control-char", 58 json: `"\\n"`, 59 expectedResult: `\n`, 60 err: false, 61 }, 62 { 63 name: "escape-control-char", 64 json: `"\t"`, 65 expectedResult: "\t", 66 err: false, 67 }, 68 { 69 name: "escape-control-char", 70 json: `"\\t"`, 71 expectedResult: `\t`, 72 err: false, 73 }, 74 { 75 name: "escape-control-char", 76 json: `"\b"`, 77 expectedResult: "\b", 78 err: false, 79 }, 80 { 81 name: "escape-control-char", 82 json: `"\\b"`, 83 expectedResult: `\b`, 84 err: false, 85 }, 86 { 87 name: "escape-control-char", 88 json: `"\f"`, 89 expectedResult: "\f", 90 err: false, 91 }, 92 { 93 name: "escape-control-char", 94 json: `"\\f"`, 95 expectedResult: `\f`, 96 err: false, 97 }, 98 { 99 name: "escape-control-char", 100 json: `"\r"`, 101 expectedResult: "\r", 102 err: false, 103 }, 104 { 105 name: "escape-control-char", 106 json: `"\`, 107 expectedResult: "", 108 err: true, 109 }, 110 { 111 name: "escape-control-char-solidus", 112 json: `"\/"`, 113 expectedResult: "/", 114 err: false, 115 }, 116 { 117 name: "escape-control-char-solidus", 118 json: `"/"`, 119 expectedResult: "/", 120 err: false, 121 }, 122 { 123 name: "escape-control-char-solidus-escape-char", 124 json: `"\\/"`, 125 expectedResult: `\/`, 126 err: false, 127 }, 128 { 129 name: "escape-control-char", 130 json: `"\\r"`, 131 expectedResult: `\r`, 132 err: false, 133 }, 134 { 135 name: "utf8", 136 json: `"𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿"`, 137 expectedResult: "𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿", 138 err: false, 139 }, 140 { 141 name: "utf8-code-point", 142 json: `"\u06fc"`, 143 expectedResult: `ۼ`, 144 err: false, 145 }, 146 { 147 name: "utf8-code-point-escaped", 148 json: `"\\u2070"`, 149 expectedResult: `\u2070`, 150 err: false, 151 }, 152 { 153 name: "utf8-code-point-err", 154 json: `"\u2Z70"`, 155 expectedResult: ``, 156 err: true, 157 }, 158 { 159 name: "utf16-surrogate", 160 json: `"\uD834\uDD1E"`, 161 expectedResult: `𝄞`, 162 err: false, 163 }, 164 { 165 name: "utf16-surrogate", 166 json: `"\uD834\\"`, 167 expectedResult: `�\`, 168 err: false, 169 }, 170 { 171 name: "utf16-surrogate", 172 json: `"\uD834\uD834"`, 173 expectedResult: "�\x00\x00\x00", 174 err: false, 175 }, 176 { 177 name: "utf16-surrogate", 178 json: `"\uD834"`, 179 expectedResult: `�`, 180 err: false, 181 }, 182 { 183 name: "utf16-surrogate-err", 184 json: `"\uD834\`, 185 expectedResult: ``, 186 err: true, 187 }, 188 { 189 name: "utf16-surrogate-err2", 190 json: `"\uD834\uDZ1E`, 191 expectedResult: ``, 192 err: true, 193 }, 194 { 195 name: "utf16-surrogate-err3", 196 json: `"\uD834`, 197 expectedResult: ``, 198 err: true, 199 }, 200 { 201 name: "utf16-surrogate-followed-by-control-char", 202 json: `"\uD834\t"`, 203 expectedResult: "�\t", 204 err: false, 205 }, 206 { 207 name: "utf16-surrogate-followed-by-control-char", 208 json: `"\uD834\n"`, 209 expectedResult: "�\n", 210 err: false, 211 }, 212 { 213 name: "utf16-surrogate-followed-by-control-char", 214 json: `"\uD834\f"`, 215 expectedResult: "�\f", 216 err: false, 217 }, 218 { 219 name: "utf16-surrogate-followed-by-control-char", 220 json: `"\uD834\b"`, 221 expectedResult: "�\b", 222 err: false, 223 }, 224 { 225 name: "utf16-surrogate-followed-by-control-char", 226 json: `"\uD834\r"`, 227 expectedResult: "�\r", 228 err: false, 229 }, 230 { 231 name: "utf16-surrogate-followed-by-control-char", 232 json: `"\uD834\h"`, 233 expectedResult: "", 234 err: true, 235 }, 236 { 237 name: "null", 238 json: `null`, 239 expectedResult: "", 240 }, 241 { 242 name: "null-err", 243 json: `nall`, 244 expectedResult: "", 245 err: true, 246 }, 247 { 248 name: "escape quote err", 249 json: `"test string \" escaped"`, 250 expectedResult: `test string " escaped`, 251 err: false, 252 }, 253 { 254 name: "escape quote err2", 255 json: `"test string \t escaped"`, 256 expectedResult: "test string \t escaped", 257 err: false, 258 }, 259 { 260 name: "escape quote err2", 261 json: `"test string \r escaped"`, 262 expectedResult: "test string \r escaped", 263 err: false, 264 }, 265 { 266 name: "escape quote err2", 267 json: `"test string \b escaped"`, 268 expectedResult: "test string \b escaped", 269 err: false, 270 }, 271 { 272 name: "escape quote err", 273 json: `"test string \n escaped"`, 274 expectedResult: "test string \n escaped", 275 err: false, 276 }, 277 { 278 name: "escape quote err", 279 json: `"test string \\\" escaped`, 280 expectedResult: ``, 281 err: true, 282 errType: InvalidJSONError(""), 283 }, 284 { 285 name: "escape quote err", 286 json: `"test string \\\l escaped"`, 287 expectedResult: ``, 288 err: true, 289 errType: InvalidJSONError(""), 290 }, 291 { 292 name: "invalid-json", 293 json: `invalid`, 294 expectedResult: ``, 295 err: true, 296 errType: InvalidJSONError(""), 297 }, 298 { 299 name: "string-complex", 300 json: ` "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char"`, 301 expectedResult: "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char", 302 }, 303 } 304 305 for _, testCase := range testCases { 306 t.Run(testCase.name, func(t *testing.T) { 307 str := "" 308 dec := NewDecoder(strings.NewReader(testCase.json)) 309 err := dec.Decode(&str) 310 if testCase.err { 311 assert.NotNil(t, err, "err should not be nil") 312 if testCase.errType != nil { 313 assert.IsType(t, testCase.errType, err, "err should of the given type") 314 } 315 } else { 316 assert.Nil(t, err, "err should be nil") 317 } 318 assert.Equal(t, testCase.expectedResult, str, fmt.Sprintf("'%s' should be equal to expectedResult", str)) 319 }) 320 } 321 } 322 func TestDecoderStringNull(t *testing.T) { 323 testCases := []struct { 324 name string 325 json string 326 expectedResult string 327 err bool 328 errType interface{} 329 resultIsNil bool 330 }{ 331 { 332 name: "basic-string", 333 json: `"string"`, 334 expectedResult: "string", 335 err: false, 336 }, 337 { 338 name: "string-solidus", 339 json: `"\/"`, 340 expectedResult: "/", 341 err: false, 342 }, 343 { 344 name: "basic-string", 345 json: ``, 346 expectedResult: "", 347 err: false, 348 resultIsNil: true, 349 }, 350 { 351 name: "basic-string", 352 json: `""`, 353 expectedResult: "", 354 err: false, 355 }, 356 { 357 name: "basic-string2", 358 json: `"hello world!"`, 359 expectedResult: "hello world!", 360 err: false, 361 }, 362 { 363 name: "escape-control-char", 364 json: `"\n"`, 365 expectedResult: "\n", 366 err: false, 367 }, 368 { 369 name: "escape-control-char", 370 json: `"\\n"`, 371 expectedResult: `\n`, 372 err: false, 373 }, 374 { 375 name: "escape-control-char", 376 json: `"\t"`, 377 expectedResult: "\t", 378 err: false, 379 }, 380 { 381 name: "escape-control-char", 382 json: `"\\t"`, 383 expectedResult: `\t`, 384 err: false, 385 }, 386 { 387 name: "escape-control-char", 388 json: `"\b"`, 389 expectedResult: "\b", 390 err: false, 391 }, 392 { 393 name: "escape-control-char", 394 json: `"\\b"`, 395 expectedResult: `\b`, 396 err: false, 397 }, 398 { 399 name: "escape-control-char", 400 json: `"\f"`, 401 expectedResult: "\f", 402 err: false, 403 }, 404 { 405 name: "escape-control-char", 406 json: `"\\f"`, 407 expectedResult: `\f`, 408 err: false, 409 }, 410 { 411 name: "escape-control-char", 412 json: `"\r"`, 413 expectedResult: "\r", 414 err: false, 415 }, 416 { 417 name: "escape-control-char", 418 json: `"\`, 419 expectedResult: "", 420 err: true, 421 }, 422 { 423 name: "escape-control-char-solidus", 424 json: `"\/"`, 425 expectedResult: "/", 426 err: false, 427 }, 428 { 429 name: "escape-control-char-solidus", 430 json: `"/"`, 431 expectedResult: "/", 432 err: false, 433 }, 434 { 435 name: "escape-control-char-solidus-escape-char", 436 json: `"\\/"`, 437 expectedResult: `\/`, 438 err: false, 439 }, 440 { 441 name: "escape-control-char", 442 json: `"\\r"`, 443 expectedResult: `\r`, 444 err: false, 445 }, 446 { 447 name: "utf8", 448 json: `"𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿"`, 449 expectedResult: "𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿", 450 err: false, 451 }, 452 { 453 name: "utf8-code-point", 454 json: `"\u06fc"`, 455 expectedResult: `ۼ`, 456 err: false, 457 }, 458 { 459 name: "utf8-code-point-escaped", 460 json: `"\\u2070"`, 461 expectedResult: `\u2070`, 462 err: false, 463 }, 464 { 465 name: "utf8-code-point-err", 466 json: `"\u2Z70"`, 467 expectedResult: ``, 468 err: true, 469 }, 470 { 471 name: "utf16-surrogate", 472 json: `"\uD834\uDD1E"`, 473 expectedResult: `𝄞`, 474 err: false, 475 }, 476 { 477 name: "utf16-surrogate", 478 json: `"\uD834\\"`, 479 expectedResult: `�\`, 480 err: false, 481 }, 482 { 483 name: "utf16-surrogate", 484 json: `"\uD834\uD834"`, 485 expectedResult: "�\x00\x00\x00", 486 err: false, 487 }, 488 { 489 name: "utf16-surrogate", 490 json: `"\uD834"`, 491 expectedResult: `�`, 492 err: false, 493 }, 494 { 495 name: "utf16-surrogate-err", 496 json: `"\uD834\`, 497 expectedResult: ``, 498 err: true, 499 }, 500 { 501 name: "utf16-surrogate-err2", 502 json: `"\uD834\uDZ1E`, 503 expectedResult: ``, 504 err: true, 505 }, 506 { 507 name: "utf16-surrogate-err3", 508 json: `"\uD834`, 509 expectedResult: ``, 510 err: true, 511 }, 512 { 513 name: "utf16-surrogate-followed-by-control-char", 514 json: `"\uD834\t"`, 515 expectedResult: "�\t", 516 err: false, 517 }, 518 { 519 name: "utf16-surrogate-followed-by-control-char", 520 json: `"\uD834\n"`, 521 expectedResult: "�\n", 522 err: false, 523 }, 524 { 525 name: "utf16-surrogate-followed-by-control-char", 526 json: `"\uD834\f"`, 527 expectedResult: "�\f", 528 err: false, 529 }, 530 { 531 name: "utf16-surrogate-followed-by-control-char", 532 json: `"\uD834\b"`, 533 expectedResult: "�\b", 534 err: false, 535 }, 536 { 537 name: "utf16-surrogate-followed-by-control-char", 538 json: `"\uD834\r"`, 539 expectedResult: "�\r", 540 err: false, 541 }, 542 { 543 name: "utf16-surrogate-followed-by-control-char", 544 json: `"\uD834\h"`, 545 expectedResult: "", 546 err: true, 547 }, 548 { 549 name: "null", 550 json: `null`, 551 expectedResult: "", 552 resultIsNil: true, 553 }, 554 { 555 name: "null-err", 556 json: `nall`, 557 expectedResult: "", 558 err: true, 559 }, 560 { 561 name: "escape quote err", 562 json: `"test string \" escaped"`, 563 expectedResult: `test string " escaped`, 564 err: false, 565 }, 566 { 567 name: "escape quote err2", 568 json: `"test string \t escaped"`, 569 expectedResult: "test string \t escaped", 570 err: false, 571 }, 572 { 573 name: "escape quote err2", 574 json: `"test string \r escaped"`, 575 expectedResult: "test string \r escaped", 576 err: false, 577 }, 578 { 579 name: "escape quote err2", 580 json: `"test string \b escaped"`, 581 expectedResult: "test string \b escaped", 582 err: false, 583 }, 584 { 585 name: "escape quote err", 586 json: `"test string \n escaped"`, 587 expectedResult: "test string \n escaped", 588 err: false, 589 }, 590 { 591 name: "escape quote err", 592 json: `"test string \\\" escaped`, 593 expectedResult: ``, 594 err: true, 595 errType: InvalidJSONError(""), 596 }, 597 { 598 name: "escape quote err", 599 json: `"test string \\\l escaped"`, 600 expectedResult: ``, 601 err: true, 602 errType: InvalidJSONError(""), 603 }, 604 { 605 name: "invalid-json", 606 json: `invalid`, 607 expectedResult: ``, 608 err: true, 609 errType: InvalidJSONError(""), 610 }, 611 { 612 name: "string-complex", 613 json: ` "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char"`, 614 expectedResult: "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char", 615 }, 616 } 617 618 for _, testCase := range testCases { 619 t.Run(testCase.name, func(t *testing.T) { 620 str := (*string)(nil) 621 err := Unmarshal([]byte(testCase.json), &str) 622 if testCase.err { 623 assert.NotNil(t, err, "err should not be nil") 624 if testCase.errType != nil { 625 assert.IsType(t, testCase.errType, err, "err should of the given type") 626 } 627 return 628 } 629 assert.Nil(t, err, "Err must be nil") 630 if testCase.resultIsNil { 631 assert.Nil(t, str) 632 } else { 633 assert.Equal(t, testCase.expectedResult, *str, fmt.Sprintf("v must be equal to %s", testCase.expectedResult)) 634 } 635 }) 636 } 637 t.Run("decoder-api-invalid-json2", func(t *testing.T) { 638 var v = new(string) 639 var dec = NewDecoder(strings.NewReader(`a`)) 640 err := dec.StringNull(&v) 641 assert.NotNil(t, err, "Err must not be nil") 642 assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") 643 }) 644 } 645 func TestDecoderStringInvalidType(t *testing.T) { 646 json := []byte(`1`) 647 var v string 648 err := Unmarshal(json, &v) 649 assert.NotNil(t, err, "Err must not be nil as JSON is invalid") 650 assert.IsType(t, InvalidUnmarshalError(""), err, "err message must be 'Invalid JSON'") 651 } 652 653 func TestDecoderStringDecoderAPI(t *testing.T) { 654 var v string 655 dec := NewDecoder(strings.NewReader(`"hello world!"`)) 656 defer dec.Release() 657 err := dec.DecodeString(&v) 658 assert.Nil(t, err, "Err must be nil") 659 assert.Equal(t, "hello world!", v, "v must be equal to 'hello world!'") 660 } 661 662 func TestDecoderStringPoolError(t *testing.T) { 663 // reset the pool to make sure it's not full 664 decPool = sync.Pool{ 665 New: func() interface{} { 666 return NewDecoder(nil) 667 }, 668 } 669 result := "" 670 dec := NewDecoder(nil) 671 dec.Release() 672 defer func() { 673 err := recover() 674 assert.NotNil(t, err, "err shouldnt be nil") 675 assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") 676 }() 677 _ = dec.DecodeString(&result) 678 assert.True(t, false, "should not be called as decoder should have panicked") 679 } 680 681 func TestDecoderSkipEscapedStringError(t *testing.T) { 682 dec := NewDecoder(strings.NewReader(``)) 683 defer dec.Release() 684 err := dec.skipEscapedString() 685 assert.NotNil(t, err, "Err must be nil") 686 assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") 687 } 688 689 func TestDecoderSkipEscapedStringError2(t *testing.T) { 690 dec := NewDecoder(strings.NewReader(`\"`)) 691 defer dec.Release() 692 err := dec.skipEscapedString() 693 assert.NotNil(t, err, "Err must be nil") 694 assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") 695 } 696 697 func TestDecoderSkipEscapedStringError3(t *testing.T) { 698 dec := NewDecoder(strings.NewReader(`invalid`)) 699 defer dec.Release() 700 err := dec.skipEscapedString() 701 assert.NotNil(t, err, "Err must be nil") 702 assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") 703 } 704 705 func TestDecoderSkipEscapedStringError4(t *testing.T) { 706 dec := NewDecoder(strings.NewReader(`\u12`)) 707 defer dec.Release() 708 err := dec.skipEscapedString() 709 assert.NotNil(t, err, "Err must be nil") 710 assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") 711 } 712 713 func TestDecoderSkipStringError(t *testing.T) { 714 dec := NewDecoder(strings.NewReader(`invalid`)) 715 defer dec.Release() 716 err := dec.skipString() 717 assert.NotNil(t, err, "Err must be nil") 718 assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") 719 } 720 721 func TestSkipString(t *testing.T) { 722 testCases := []struct { 723 name string 724 json string 725 expectedResult string 726 err bool 727 errType interface{} 728 }{ 729 { 730 name: "escape quote err", 731 json: `test string \\" escaped"`, 732 expectedResult: ``, 733 err: true, 734 errType: InvalidJSONError(""), 735 }, 736 { 737 name: "escape quote err", 738 json: `test string \\\l escaped"`, 739 expectedResult: ``, 740 err: true, 741 errType: InvalidJSONError(""), 742 }, 743 { 744 name: "string-solidus", 745 json: `Asia\/Bangkok","enable":true}"`, 746 expectedResult: "", 747 err: false, 748 }, 749 { 750 name: "string-unicode", 751 json: `[2]\u66fe\u5b97\u5357"`, 752 expectedResult: "", 753 err: false, 754 }, 755 } 756 757 for _, testCase := range testCases { 758 dec := NewDecoder(strings.NewReader(testCase.json)) 759 err := dec.skipString() 760 if testCase.err { 761 assert.NotNil(t, err, "err should not be nil") 762 if testCase.errType != nil { 763 assert.IsType(t, testCase.errType, err, "err should be of expected type") 764 } 765 return 766 } 767 assert.Nil(t, err, "err should be nil") 768 } 769 }