github.com/whiteCcinn/protobuf-go@v1.0.9/internal/encoding/text/decode_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package text_test 6 7 import ( 8 "fmt" 9 "math" 10 "strings" 11 "testing" 12 "unicode/utf8" 13 14 "github.com/google/go-cmp/cmp" 15 16 "github.com/whiteCcinn/protobuf-go/internal/encoding/text" 17 "github.com/whiteCcinn/protobuf-go/internal/flags" 18 ) 19 20 var eofErr = text.ErrUnexpectedEOF.Error() 21 22 type R struct { 23 // K is expected Kind of the returned Token object from calling Decoder.Read. 24 K text.Kind 25 // E is expected error substring from calling Decoder.Read if set. 26 E string 27 // T contains NT (if K is Name) or ST (if K is Scalar) or nil (others) 28 T interface{} 29 // P is expected Token.Pos if set > 0. 30 P int 31 // RS is expected result from Token.RawString() if not empty. 32 RS string 33 } 34 35 // NT contains data for checking against a name token. 36 type NT struct { 37 K text.NameKind 38 // Sep is true if name token should have separator character, else false. 39 Sep bool 40 // If K is IdentName or TypeName, invoke corresponding getter and compare against this field. 41 S string 42 // If K is FieldNumber, invoke getter and compare against this field. 43 N int32 44 } 45 46 // ST contains data for checking against a scalar token. 47 type ST struct { 48 // checker that is expected to return OK. 49 ok checker 50 // checker that is expected to return not OK. 51 nok checker 52 } 53 54 // checker provides API for the token wrapper API call types Str, Enum, Bool, 55 // Uint64, Uint32, Int64, Int32, Float64, Float32. 56 type checker interface { 57 // checkOk checks and expects for token API call to return ok and compare 58 // against implementation-stored value. Returns empty string if success, 59 // else returns error message describing the error. 60 checkOk(text.Token) string 61 // checkNok checks and expects for token API call to return not ok. Returns 62 // empty string if success, else returns error message describing the error. 63 checkNok(text.Token) string 64 } 65 66 type Str struct { 67 val string 68 } 69 70 func (s Str) checkOk(tok text.Token) string { 71 got, ok := tok.String() 72 if !ok { 73 return fmt.Sprintf("Token.String() returned not OK for token: %v", tok.RawString()) 74 } 75 if got != s.val { 76 return fmt.Sprintf("Token.String() got %q want %q for token: %v", got, s.val, tok.RawString()) 77 } 78 return "" 79 } 80 81 func (s Str) checkNok(tok text.Token) string { 82 if _, ok := tok.String(); ok { 83 return fmt.Sprintf("Token.String() returned OK for token: %v", tok.RawString()) 84 } 85 return "" 86 } 87 88 type Enum struct { 89 val string 90 } 91 92 func (e Enum) checkOk(tok text.Token) string { 93 got, ok := tok.Enum() 94 if !ok { 95 return fmt.Sprintf("Token.Enum() returned not OK for token: %v", tok.RawString()) 96 } 97 if got != e.val { 98 return fmt.Sprintf("Token.Enum() got %q want %q for token: %v", got, e.val, tok.RawString()) 99 } 100 return "" 101 } 102 103 func (e Enum) checkNok(tok text.Token) string { 104 if _, ok := tok.Enum(); ok { 105 return fmt.Sprintf("Token.Enum() returned OK for token: %v", tok.RawString()) 106 } 107 return "" 108 } 109 110 type Bool struct { 111 val bool 112 } 113 114 func (b Bool) checkOk(tok text.Token) string { 115 got, ok := tok.Bool() 116 if !ok { 117 return fmt.Sprintf("Token.Bool() returned not OK for token: %v", tok.RawString()) 118 } 119 if got != b.val { 120 return fmt.Sprintf("Token.Bool() got %v want %v for token: %v", got, b.val, tok.RawString()) 121 } 122 return "" 123 } 124 125 func (b Bool) checkNok(tok text.Token) string { 126 if _, ok := tok.Bool(); ok { 127 return fmt.Sprintf("Token.Bool() returned OK for token: %v", tok.RawString()) 128 } 129 return "" 130 } 131 132 type Uint64 struct { 133 val uint64 134 } 135 136 func (n Uint64) checkOk(tok text.Token) string { 137 got, ok := tok.Uint64() 138 if !ok { 139 return fmt.Sprintf("Token.Uint64() returned not OK for token: %v", tok.RawString()) 140 } 141 if got != n.val { 142 return fmt.Sprintf("Token.Uint64() got %v want %v for token: %v", got, n.val, tok.RawString()) 143 } 144 return "" 145 } 146 147 func (n Uint64) checkNok(tok text.Token) string { 148 if _, ok := tok.Uint64(); ok { 149 return fmt.Sprintf("Token.Uint64() returned OK for token: %v", tok.RawString()) 150 } 151 return "" 152 } 153 154 type Uint32 struct { 155 val uint32 156 } 157 158 func (n Uint32) checkOk(tok text.Token) string { 159 got, ok := tok.Uint32() 160 if !ok { 161 return fmt.Sprintf("Token.Uint32() returned not OK for token: %v", tok.RawString()) 162 } 163 if got != n.val { 164 return fmt.Sprintf("Token.Uint32() got %v want %v for token: %v", got, n.val, tok.RawString()) 165 } 166 return "" 167 } 168 169 func (n Uint32) checkNok(tok text.Token) string { 170 if _, ok := tok.Uint32(); ok { 171 return fmt.Sprintf("Token.Uint32() returned OK for token: %v", tok.RawString()) 172 } 173 return "" 174 } 175 176 type Int64 struct { 177 val int64 178 } 179 180 func (n Int64) checkOk(tok text.Token) string { 181 got, ok := tok.Int64() 182 if !ok { 183 return fmt.Sprintf("Token.Int64() returned not OK for token: %v", tok.RawString()) 184 } 185 if got != n.val { 186 return fmt.Sprintf("Token.Int64() got %v want %v for token: %v", got, n.val, tok.RawString()) 187 } 188 return "" 189 } 190 191 func (n Int64) checkNok(tok text.Token) string { 192 if _, ok := tok.Int64(); ok { 193 return fmt.Sprintf("Token.Int64() returned OK for token: %v", tok.RawString()) 194 } 195 return "" 196 } 197 198 type Int32 struct { 199 val int32 200 } 201 202 func (n Int32) checkOk(tok text.Token) string { 203 got, ok := tok.Int32() 204 if !ok { 205 return fmt.Sprintf("Token.Int32() returned not OK for token: %v", tok.RawString()) 206 } 207 if got != n.val { 208 return fmt.Sprintf("Token.Int32() got %v want %v for token: %v", got, n.val, tok.RawString()) 209 } 210 return "" 211 } 212 213 func (n Int32) checkNok(tok text.Token) string { 214 if _, ok := tok.Int32(); ok { 215 return fmt.Sprintf("Token.Int32() returned OK for token: %v", tok.RawString()) 216 } 217 return "" 218 } 219 220 type Float64 struct { 221 val float64 222 } 223 224 func (n Float64) checkOk(tok text.Token) string { 225 got, ok := tok.Float64() 226 if !ok { 227 return fmt.Sprintf("Token.Float64() returned not OK for token: %v", tok.RawString()) 228 } 229 if math.Float64bits(got) != math.Float64bits(n.val) { 230 return fmt.Sprintf("Token.Float64() got %v want %v for token: %v", got, n.val, tok.RawString()) 231 } 232 return "" 233 } 234 235 func (n Float64) checkNok(tok text.Token) string { 236 if _, ok := tok.Float64(); ok { 237 return fmt.Sprintf("Token.Float64() returned OK for token: %v", tok.RawString()) 238 } 239 return "" 240 } 241 242 type Float32 struct { 243 val float32 244 } 245 246 func (n Float32) checkOk(tok text.Token) string { 247 got, ok := tok.Float32() 248 if !ok { 249 return fmt.Sprintf("Token.Float32() returned not OK for token: %v", tok.RawString()) 250 } 251 if math.Float32bits(got) != math.Float32bits(n.val) { 252 return fmt.Sprintf("Token.Float32() got %v want %v for token: %v", got, n.val, tok.RawString()) 253 } 254 return "" 255 } 256 257 func (n Float32) checkNok(tok text.Token) string { 258 if _, ok := tok.Float32(); ok { 259 return fmt.Sprintf("Token.Float32() returned OK for token: %v", tok.RawString()) 260 } 261 return "" 262 } 263 264 func TestDecoder(t *testing.T) { 265 const space = " \n\r\t" 266 tests := []struct { 267 in string 268 // want is a list of expected Tokens returned from calling Decoder.Read. 269 // An item makes the test code invoke Decoder.Read and compare against 270 // R.K and R.E. If R.K is Name, it compares 271 want []R 272 }{ 273 { 274 in: "", 275 want: []R{{K: text.EOF}}, 276 }, 277 { 278 in: "# comment", 279 want: []R{{K: text.EOF}}, 280 }, 281 { 282 in: space + "# comment" + space, 283 want: []R{{K: text.EOF}}, 284 }, 285 { 286 in: space, 287 want: []R{{K: text.EOF, P: len(space)}}, 288 }, 289 { 290 // Calling Read after EOF will keep returning EOF for 291 // succeeding Read calls. 292 in: space, 293 want: []R{ 294 {K: text.EOF}, 295 {K: text.EOF}, 296 {K: text.EOF}, 297 }, 298 }, 299 { 300 // NUL is an invalid whitespace since C++ uses C-strings. 301 in: "\x00", 302 want: []R{{E: "invalid field name: \x00"}}, 303 }, 304 305 // Field names. 306 { 307 in: "name", 308 want: []R{ 309 {K: text.Name, T: NT{K: text.IdentName, S: "name"}, RS: "name"}, 310 {E: eofErr}, 311 }, 312 }, 313 { 314 in: space + "name:" + space, 315 want: []R{ 316 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 317 {E: eofErr}, 318 }, 319 }, 320 { 321 in: space + "name" + space + ":" + space, 322 want: []R{ 323 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 324 {E: eofErr}, 325 }, 326 }, 327 { 328 in: "name # comment", 329 want: []R{ 330 {K: text.Name, T: NT{K: text.IdentName, S: "name"}}, 331 {E: eofErr}, 332 }, 333 }, 334 { 335 // Comments only extend until the newline. 336 in: "# comment \nname", 337 want: []R{ 338 {K: text.Name, T: NT{K: text.IdentName, S: "name"}, P: 11}, 339 }, 340 }, 341 { 342 in: "name # comment \n:", 343 want: []R{ 344 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 345 }, 346 }, 347 { 348 in: "name123", 349 want: []R{ 350 {K: text.Name, T: NT{K: text.IdentName, S: "name123"}}, 351 }, 352 }, 353 { 354 in: "name_123", 355 want: []R{ 356 {K: text.Name, T: NT{K: text.IdentName, S: "name_123"}}, 357 }, 358 }, 359 { 360 in: "_123", 361 want: []R{ 362 {K: text.Name, T: NT{K: text.IdentName, S: "_123"}}, 363 }, 364 }, 365 { 366 in: ":", 367 want: []R{{E: "syntax error (line 1:1): invalid field name: :"}}, 368 }, 369 { 370 in: "\n\n\n {", 371 want: []R{{E: "syntax error (line 4:2): invalid field name: {"}}, 372 }, 373 { 374 in: "123name", 375 want: []R{{E: "invalid field name: 123name"}}, 376 }, 377 { 378 in: `/`, 379 want: []R{{E: `invalid field name: /`}}, 380 }, 381 { 382 in: `δΈη`, 383 want: []R{{E: `invalid field name: δΈ`}}, 384 }, 385 { 386 in: `1a/b`, 387 want: []R{{E: `invalid field name: 1a`}}, 388 }, 389 { 390 in: `1c\d`, 391 want: []R{{E: `invalid field name: 1c`}}, 392 }, 393 { 394 in: "\x84f", 395 want: []R{{E: "invalid field name: \x84"}}, 396 }, 397 { 398 in: "\uFFFDxxx", 399 want: []R{{E: "invalid field name: \uFFFD"}}, 400 }, 401 { 402 in: "-a234567890123456789012345678901234567890abc", 403 want: []R{{E: "invalid field name: -a2345678901234567890123456789012β¦"}}, 404 }, 405 { 406 in: "[type]", 407 want: []R{ 408 {K: text.Name, T: NT{K: text.TypeName, S: "type"}, RS: "[type]"}, 409 }, 410 }, 411 { 412 // V1 allows this syntax. C++ does not, however, C++ also fails if 413 // field is Any and does not contain '/'. 414 in: "[/type]", 415 want: []R{ 416 {K: text.Name, T: NT{K: text.TypeName, S: "/type"}}, 417 }, 418 }, 419 { 420 in: "[.type]", 421 want: []R{{E: "invalid type URL/extension field name: [.type]"}}, 422 }, 423 { 424 in: "[pkg.Foo.extension_field]", 425 want: []R{ 426 {K: text.Name, T: NT{K: text.TypeName, S: "pkg.Foo.extension_field"}}, 427 }, 428 }, 429 { 430 in: "[domain.com/type]", 431 want: []R{ 432 {K: text.Name, T: NT{K: text.TypeName, S: "domain.com/type"}}, 433 }, 434 }, 435 { 436 in: "[domain.com/pkg.type]", 437 want: []R{ 438 {K: text.Name, T: NT{K: text.TypeName, S: "domain.com/pkg.type"}}, 439 }, 440 }, 441 { 442 in: "[sub.domain.com\x2fpath\x2fto\x2fproto.package.name]", 443 want: []R{ 444 { 445 K: text.Name, 446 T: NT{ 447 K: text.TypeName, 448 S: "sub.domain.com/path/to/proto.package.name", 449 }, 450 RS: "[sub.domain.com\x2fpath\x2fto\x2fproto.package.name]", 451 }, 452 }, 453 }, 454 { 455 // V2 no longer allows a quoted string for the Any type URL. 456 in: `["domain.com/pkg.type"]`, 457 want: []R{{E: `invalid type URL/extension field name: ["`}}, 458 }, 459 { 460 // V2 no longer allows a quoted string for the Any type URL. 461 in: `['domain.com/pkg.type']`, 462 want: []R{{E: `invalid type URL/extension field name: ['`}}, 463 }, 464 { 465 in: "[pkg.Foo.extension_field:", 466 want: []R{{E: "invalid type URL/extension field name: [pkg.Foo.extension_field:"}}, 467 }, 468 { 469 // V2 no longer allows whitespace within identifier "word". 470 in: "[proto.packa ge.field]", 471 want: []R{{E: "invalid type URL/extension field name: [proto.packa g"}}, 472 }, 473 { 474 // V2 no longer allows comments within identifier "word". 475 in: "[proto.packa # comment\n ge.field]", 476 want: []R{{E: "invalid type URL/extension field name: [proto.packa # comment\n g"}}, 477 }, 478 { 479 in: "[proto.package.]", 480 want: []R{{E: "invalid type URL/extension field name: [proto.package."}}, 481 }, 482 { 483 in: "[proto.package/]", 484 want: []R{{E: "invalid type URL/extension field name: [proto.package/"}}, 485 }, 486 { 487 in: `message_field{[bad@]`, 488 want: []R{ 489 {K: text.Name}, 490 {K: text.MessageOpen}, 491 {E: `invalid type URL/extension field name: [bad@`}, 492 }, 493 }, 494 { 495 in: `message_field{[invalid//type]`, 496 want: []R{ 497 {K: text.Name}, 498 {K: text.MessageOpen}, 499 {E: `invalid type URL/extension field name: [invalid//`}, 500 }, 501 }, 502 { 503 in: `message_field{[proto.package.]`, 504 want: []R{ 505 {K: text.Name}, 506 {K: text.MessageOpen}, 507 {E: `invalid type URL/extension field name: [proto.package.`}, 508 }, 509 }, 510 { 511 in: "[proto.package", 512 want: []R{{E: eofErr}}, 513 }, 514 { 515 in: "[" + space + "type" + space + "]" + space + ":", 516 want: []R{ 517 { 518 K: text.Name, 519 T: NT{ 520 K: text.TypeName, 521 Sep: true, 522 S: "type", 523 }, 524 RS: "[" + space + "type" + space + "]", 525 }, 526 }, 527 }, 528 { 529 // Whitespaces/comments are only allowed betweeb 530 in: "[" + space + "domain" + space + "." + space + "com # comment\n" + 531 "/" + "pkg" + space + "." + space + "type" + space + "]", 532 want: []R{ 533 {K: text.Name, T: NT{K: text.TypeName, S: "domain.com/pkg.type"}}, 534 }, 535 }, 536 { 537 in: "42", 538 want: []R{ 539 {K: text.Name, T: NT{K: text.FieldNumber, N: 42}}, 540 }, 541 }, 542 { 543 in: "0x42:", 544 want: []R{{E: "invalid field number: 0x42"}}, 545 }, 546 { 547 in: "042:", 548 want: []R{{E: "invalid field number: 042"}}, 549 }, 550 { 551 in: "123.456:", 552 want: []R{{E: "invalid field number: 123.456"}}, 553 }, 554 { 555 in: "-123", 556 want: []R{{E: "invalid field number: -123"}}, 557 }, 558 { 559 // Field number > math.MaxInt32. 560 in: "2147483648:", 561 want: []R{{E: "invalid field number: 2147483648"}}, 562 }, 563 564 // String field value. More string parsing specific testing in 565 // TestUnmarshalString. 566 { 567 in: `name: "hello world"`, 568 want: []R{ 569 {K: text.Name}, 570 { 571 K: text.Scalar, 572 T: ST{ok: Str{"hello world"}, nok: Enum{}}, 573 RS: `"hello world"`, 574 }, 575 {K: text.EOF}, 576 }, 577 }, 578 { 579 in: `name: 'hello'`, 580 want: []R{ 581 {K: text.Name}, 582 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 583 }, 584 }, 585 { 586 in: `name: "hello'`, 587 want: []R{ 588 {K: text.Name}, 589 {E: eofErr}, 590 }, 591 }, 592 { 593 in: `name: 'hello`, 594 want: []R{ 595 {K: text.Name}, 596 {E: eofErr}, 597 }, 598 }, 599 { 600 // Field name without separator is ok. prototext package will need 601 // to determine that this is not valid for scalar values. 602 in: space + `name` + space + `"hello"` + space, 603 want: []R{ 604 {K: text.Name}, 605 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 606 }, 607 }, 608 { 609 in: `name'hello'`, 610 want: []R{ 611 {K: text.Name}, 612 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 613 }, 614 }, 615 { 616 in: `name: ` + space + `"hello"` + space + `,`, 617 want: []R{ 618 {K: text.Name}, 619 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 620 {K: text.EOF}, 621 }, 622 }, 623 { 624 in: `name` + space + `:` + `"hello"` + space + `;` + space, 625 want: []R{ 626 {K: text.Name}, 627 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 628 {K: text.EOF}, 629 }, 630 }, 631 { 632 in: `name:"hello" , ,`, 633 want: []R{ 634 {K: text.Name}, 635 {K: text.Scalar}, 636 {E: "(line 1:16): invalid field name: ,"}, 637 }, 638 }, 639 { 640 in: `name:"hello" , ;`, 641 want: []R{ 642 {K: text.Name}, 643 {K: text.Scalar}, 644 {E: "(line 1:16): invalid field name: ;"}, 645 }, 646 }, 647 { 648 in: `name:"hello" name:'world'`, 649 want: []R{ 650 {K: text.Name}, 651 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 652 {K: text.Name}, 653 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 654 {K: text.EOF}, 655 }, 656 }, 657 { 658 in: `name:"hello", name:"world"`, 659 want: []R{ 660 {K: text.Name}, 661 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 662 {K: text.Name}, 663 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 664 {K: text.EOF}, 665 }, 666 }, 667 { 668 in: `name:"hello"; name:"world",`, 669 want: []R{ 670 {K: text.Name}, 671 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 672 {K: text.Name}, 673 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 674 {K: text.EOF}, 675 }, 676 }, 677 { 678 in: `foo:"hello"bar:"world"`, 679 want: []R{ 680 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "foo"}}, 681 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 682 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "bar"}}, 683 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 684 {K: text.EOF}, 685 }, 686 }, 687 { 688 in: `foo:"hello"[bar]:"world"`, 689 want: []R{ 690 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "foo"}}, 691 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 692 {K: text.Name, T: NT{K: text.TypeName, Sep: true, S: "bar"}}, 693 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 694 {K: text.EOF}, 695 }, 696 }, 697 { 698 in: `name:"foo"` + space + `"bar"` + space + `'qux'`, 699 want: []R{ 700 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 701 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 702 {K: text.EOF}, 703 }, 704 }, 705 { 706 in: `name:"foo"'bar'"qux"`, 707 want: []R{ 708 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 709 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 710 {K: text.EOF}, 711 }, 712 }, 713 { 714 in: `name:"foo"` + space + `"bar" # comment` + "\n'qux' # comment", 715 want: []R{ 716 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 717 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 718 {K: text.EOF}, 719 }, 720 }, 721 722 // Lists. 723 { 724 in: `name: [`, 725 want: []R{ 726 {K: text.Name}, 727 {K: text.ListOpen}, 728 {E: eofErr}, 729 }, 730 }, 731 { 732 in: `name: []`, 733 want: []R{ 734 {K: text.Name}, 735 {K: text.ListOpen}, 736 {K: text.ListClose}, 737 {K: text.EOF}, 738 }, 739 }, 740 { 741 in: `name []`, 742 want: []R{ 743 {K: text.Name}, 744 {K: text.ListOpen}, 745 {K: text.ListClose}, 746 {K: text.EOF}, 747 }, 748 }, 749 { 750 in: `name: [,`, 751 want: []R{ 752 {K: text.Name}, 753 {K: text.ListOpen}, 754 {E: `(line 1:8): invalid scalar value: ,`}, 755 }, 756 }, 757 { 758 in: `name: [0`, 759 want: []R{ 760 {K: text.Name}, 761 {K: text.ListOpen}, 762 {K: text.Scalar}, 763 {E: eofErr}, 764 }, 765 }, 766 { 767 in: `name: [` + space + `"hello"` + space + `]` + space, 768 want: []R{ 769 {K: text.Name}, 770 {K: text.ListOpen}, 771 {K: text.Scalar, T: ST{ok: Str{"hello"}}, P: len(space) + 7}, 772 {K: text.ListClose}, 773 {K: text.EOF}, 774 }, 775 }, 776 { 777 in: `name: ["hello",]`, 778 want: []R{ 779 {K: text.Name}, 780 {K: text.ListOpen}, 781 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 782 {E: `invalid scalar value: ]`}, 783 }, 784 }, 785 { 786 in: `name: ["foo"` + space + `'bar' "qux"]`, 787 want: []R{ 788 {K: text.Name}, 789 {K: text.ListOpen}, 790 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 791 {K: text.ListClose}, 792 {K: text.EOF}, 793 }, 794 }, 795 { 796 in: `name:` + space + `["foo",` + space + "'bar', # comment\n\n" + `"qux"]`, 797 want: []R{ 798 {K: text.Name}, 799 {K: text.ListOpen}, 800 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 801 {K: text.Scalar, T: ST{ok: Str{"bar"}}}, 802 {K: text.Scalar, T: ST{ok: Str{"qux"}}}, 803 {K: text.ListClose}, 804 {K: text.EOF}, 805 }, 806 }, 807 808 { 809 // List within list is not allowed. 810 in: `name: [[]]`, 811 want: []R{ 812 {K: text.Name}, 813 {K: text.ListOpen}, 814 {E: `syntax error (line 1:8): invalid scalar value: [`}, 815 }, 816 }, 817 { 818 // List items need to be separated by ,. 819 in: `name: ["foo" true]`, 820 want: []R{ 821 {K: text.Name}, 822 {K: text.ListOpen}, 823 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 824 {E: `syntax error (line 1:14): unexpected character 't'`}, 825 }, 826 }, 827 { 828 in: `name: ["foo"; "bar"]`, 829 want: []R{ 830 {K: text.Name}, 831 {K: text.ListOpen}, 832 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 833 {E: `syntax error (line 1:13): unexpected character ';'`}, 834 }, 835 }, 836 { 837 in: `name: ["foo", true, ENUM, 1.0]`, 838 want: []R{ 839 {K: text.Name}, 840 {K: text.ListOpen}, 841 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 842 {K: text.Scalar, T: ST{ok: Enum{"true"}}}, 843 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 844 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 845 {K: text.ListClose}, 846 }, 847 }, 848 849 // Boolean literal values. 850 { 851 in: `name: True`, 852 want: []R{ 853 {K: text.Name}, 854 { 855 K: text.Scalar, 856 T: ST{ok: Bool{true}}, 857 }, 858 {K: text.EOF}, 859 }, 860 }, 861 { 862 in: `name false`, 863 want: []R{ 864 {K: text.Name}, 865 { 866 K: text.Scalar, 867 T: ST{ok: Bool{false}}, 868 }, 869 {K: text.EOF}, 870 }, 871 }, 872 { 873 in: `name: [t, f, True, False, true, false, 1, 0, 0x01, 0x00, 01, 00]`, 874 want: []R{ 875 {K: text.Name}, 876 {K: text.ListOpen}, 877 {K: text.Scalar, T: ST{ok: Bool{true}}}, 878 {K: text.Scalar, T: ST{ok: Bool{false}}}, 879 {K: text.Scalar, T: ST{ok: Bool{true}}}, 880 {K: text.Scalar, T: ST{ok: Bool{false}}}, 881 {K: text.Scalar, T: ST{ok: Bool{true}}}, 882 {K: text.Scalar, T: ST{ok: Bool{false}}}, 883 {K: text.Scalar, T: ST{ok: Bool{true}}}, 884 {K: text.Scalar, T: ST{ok: Bool{false}}}, 885 {K: text.Scalar, T: ST{ok: Bool{true}}}, 886 {K: text.Scalar, T: ST{ok: Bool{false}}}, 887 {K: text.Scalar, T: ST{ok: Bool{true}}}, 888 {K: text.Scalar, T: ST{ok: Bool{false}}}, 889 {K: text.ListClose}, 890 }, 891 }, 892 { 893 // Looks like boolean but not. 894 in: `name: [tRUe, falSE, -1, -0, -0x01, -0x00, -01, -00, 0.0]`, 895 want: []R{ 896 {K: text.Name}, 897 {K: text.ListOpen}, 898 {K: text.Scalar, T: ST{nok: Bool{}}}, 899 {K: text.Scalar, T: ST{nok: Bool{}}}, 900 {K: text.Scalar, T: ST{nok: Bool{}}}, 901 {K: text.Scalar, T: ST{nok: Bool{}}}, 902 {K: text.Scalar, T: ST{nok: Bool{}}}, 903 {K: text.Scalar, T: ST{nok: Bool{}}}, 904 {K: text.Scalar, T: ST{nok: Bool{}}}, 905 {K: text.Scalar, T: ST{nok: Bool{}}}, 906 {K: text.Scalar, T: ST{nok: Bool{}}}, 907 {K: text.ListClose}, 908 }, 909 }, 910 { 911 in: `foo: true[bar] false`, 912 want: []R{ 913 {K: text.Name}, 914 {K: text.Scalar, T: ST{ok: Bool{true}}}, 915 {K: text.Name}, 916 {K: text.Scalar, T: ST{ok: Bool{false}}}, 917 }, 918 }, 919 920 // Enum field values. 921 { 922 in: space + `name: ENUM`, 923 want: []R{ 924 {K: text.Name}, 925 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 926 }, 927 }, 928 { 929 in: space + `name:[TRUE, FALSE, T, F, t, f]`, 930 want: []R{ 931 {K: text.Name}, 932 {K: text.ListOpen}, 933 {K: text.Scalar, T: ST{ok: Enum{"TRUE"}}}, 934 {K: text.Scalar, T: ST{ok: Enum{"FALSE"}}}, 935 {K: text.Scalar, T: ST{ok: Enum{"T"}}}, 936 {K: text.Scalar, T: ST{ok: Enum{"F"}}}, 937 {K: text.Scalar, T: ST{ok: Enum{"t"}}}, 938 {K: text.Scalar, T: ST{ok: Enum{"f"}}}, 939 {K: text.ListClose}, 940 }, 941 }, 942 { 943 in: `foo: Enum1[bar]:Enum2`, 944 want: []R{ 945 {K: text.Name}, 946 {K: text.Scalar, T: ST{ok: Enum{"Enum1"}}}, 947 {K: text.Name}, 948 {K: text.Scalar, T: ST{ok: Enum{"Enum2"}}}, 949 }, 950 }, 951 { 952 // Invalid enum values. 953 in: `name: [-inf, -foo, "string", 42, 1.0, 0x47]`, 954 want: []R{ 955 {K: text.Name}, 956 {K: text.ListOpen}, 957 {K: text.Scalar, T: ST{nok: Enum{}}}, 958 {K: text.Scalar, T: ST{nok: Enum{}}}, 959 {K: text.Scalar, T: ST{nok: Enum{}}}, 960 {K: text.Scalar, T: ST{nok: Enum{}}}, 961 {K: text.Scalar, T: ST{nok: Enum{}}}, 962 {K: text.Scalar, T: ST{nok: Enum{}}}, 963 {K: text.ListClose}, 964 }, 965 }, 966 { 967 in: `name: true.`, 968 want: []R{ 969 {K: text.Name}, 970 {E: `invalid scalar value: true.`}, 971 }, 972 }, 973 974 // Numeric values. 975 { 976 in: `nums:42 nums:0x2A nums:052`, 977 want: []R{ 978 {K: text.Name}, 979 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 980 {K: text.Name}, 981 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 982 {K: text.Name}, 983 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 984 }, 985 }, 986 { 987 in: `nums:[-42, -0x2a, -052]`, 988 want: []R{ 989 {K: text.Name}, 990 {K: text.ListOpen}, 991 {K: text.Scalar, T: ST{nok: Uint64{}}}, 992 {K: text.Scalar, T: ST{nok: Uint64{}}}, 993 {K: text.Scalar, T: ST{nok: Uint64{}}}, 994 {K: text.ListClose}, 995 }, 996 }, 997 { 998 in: `nums:[-42, -0x2a, -052]`, 999 want: []R{ 1000 {K: text.Name}, 1001 {K: text.ListOpen}, 1002 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1003 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1004 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1005 {K: text.ListClose}, 1006 }, 1007 }, 1008 { 1009 in: `nums: [0,0x0,00,-9876543210,9876543210,0x0123456789abcdef,-0x0123456789abcdef,01234567,-01234567]`, 1010 want: []R{ 1011 {K: text.Name}, 1012 {K: text.ListOpen}, 1013 {K: text.Scalar, T: ST{ok: Uint64{0}}}, 1014 {K: text.Scalar, T: ST{ok: Int64{0}}}, 1015 {K: text.Scalar, T: ST{ok: Uint64{0}}}, 1016 {K: text.Scalar, T: ST{ok: Int64{-9876543210}}}, 1017 {K: text.Scalar, T: ST{ok: Uint64{9876543210}}}, 1018 {K: text.Scalar, T: ST{ok: Uint64{0x0123456789abcdef}}}, 1019 {K: text.Scalar, T: ST{ok: Int64{-0x0123456789abcdef}}}, 1020 {K: text.Scalar, T: ST{ok: Uint64{01234567}}}, 1021 {K: text.Scalar, T: ST{ok: Int64{-01234567}}}, 1022 {K: text.ListClose}, 1023 }, 1024 }, 1025 { 1026 in: `nums: [0,0x0,00,-876543210,876543210,0x01234,-0x01234,01234567,-01234567]`, 1027 want: []R{ 1028 {K: text.Name}, 1029 {K: text.ListOpen}, 1030 {K: text.Scalar, T: ST{ok: Uint32{0}}}, 1031 {K: text.Scalar, T: ST{ok: Int32{0}}}, 1032 {K: text.Scalar, T: ST{ok: Uint32{0}}}, 1033 {K: text.Scalar, T: ST{ok: Int32{-876543210}}}, 1034 {K: text.Scalar, T: ST{ok: Uint32{876543210}}}, 1035 {K: text.Scalar, T: ST{ok: Uint32{0x01234}}}, 1036 {K: text.Scalar, T: ST{ok: Int32{-0x01234}}}, 1037 {K: text.Scalar, T: ST{ok: Uint32{01234567}}}, 1038 {K: text.Scalar, T: ST{ok: Int32{-01234567}}}, 1039 {K: text.ListClose}, 1040 }, 1041 }, 1042 { 1043 in: `nums: [` + 1044 fmt.Sprintf("%d", uint64(math.MaxUint64)) + `,` + 1045 fmt.Sprintf("%d", uint32(math.MaxUint32)) + `,` + 1046 fmt.Sprintf("%d", int64(math.MaxInt64)) + `,` + 1047 fmt.Sprintf("%d", int64(math.MinInt64)) + `,` + 1048 fmt.Sprintf("%d", int32(math.MaxInt32)) + `,` + 1049 fmt.Sprintf("%d", int32(math.MinInt32)) + 1050 `]`, 1051 want: []R{ 1052 {K: text.Name}, 1053 {K: text.ListOpen}, 1054 {K: text.Scalar, T: ST{ok: Uint64{math.MaxUint64}}}, 1055 {K: text.Scalar, T: ST{ok: Uint32{math.MaxUint32}}}, 1056 {K: text.Scalar, T: ST{ok: Int64{math.MaxInt64}}}, 1057 {K: text.Scalar, T: ST{ok: Int64{math.MinInt64}}}, 1058 {K: text.Scalar, T: ST{ok: Int32{math.MaxInt32}}}, 1059 {K: text.Scalar, T: ST{ok: Int32{math.MinInt32}}}, 1060 {K: text.ListClose}, 1061 }, 1062 }, 1063 { 1064 // Integer exceeds range. 1065 in: `nums: [` + 1066 `18446744073709551616,` + // max uint64 + 1 1067 fmt.Sprintf("%d", uint64(math.MaxUint32+1)) + `,` + 1068 fmt.Sprintf("%d", uint64(math.MaxInt64+1)) + `,` + 1069 `-9223372036854775809,` + // min int64 - 1 1070 fmt.Sprintf("%d", uint64(math.MaxInt32+1)) + `,` + 1071 fmt.Sprintf("%d", int64(math.MinInt32-1)) + `` + 1072 `]`, 1073 want: []R{ 1074 {K: text.Name}, 1075 {K: text.ListOpen}, 1076 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1077 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1078 {K: text.Scalar, T: ST{nok: Int64{}}}, 1079 {K: text.Scalar, T: ST{nok: Int64{}}}, 1080 {K: text.Scalar, T: ST{nok: Int32{}}}, 1081 {K: text.Scalar, T: ST{nok: Int32{}}}, 1082 {K: text.ListClose}, 1083 }, 1084 }, 1085 { 1086 in: `nums: [0xbeefbeef, 0xbeefbeefbeefbeef]`, 1087 want: []R{ 1088 {K: text.Name}, 1089 {K: text.ListOpen}, 1090 { 1091 K: text.Scalar, 1092 T: func() ST { 1093 if flags.ProtoLegacy { 1094 return ST{ok: Int32{-1091584273}} 1095 } 1096 return ST{nok: Int32{}} 1097 }(), 1098 }, 1099 { 1100 K: text.Scalar, 1101 T: func() ST { 1102 if flags.ProtoLegacy { 1103 return ST{ok: Int64{-4688318750159552785}} 1104 } 1105 return ST{nok: Int64{}} 1106 }(), 1107 }, 1108 {K: text.ListClose}, 1109 }, 1110 }, 1111 { 1112 in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, 1113 want: []R{ 1114 {K: text.Name}, 1115 {K: text.ListOpen}, 1116 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1117 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1118 {K: text.Scalar, T: ST{ok: Float64{1.0}}}, 1119 {K: text.Scalar, T: ST{ok: Float64{10.0}}}, 1120 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1121 {K: text.Scalar, T: ST{ok: Float64{-1.0}}}, 1122 {K: text.Scalar, T: ST{ok: Float64{-10.0}}}, 1123 {K: text.Scalar, T: ST{ok: Float64{1.0}}}, 1124 {K: text.Scalar, T: ST{ok: Float64{0.1e-3}}}, 1125 {K: text.Scalar, T: ST{ok: Float64{1.5e+5}}}, 1126 {K: text.Scalar, T: ST{ok: Float64{1.0e+10}}}, 1127 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1128 {K: text.ListClose}, 1129 }, 1130 }, 1131 { 1132 in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, 1133 want: []R{ 1134 {K: text.Name}, 1135 {K: text.ListOpen}, 1136 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1137 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1138 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1139 {K: text.Scalar, T: ST{ok: Float32{10.0}}}, 1140 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1141 {K: text.Scalar, T: ST{ok: Float32{-1.0}}}, 1142 {K: text.Scalar, T: ST{ok: Float32{-10.0}}}, 1143 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1144 {K: text.Scalar, T: ST{ok: Float32{0.1e-3}}}, 1145 {K: text.Scalar, T: ST{ok: Float32{1.5e+5}}}, 1146 {K: text.Scalar, T: ST{ok: Float32{1.0e+10}}}, 1147 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1148 {K: text.ListClose}, 1149 }, 1150 }, 1151 { 1152 in: `nums: [0.,1f,10F,1e1,1.10]`, 1153 want: []R{ 1154 {K: text.Name}, 1155 {K: text.ListOpen}, 1156 {K: text.Scalar, T: ST{nok: Int64{}}}, 1157 {K: text.Scalar, T: ST{nok: Int64{}}}, 1158 {K: text.Scalar, T: ST{nok: Int64{}}}, 1159 {K: text.Scalar, T: ST{nok: Int64{}}}, 1160 {K: text.Scalar, T: ST{nok: Int64{}}}, 1161 {K: text.ListClose}, 1162 }, 1163 }, 1164 { 1165 in: `nums: [0.,1f,10F,1e1,1.10]`, 1166 want: []R{ 1167 {K: text.Name}, 1168 {K: text.ListOpen}, 1169 {K: text.Scalar, T: ST{nok: Int32{}}}, 1170 {K: text.Scalar, T: ST{nok: Int32{}}}, 1171 {K: text.Scalar, T: ST{nok: Int32{}}}, 1172 {K: text.Scalar, T: ST{nok: Int32{}}}, 1173 {K: text.Scalar, T: ST{nok: Int32{}}}, 1174 {K: text.ListClose}, 1175 }, 1176 }, 1177 { 1178 in: `nums: [0.,1f,10F,1e1,1.10]`, 1179 want: []R{ 1180 {K: text.Name}, 1181 {K: text.ListOpen}, 1182 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1183 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1184 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1185 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1186 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1187 {K: text.ListClose}, 1188 }, 1189 }, 1190 { 1191 in: `nums: [0.,1f,10F,1e1,1.10]`, 1192 want: []R{ 1193 {K: text.Name}, 1194 {K: text.ListOpen}, 1195 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1196 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1197 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1198 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1199 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1200 {K: text.ListClose}, 1201 }, 1202 }, 1203 { 1204 in: `nums: [` + 1205 fmt.Sprintf("%g", math.MaxFloat32) + `,` + 1206 fmt.Sprintf("%g", -math.MaxFloat32) + `,` + 1207 fmt.Sprintf("%g", math.MaxFloat32*2) + `,` + 1208 fmt.Sprintf("%g", -math.MaxFloat32*2) + `,` + 1209 `3.59539e+308,` + // math.MaxFloat64 * 2 1210 `-3.59539e+308,` + // -math.MaxFloat64 * 2 1211 fmt.Sprintf("%d000", uint64(math.MaxUint64)) + 1212 `]`, 1213 want: []R{ 1214 {K: text.Name}, 1215 {K: text.ListOpen}, 1216 {K: text.Scalar, T: ST{ok: Float32{float32(math.MaxFloat32)}}}, 1217 {K: text.Scalar, T: ST{ok: Float32{float32(-math.MaxFloat32)}}}, 1218 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1219 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1220 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1221 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1222 {K: text.Scalar, T: ST{ok: Float32{float32(math.MaxUint64) * 1000}}}, 1223 {K: text.ListClose}, 1224 }, 1225 }, 1226 { 1227 in: `nums: [` + 1228 fmt.Sprintf("%g", math.MaxFloat64) + `,` + 1229 fmt.Sprintf("%g", -math.MaxFloat64) + `,` + 1230 `3.59539e+308,` + // math.MaxFloat64 * 2 1231 `-3.59539e+308,` + // -math.MaxFloat64 * 2 1232 fmt.Sprintf("%d000", uint64(math.MaxUint64)) + 1233 `]`, 1234 want: []R{ 1235 {K: text.Name}, 1236 {K: text.ListOpen}, 1237 {K: text.Scalar, T: ST{ok: Float64{math.MaxFloat64}}}, 1238 {K: text.Scalar, T: ST{ok: Float64{-math.MaxFloat64}}}, 1239 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1240 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1241 {K: text.Scalar, T: ST{ok: Float64{float64(math.MaxUint64) * 1000}}}, 1242 {K: text.ListClose}, 1243 }, 1244 }, 1245 { 1246 // -0 is only valid for signed types. It is not valid for unsigned types. 1247 in: `num: [-0, -0]`, 1248 want: []R{ 1249 {K: text.Name}, 1250 {K: text.ListOpen}, 1251 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1252 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1253 {K: text.ListClose}, 1254 }, 1255 }, 1256 { 1257 // -0 is only valid for signed types. It is not valid for unsigned types. 1258 in: `num: [-0, -0]`, 1259 want: []R{ 1260 {K: text.Name}, 1261 {K: text.ListOpen}, 1262 {K: text.Scalar, T: ST{ok: Int32{0}}}, 1263 {K: text.Scalar, T: ST{ok: Int64{0}}}, 1264 {K: text.ListClose}, 1265 }, 1266 }, 1267 { 1268 // Negative zeros on float64 should preserve sign bit. 1269 in: `num: [-0, -.0]`, 1270 want: []R{ 1271 {K: text.Name}, 1272 {K: text.ListOpen}, 1273 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1274 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1275 {K: text.ListClose}, 1276 }, 1277 }, 1278 { 1279 // Negative zeros on float32 should preserve sign bit. 1280 in: `num: [-0, -.0]`, 1281 want: []R{ 1282 {K: text.Name}, 1283 {K: text.ListOpen}, 1284 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1285 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1286 {K: text.ListClose}, 1287 }, 1288 }, 1289 { 1290 in: `num: +0`, 1291 want: []R{ 1292 {K: text.Name}, 1293 {E: `invalid scalar value: +`}, 1294 }, 1295 }, 1296 { 1297 in: `num: 01.1234`, 1298 want: []R{ 1299 {K: text.Name}, 1300 {E: `invalid scalar value: 01.1234`}, 1301 }, 1302 }, 1303 { 1304 in: `num: 0x`, 1305 want: []R{ 1306 {K: text.Name}, 1307 {E: `invalid scalar value: 0x`}, 1308 }, 1309 }, 1310 { 1311 in: `num: 0xX`, 1312 want: []R{ 1313 {K: text.Name}, 1314 {E: `invalid scalar value: 0xX`}, 1315 }, 1316 }, 1317 { 1318 in: `num: 0800`, 1319 want: []R{ 1320 {K: text.Name}, 1321 {E: `invalid scalar value: 0800`}, 1322 }, 1323 }, 1324 { 1325 in: `num: 1.`, 1326 want: []R{ 1327 {K: text.Name}, 1328 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1329 }, 1330 }, 1331 { 1332 in: `num: -.`, 1333 want: []R{ 1334 {K: text.Name}, 1335 {E: `invalid scalar value: -.`}, 1336 }, 1337 }, 1338 1339 // Float special literal values, case-insensitive match. 1340 { 1341 in: `name:[nan, NaN, Nan, NAN]`, 1342 want: []R{ 1343 {K: text.Name}, 1344 {K: text.ListOpen}, 1345 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1346 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1347 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1348 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1349 {K: text.ListClose}, 1350 }, 1351 }, 1352 { 1353 in: `name:[inf, INF, infinity, Infinity, INFinity]`, 1354 want: []R{ 1355 {K: text.Name}, 1356 {K: text.ListOpen}, 1357 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1358 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1359 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1360 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1361 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1362 {K: text.ListClose}, 1363 }, 1364 }, 1365 { 1366 in: `name:[-inf, -INF, -infinity, -Infinity, -INFinity]`, 1367 want: []R{ 1368 {K: text.Name}, 1369 {K: text.ListOpen}, 1370 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1371 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1372 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1373 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1374 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1375 {K: text.ListClose}, 1376 }, 1377 }, 1378 { 1379 in: `name:[nan, NaN, Nan, NAN]`, 1380 want: []R{ 1381 {K: text.Name}, 1382 {K: text.ListOpen}, 1383 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1384 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1385 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1386 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1387 {K: text.ListClose}, 1388 }, 1389 }, 1390 { 1391 in: `name:[inf, INF, infinity, Infinity, INFinity]`, 1392 want: []R{ 1393 {K: text.Name}, 1394 {K: text.ListOpen}, 1395 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1396 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1397 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1398 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1399 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1400 {K: text.ListClose}, 1401 }, 1402 }, 1403 { 1404 in: `name:[-inf, -INF, -infinity, -Infinity, -INFinity]`, 1405 want: []R{ 1406 {K: text.Name}, 1407 {K: text.ListOpen}, 1408 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1409 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1410 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1411 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1412 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1413 {K: text.ListClose}, 1414 }, 1415 }, 1416 { 1417 // C++ permits this, but we currently reject this. It is easy to add 1418 // if needed. 1419 in: `name: -nan`, 1420 want: []R{ 1421 {K: text.Name}, 1422 {K: text.Scalar, T: ST{nok: Float64{}}}, 1423 }, 1424 }, 1425 // Messages. 1426 { 1427 in: `m: {}`, 1428 want: []R{ 1429 {K: text.Name}, 1430 {K: text.MessageOpen}, 1431 {K: text.MessageClose}, 1432 {K: text.EOF}, 1433 }, 1434 }, 1435 { 1436 in: `m: <>`, 1437 want: []R{ 1438 {K: text.Name}, 1439 {K: text.MessageOpen}, 1440 {K: text.MessageClose}, 1441 {K: text.EOF}, 1442 }, 1443 }, 1444 { 1445 in: space + `m {` + space + "\n# comment\n" + `}` + space, 1446 want: []R{ 1447 {K: text.Name}, 1448 {K: text.MessageOpen}, 1449 {K: text.MessageClose}, 1450 }, 1451 }, 1452 { 1453 in: `m { foo: < bar: "hello" > }`, 1454 want: []R{ 1455 {K: text.Name, RS: "m"}, 1456 {K: text.MessageOpen}, 1457 1458 {K: text.Name, RS: "foo"}, 1459 {K: text.MessageOpen}, 1460 1461 {K: text.Name, RS: "bar"}, 1462 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1463 1464 {K: text.MessageClose}, 1465 1466 {K: text.MessageClose}, 1467 }, 1468 }, 1469 { 1470 in: `list [ <s:"hello">, {s:"world"} ]`, 1471 want: []R{ 1472 {K: text.Name, RS: "list"}, 1473 {K: text.ListOpen}, 1474 1475 {K: text.MessageOpen}, 1476 {K: text.Name, RS: "s"}, 1477 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1478 {K: text.MessageClose}, 1479 1480 {K: text.MessageOpen}, 1481 {K: text.Name, RS: "s"}, 1482 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 1483 {K: text.MessageClose}, 1484 1485 {K: text.ListClose}, 1486 {K: text.EOF}, 1487 }, 1488 }, 1489 { 1490 in: `m: { >`, 1491 want: []R{ 1492 {K: text.Name}, 1493 {K: text.MessageOpen}, 1494 {E: `mismatched close character '>'`}, 1495 }, 1496 }, 1497 { 1498 in: `m: <s: "hello"}`, 1499 want: []R{ 1500 {K: text.Name}, 1501 {K: text.MessageOpen}, 1502 1503 {K: text.Name}, 1504 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1505 1506 {E: `mismatched close character '}'`}, 1507 }, 1508 }, 1509 { 1510 in: `{}`, 1511 want: []R{{E: `invalid field name: {`}}, 1512 }, 1513 { 1514 in: ` 1515 m: { 1516 foo: true; 1517 bar: { 1518 enum: ENUM 1519 list: [ < >, { } ] ; 1520 } 1521 [qux]: "end" 1522 } 1523 `, 1524 want: []R{ 1525 {K: text.Name}, 1526 {K: text.MessageOpen}, 1527 1528 {K: text.Name, RS: "foo"}, 1529 {K: text.Scalar, T: ST{ok: Bool{true}}}, 1530 1531 {K: text.Name, RS: "bar"}, 1532 {K: text.MessageOpen}, 1533 1534 {K: text.Name, RS: "enum"}, 1535 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 1536 1537 {K: text.Name, RS: "list"}, 1538 {K: text.ListOpen}, 1539 {K: text.MessageOpen}, 1540 {K: text.MessageClose}, 1541 {K: text.MessageOpen}, 1542 {K: text.MessageClose}, 1543 {K: text.ListClose}, 1544 1545 {K: text.MessageClose}, 1546 1547 {K: text.Name, RS: "[qux]"}, 1548 {K: text.Scalar, T: ST{ok: Str{"end"}}}, 1549 1550 {K: text.MessageClose}, 1551 {K: text.EOF}, 1552 }, 1553 }, 1554 1555 // Other syntax errors. 1556 { 1557 in: "x: -", 1558 want: []R{ 1559 {K: text.Name}, 1560 {E: `syntax error (line 1:4): invalid scalar value: -`}, 1561 }, 1562 }, 1563 { 1564 in: "x:[\"π©\"x", 1565 want: []R{ 1566 {K: text.Name}, 1567 {K: text.ListOpen}, 1568 {K: text.Scalar, T: ST{ok: Str{"π©"}}, P: 3}, 1569 {E: `syntax error (line 1:7)`}, 1570 }, 1571 }, 1572 { 1573 in: "x:\n\n[\"π₯π₯π₯\"x", 1574 want: []R{ 1575 {K: text.Name}, 1576 {K: text.ListOpen}, 1577 {K: text.Scalar, T: ST{ok: Str{"π₯π₯π₯"}}, P: 5}, 1578 {E: `syntax error (line 3:7)`}, 1579 }, 1580 }, 1581 { 1582 // multi-rune emojis; could be column:8 1583 in: "x:[\"ππ»ππΏ\"x", 1584 want: []R{ 1585 {K: text.Name}, 1586 {K: text.ListOpen}, 1587 {K: text.Scalar, T: ST{ok: Str{"ππ»ππΏ"}}, P: 3}, 1588 {E: `syntax error (line 1:10)`}, 1589 }, 1590 }, 1591 } 1592 1593 for _, tc := range tests { 1594 t.Run("", func(t *testing.T) { 1595 tc := tc 1596 in := []byte(tc.in) 1597 dec := text.NewDecoder(in[:len(in):len(in)]) 1598 for i, want := range tc.want { 1599 peekTok, peekErr := dec.Peek() 1600 tok, err := dec.Read() 1601 if err != nil { 1602 if want.E == "" { 1603 errorf(t, tc.in, "Read() got unexpected error: %v", err) 1604 } else if !strings.Contains(err.Error(), want.E) { 1605 errorf(t, tc.in, "Read() got %q, want %q", err, want.E) 1606 } 1607 return 1608 } 1609 if want.E != "" { 1610 errorf(t, tc.in, "Read() got nil error, want %q", want.E) 1611 return 1612 } 1613 gotK := tok.Kind() 1614 if gotK != want.K { 1615 errorf(t, tc.in, "Read() got %v, want %v", gotK, want.K) 1616 return 1617 } 1618 checkToken(t, tok, i, want, tc.in) 1619 if !cmp.Equal(tok, peekTok, cmp.Comparer(text.TokenEquals)) { 1620 errorf(t, tc.in, "Peek() %+v != Read() token %+v", peekTok, tok) 1621 } 1622 if err != peekErr { 1623 errorf(t, tc.in, "Peek() error %v != Read() error %v", err, peekErr) 1624 } 1625 } 1626 }) 1627 } 1628 } 1629 1630 func checkToken(t *testing.T, tok text.Token, idx int, r R, in string) { 1631 // Validate Token.Pos() if R.P is set. 1632 if r.P > 0 { 1633 got := tok.Pos() 1634 if got != r.P { 1635 errorf(t, in, "want#%d: Token.Pos() got %v want %v", idx, got, r.P) 1636 } 1637 } 1638 1639 // Validate Token.RawString if R.RS is set. 1640 if len(r.RS) > 0 { 1641 got := tok.RawString() 1642 if got != r.RS { 1643 errorf(t, in, "want#%d: Token.RawString() got %v want %v", idx, got, r.P) 1644 } 1645 } 1646 1647 // Skip checking for Token details if r.T is not set. 1648 if r.T == nil { 1649 return 1650 } 1651 1652 switch tok.Kind() { 1653 case text.Name: 1654 want := r.T.(NT) 1655 kind := tok.NameKind() 1656 if kind != want.K { 1657 errorf(t, in, "want#%d: Token.NameKind() got %v want %v", idx, kind, want.K) 1658 return 1659 } 1660 switch kind { 1661 case text.IdentName: 1662 got := tok.IdentName() 1663 if got != want.S { 1664 errorf(t, in, "want#%d: Token.IdentName() got %v want %v", idx, got, want.S) 1665 } 1666 case text.TypeName: 1667 got := tok.TypeName() 1668 if got != want.S { 1669 errorf(t, in, "want#%d: Token.TypeName() got %v want %v", idx, got, want.S) 1670 } 1671 case text.FieldNumber: 1672 got := tok.FieldNumber() 1673 if got != want.N { 1674 errorf(t, in, "want#%d: Token.FieldNumber() got %v want %v", idx, got, want.N) 1675 } 1676 } 1677 1678 case text.Scalar: 1679 want := r.T.(ST) 1680 if ok := want.ok; ok != nil { 1681 if err := ok.checkOk(tok); err != "" { 1682 errorf(t, in, "want#%d: %s", idx, err) 1683 } 1684 } 1685 if nok := want.nok; nok != nil { 1686 if err := nok.checkNok(tok); err != "" { 1687 errorf(t, in, "want#%d: %s", idx, err) 1688 } 1689 } 1690 } 1691 } 1692 1693 func errorf(t *testing.T, in string, fmtStr string, args ...interface{}) { 1694 t.Helper() 1695 vargs := []interface{}{in} 1696 for _, arg := range args { 1697 vargs = append(vargs, arg) 1698 } 1699 t.Errorf("input:\n%s\n~end~\n"+fmtStr, vargs...) 1700 } 1701 1702 func TestUnmarshalString(t *testing.T) { 1703 tests := []struct { 1704 in string 1705 // want is expected string result. 1706 want string 1707 // err is expected error substring from calling DecodeString if set. 1708 err string 1709 }{ 1710 { 1711 in: func() string { 1712 var b []byte 1713 for i := 0; i < utf8.RuneSelf; i++ { 1714 switch i { 1715 case 0, '\\', '\n', '\'': // these must be escaped, so ignore them 1716 default: 1717 b = append(b, byte(i)) 1718 } 1719 } 1720 return "'" + string(b) + "'" 1721 }(), 1722 want: "\x01\x02\x03\x04\x05\x06\a\b\t\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f", 1723 }, 1724 { 1725 in: "'\xde\xad\xbe\xef'", 1726 err: `invalid UTF-8 detected`, 1727 }, 1728 { 1729 // Valid UTF-8 wire encoding, but sub-optimal encoding. 1730 in: "'\xc0\x80'", 1731 err: "invalid UTF-8 detected", 1732 }, 1733 { 1734 // Valid UTF-8 wire encoding, but invalid rune (surrogate pair). 1735 in: "'\xed\xa0\x80'", 1736 err: "invalid UTF-8 detected", 1737 }, 1738 { 1739 // Valid UTF-8 wire encoding, but invalid rune (above max rune). 1740 in: "'\xf7\xbf\xbf\xbf'", 1741 err: "invalid UTF-8 detected", 1742 }, 1743 { 1744 // Valid UTF-8 wire encoding of the RuneError rune. 1745 in: "'\xef\xbf\xbd'", 1746 want: string(utf8.RuneError), 1747 }, 1748 { 1749 in: "'hello\u1234world'", 1750 want: "hello\u1234world", 1751 }, 1752 { 1753 in: `'\"\'\\\?\a\b\n\r\t\v\f\1\12\123\xA\xaB\x12\uAb8f\U0010FFFF'`, 1754 want: "\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff", 1755 }, 1756 { 1757 in: `str: '\8'`, 1758 err: `invalid escape code "\\8" in string`, 1759 }, 1760 { 1761 in: `'\1x'`, 1762 want: "\001x", 1763 }, 1764 { 1765 in: `'\12x'`, 1766 want: "\012x", 1767 }, 1768 { 1769 in: `'\123x'`, 1770 want: "\123x", 1771 }, 1772 { 1773 in: `'\1234x'`, 1774 want: "\1234x", 1775 }, 1776 { 1777 in: `'\1'`, 1778 want: "\001", 1779 }, 1780 { 1781 in: `'\12'`, 1782 want: "\012", 1783 }, 1784 { 1785 in: `'\123'`, 1786 want: "\123", 1787 }, 1788 { 1789 in: `'\1234'`, 1790 want: "\1234", 1791 }, 1792 { 1793 in: `'\377'`, 1794 want: "\377", 1795 }, 1796 { 1797 // Overflow octal escape. 1798 in: `'\400'`, 1799 err: `invalid octal escape code "\\400" in string`, 1800 }, 1801 { 1802 in: `'\xfx'`, 1803 want: "\x0fx", 1804 }, 1805 { 1806 in: `'\xffx'`, 1807 want: "\xffx", 1808 }, 1809 { 1810 in: `'\xfffx'`, 1811 want: "\xfffx", 1812 }, 1813 { 1814 in: `'\xf'`, 1815 want: "\x0f", 1816 }, 1817 { 1818 in: `'\xff'`, 1819 want: "\xff", 1820 }, 1821 { 1822 in: `'\xfff'`, 1823 want: "\xfff", 1824 }, 1825 { 1826 in: `'\xz'`, 1827 err: `invalid hex escape code "\\x" in string`, 1828 }, 1829 { 1830 in: `'\uPo'`, 1831 err: eofErr, 1832 }, 1833 { 1834 in: `'\uPoo'`, 1835 err: `invalid Unicode escape code "\\uPoo'" in string`, 1836 }, 1837 { 1838 in: `str: '\uPoop'`, 1839 err: `invalid Unicode escape code "\\uPoop" in string`, 1840 }, 1841 { 1842 // Unmatched surrogate pair. 1843 in: `str: '\uDEAD'`, 1844 err: `unexpected EOF`, // trying to reader other half 1845 }, 1846 { 1847 // Surrogate pair with invalid other half. 1848 in: `str: '\uDEAD\u0000'`, 1849 err: `invalid Unicode escape code "\\u0000" in string`, 1850 }, 1851 { 1852 // Properly matched surrogate pair. 1853 in: `'\uD800\uDEAD'`, 1854 want: "π", 1855 }, 1856 { 1857 // Overflow on Unicode rune. 1858 in: `'\U00110000'`, 1859 err: `invalid Unicode escape code "\\U00110000" in string`, 1860 }, 1861 { 1862 in: `'\z'`, 1863 err: `invalid escape code "\\z" in string`, 1864 }, 1865 { 1866 // Strings cannot have NUL literal since C-style strings forbid them. 1867 in: "'\x00'", 1868 err: `invalid character '\x00' in string`, 1869 }, 1870 { 1871 // Strings cannot have newline literal. The C++ permits them if an 1872 // option is specified to allow them. In Go, we always forbid them. 1873 in: "'\n'", 1874 err: `invalid character '\n' in string`, 1875 }, 1876 } 1877 1878 for _, tc := range tests { 1879 t.Run("", func(t *testing.T) { 1880 got, err := text.UnmarshalString(tc.in) 1881 if err != nil { 1882 if tc.err == "" { 1883 errorf(t, tc.in, "UnmarshalString() got unexpected error: %q", err) 1884 } else if !strings.Contains(err.Error(), tc.err) { 1885 errorf(t, tc.in, "UnmarshalString() error got %q, want %q", err, tc.err) 1886 } 1887 return 1888 } 1889 if tc.err != "" { 1890 errorf(t, tc.in, "UnmarshalString() got nil error, want %q", tc.err) 1891 return 1892 } 1893 if got != tc.want { 1894 errorf(t, tc.in, "UnmarshalString()\n[got]\n%s\n[want]\n%s", got, tc.want) 1895 } 1896 }) 1897 } 1898 } 1899 1900 // Tests line and column number produced by Decoder.Position. 1901 func TestPosition(t *testing.T) { 1902 dec := text.NewDecoder([]byte("0123456789\n12345\n789")) 1903 1904 tests := []struct { 1905 pos int 1906 row int 1907 col int 1908 }{ 1909 { 1910 pos: 0, 1911 row: 1, 1912 col: 1, 1913 }, 1914 { 1915 pos: 10, 1916 row: 1, 1917 col: 11, 1918 }, 1919 { 1920 pos: 11, 1921 row: 2, 1922 col: 1, 1923 }, 1924 { 1925 pos: 18, 1926 row: 3, 1927 col: 2, 1928 }, 1929 } 1930 1931 for _, tc := range tests { 1932 t.Run("", func(t *testing.T) { 1933 row, col := dec.Position(tc.pos) 1934 if row != tc.row || col != tc.col { 1935 t.Errorf("Position(%d) got (%d,%d) want (%d,%d)", tc.pos, row, col, tc.row, tc.col) 1936 } 1937 }) 1938 } 1939 }