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 5package text_test 6 7import ( 8 "fmt" 9 "math" 10 "strings" 11 "testing" 12 "unicode/utf8" 13 14 "github.com/google/go-cmp/cmp" 15 16 "google.golang.org/protobuf/internal/encoding/text" 17 "google.golang.org/protobuf/internal/flags" 18) 19 20var eofErr = text.ErrUnexpectedEOF.Error() 21 22type 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. 36type 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. 47type 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. 56type 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 66type Str struct { 67 val string 68} 69 70func (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 81func (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 88type Enum struct { 89 val string 90} 91 92func (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 103func (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 110type Bool struct { 111 val bool 112} 113 114func (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 125func (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 132type Uint64 struct { 133 val uint64 134} 135 136func (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 147func (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 154type Uint32 struct { 155 val uint32 156} 157 158func (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 169func (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 176type Int64 struct { 177 val int64 178} 179 180func (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 191func (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 198type Int32 struct { 199 val int32 200} 201 202func (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 213func (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 220type Float64 struct { 221 val float64 222} 223 224func (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 235func (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 242type Float32 struct { 243 val float32 244} 245 246func (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 257func (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 264func 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 in: "- \t 123.321e6", 560 want: []R{{E: "invalid field number: -123.321e6"}}, 561 }, 562 { 563 in: "-", 564 want: []R{{E: "invalid field name: -"}}, 565 }, 566 { 567 in: "- ", 568 want: []R{{E: "invalid field name: -"}}, 569 }, 570 { 571 in: "- # negative\n 123", 572 want: []R{{E: "invalid field number: -123"}}, 573 }, 574 { 575 // Field number > math.MaxInt32. 576 in: "2147483648:", 577 want: []R{{E: "invalid field number: 2147483648"}}, 578 }, 579 580 // String field value. More string parsing specific testing in 581 // TestUnmarshalString. 582 { 583 in: `name: "hello world"`, 584 want: []R{ 585 {K: text.Name}, 586 { 587 K: text.Scalar, 588 T: ST{ok: Str{"hello world"}, nok: Enum{}}, 589 RS: `"hello world"`, 590 }, 591 {K: text.EOF}, 592 }, 593 }, 594 { 595 in: `name: 'hello'`, 596 want: []R{ 597 {K: text.Name}, 598 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 599 }, 600 }, 601 { 602 in: `name: "hello'`, 603 want: []R{ 604 {K: text.Name}, 605 {E: eofErr}, 606 }, 607 }, 608 { 609 in: `name: 'hello`, 610 want: []R{ 611 {K: text.Name}, 612 {E: eofErr}, 613 }, 614 }, 615 { 616 // Field name without separator is ok. prototext package will need 617 // to determine that this is not valid for scalar values. 618 in: space + `name` + space + `"hello"` + space, 619 want: []R{ 620 {K: text.Name}, 621 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 622 }, 623 }, 624 { 625 in: `name'hello'`, 626 want: []R{ 627 {K: text.Name}, 628 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 629 }, 630 }, 631 { 632 in: `name: ` + space + `"hello"` + space + `,`, 633 want: []R{ 634 {K: text.Name}, 635 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 636 {K: text.EOF}, 637 }, 638 }, 639 { 640 in: `name` + space + `:` + `"hello"` + space + `;` + space, 641 want: []R{ 642 {K: text.Name}, 643 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 644 {K: text.EOF}, 645 }, 646 }, 647 { 648 in: `name:"hello" , ,`, 649 want: []R{ 650 {K: text.Name}, 651 {K: text.Scalar}, 652 {E: "(line 1:16): invalid field name: ,"}, 653 }, 654 }, 655 { 656 in: `name:"hello" , ;`, 657 want: []R{ 658 {K: text.Name}, 659 {K: text.Scalar}, 660 {E: "(line 1:16): invalid field name: ;"}, 661 }, 662 }, 663 { 664 in: `name:"hello" name:'world'`, 665 want: []R{ 666 {K: text.Name}, 667 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 668 {K: text.Name}, 669 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 670 {K: text.EOF}, 671 }, 672 }, 673 { 674 in: `name:"hello", name:"world"`, 675 want: []R{ 676 {K: text.Name}, 677 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 678 {K: text.Name}, 679 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 680 {K: text.EOF}, 681 }, 682 }, 683 { 684 in: `name:"hello"; name:"world",`, 685 want: []R{ 686 {K: text.Name}, 687 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 688 {K: text.Name}, 689 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 690 {K: text.EOF}, 691 }, 692 }, 693 { 694 in: `foo:"hello"bar:"world"`, 695 want: []R{ 696 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "foo"}}, 697 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 698 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "bar"}}, 699 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 700 {K: text.EOF}, 701 }, 702 }, 703 { 704 in: `foo:"hello"[bar]:"world"`, 705 want: []R{ 706 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "foo"}}, 707 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 708 {K: text.Name, T: NT{K: text.TypeName, Sep: true, S: "bar"}}, 709 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 710 {K: text.EOF}, 711 }, 712 }, 713 { 714 in: `name:"foo"` + space + `"bar"` + space + `'qux'`, 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 in: `name:"foo"'bar'"qux"`, 723 want: []R{ 724 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 725 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 726 {K: text.EOF}, 727 }, 728 }, 729 { 730 in: `name:"foo"` + space + `"bar" # comment` + "\n'qux' # comment", 731 want: []R{ 732 {K: text.Name, T: NT{K: text.IdentName, Sep: true, S: "name"}}, 733 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 734 {K: text.EOF}, 735 }, 736 }, 737 738 // Lists. 739 { 740 in: `name: [`, 741 want: []R{ 742 {K: text.Name}, 743 {K: text.ListOpen}, 744 {E: eofErr}, 745 }, 746 }, 747 { 748 in: `name: []`, 749 want: []R{ 750 {K: text.Name}, 751 {K: text.ListOpen}, 752 {K: text.ListClose}, 753 {K: text.EOF}, 754 }, 755 }, 756 { 757 in: `name []`, 758 want: []R{ 759 {K: text.Name}, 760 {K: text.ListOpen}, 761 {K: text.ListClose}, 762 {K: text.EOF}, 763 }, 764 }, 765 { 766 in: `name: [,`, 767 want: []R{ 768 {K: text.Name}, 769 {K: text.ListOpen}, 770 {E: `(line 1:8): invalid scalar value: ,`}, 771 }, 772 }, 773 { 774 in: `name: [0`, 775 want: []R{ 776 {K: text.Name}, 777 {K: text.ListOpen}, 778 {K: text.Scalar}, 779 {E: eofErr}, 780 }, 781 }, 782 { 783 in: `name: [` + space + `"hello"` + space + `]` + space, 784 want: []R{ 785 {K: text.Name}, 786 {K: text.ListOpen}, 787 {K: text.Scalar, T: ST{ok: Str{"hello"}}, P: len(space) + 7}, 788 {K: text.ListClose}, 789 {K: text.EOF}, 790 }, 791 }, 792 { 793 in: `name: ["hello",]`, 794 want: []R{ 795 {K: text.Name}, 796 {K: text.ListOpen}, 797 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 798 {E: `invalid scalar value: ]`}, 799 }, 800 }, 801 { 802 in: `name: ["foo"` + space + `'bar' "qux"]`, 803 want: []R{ 804 {K: text.Name}, 805 {K: text.ListOpen}, 806 {K: text.Scalar, T: ST{ok: Str{"foobarqux"}}}, 807 {K: text.ListClose}, 808 {K: text.EOF}, 809 }, 810 }, 811 { 812 in: `name:` + space + `["foo",` + space + "'bar', # comment\n\n" + `"qux"]`, 813 want: []R{ 814 {K: text.Name}, 815 {K: text.ListOpen}, 816 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 817 {K: text.Scalar, T: ST{ok: Str{"bar"}}}, 818 {K: text.Scalar, T: ST{ok: Str{"qux"}}}, 819 {K: text.ListClose}, 820 {K: text.EOF}, 821 }, 822 }, 823 824 { 825 // List within list is not allowed. 826 in: `name: [[]]`, 827 want: []R{ 828 {K: text.Name}, 829 {K: text.ListOpen}, 830 {E: `syntax error (line 1:8): invalid scalar value: [`}, 831 }, 832 }, 833 { 834 // List items need to be separated by ,. 835 in: `name: ["foo" true]`, 836 want: []R{ 837 {K: text.Name}, 838 {K: text.ListOpen}, 839 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 840 {E: `syntax error (line 1:14): unexpected character 't'`}, 841 }, 842 }, 843 { 844 in: `name: ["foo"; "bar"]`, 845 want: []R{ 846 {K: text.Name}, 847 {K: text.ListOpen}, 848 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 849 {E: `syntax error (line 1:13): unexpected character ';'`}, 850 }, 851 }, 852 { 853 in: `name: ["foo", true, ENUM, 1.0]`, 854 want: []R{ 855 {K: text.Name}, 856 {K: text.ListOpen}, 857 {K: text.Scalar, T: ST{ok: Str{"foo"}}}, 858 {K: text.Scalar, T: ST{ok: Enum{"true"}}}, 859 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 860 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 861 {K: text.ListClose}, 862 }, 863 }, 864 865 // Boolean literal values. 866 { 867 in: `name: True`, 868 want: []R{ 869 {K: text.Name}, 870 { 871 K: text.Scalar, 872 T: ST{ok: Bool{true}}, 873 }, 874 {K: text.EOF}, 875 }, 876 }, 877 { 878 in: `name false`, 879 want: []R{ 880 {K: text.Name}, 881 { 882 K: text.Scalar, 883 T: ST{ok: Bool{false}}, 884 }, 885 {K: text.EOF}, 886 }, 887 }, 888 { 889 in: `name: [t, f, True, False, true, false, 1, 0, 0x01, 0x00, 01, 00]`, 890 want: []R{ 891 {K: text.Name}, 892 {K: text.ListOpen}, 893 {K: text.Scalar, T: ST{ok: Bool{true}}}, 894 {K: text.Scalar, T: ST{ok: Bool{false}}}, 895 {K: text.Scalar, T: ST{ok: Bool{true}}}, 896 {K: text.Scalar, T: ST{ok: Bool{false}}}, 897 {K: text.Scalar, T: ST{ok: Bool{true}}}, 898 {K: text.Scalar, T: ST{ok: Bool{false}}}, 899 {K: text.Scalar, T: ST{ok: Bool{true}}}, 900 {K: text.Scalar, T: ST{ok: Bool{false}}}, 901 {K: text.Scalar, T: ST{ok: Bool{true}}}, 902 {K: text.Scalar, T: ST{ok: Bool{false}}}, 903 {K: text.Scalar, T: ST{ok: Bool{true}}}, 904 {K: text.Scalar, T: ST{ok: Bool{false}}}, 905 {K: text.ListClose}, 906 }, 907 }, 908 { 909 // Looks like boolean but not. 910 in: `name: [tRUe, falSE, -1, -0, -0x01, -0x00, -01, -00, 0.0]`, 911 want: []R{ 912 {K: text.Name}, 913 {K: text.ListOpen}, 914 {K: text.Scalar, T: ST{nok: Bool{}}}, 915 {K: text.Scalar, T: ST{nok: Bool{}}}, 916 {K: text.Scalar, T: ST{nok: Bool{}}}, 917 {K: text.Scalar, T: ST{nok: Bool{}}}, 918 {K: text.Scalar, T: ST{nok: Bool{}}}, 919 {K: text.Scalar, T: ST{nok: Bool{}}}, 920 {K: text.Scalar, T: ST{nok: Bool{}}}, 921 {K: text.Scalar, T: ST{nok: Bool{}}}, 922 {K: text.Scalar, T: ST{nok: Bool{}}}, 923 {K: text.ListClose}, 924 }, 925 }, 926 { 927 in: `foo: true[bar] false`, 928 want: []R{ 929 {K: text.Name}, 930 {K: text.Scalar, T: ST{ok: Bool{true}}}, 931 {K: text.Name}, 932 {K: text.Scalar, T: ST{ok: Bool{false}}}, 933 }, 934 }, 935 936 // Enum field values. 937 { 938 in: space + `name: ENUM`, 939 want: []R{ 940 {K: text.Name}, 941 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 942 }, 943 }, 944 { 945 in: space + `name:[TRUE, FALSE, T, F, t, f]`, 946 want: []R{ 947 {K: text.Name}, 948 {K: text.ListOpen}, 949 {K: text.Scalar, T: ST{ok: Enum{"TRUE"}}}, 950 {K: text.Scalar, T: ST{ok: Enum{"FALSE"}}}, 951 {K: text.Scalar, T: ST{ok: Enum{"T"}}}, 952 {K: text.Scalar, T: ST{ok: Enum{"F"}}}, 953 {K: text.Scalar, T: ST{ok: Enum{"t"}}}, 954 {K: text.Scalar, T: ST{ok: Enum{"f"}}}, 955 {K: text.ListClose}, 956 }, 957 }, 958 { 959 in: `foo: Enum1[bar]:Enum2`, 960 want: []R{ 961 {K: text.Name}, 962 {K: text.Scalar, T: ST{ok: Enum{"Enum1"}}}, 963 {K: text.Name}, 964 {K: text.Scalar, T: ST{ok: Enum{"Enum2"}}}, 965 }, 966 }, 967 { 968 // Invalid enum values. 969 in: `name: [-inf, -foo, "string", 42, 1.0, 0x47]`, 970 want: []R{ 971 {K: text.Name}, 972 {K: text.ListOpen}, 973 {K: text.Scalar, T: ST{nok: Enum{}}}, 974 {K: text.Scalar, T: ST{nok: Enum{}}}, 975 {K: text.Scalar, T: ST{nok: Enum{}}}, 976 {K: text.Scalar, T: ST{nok: Enum{}}}, 977 {K: text.Scalar, T: ST{nok: Enum{}}}, 978 {K: text.Scalar, T: ST{nok: Enum{}}}, 979 {K: text.ListClose}, 980 }, 981 }, 982 { 983 in: `name: true.`, 984 want: []R{ 985 {K: text.Name}, 986 {E: `invalid scalar value: true.`}, 987 }, 988 }, 989 990 // Numeric values. 991 { 992 in: `nums:42 nums:0x2A nums:052`, 993 want: []R{ 994 {K: text.Name}, 995 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 996 {K: text.Name}, 997 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 998 {K: text.Name}, 999 {K: text.Scalar, T: ST{ok: Uint64{42}}}, 1000 }, 1001 }, 1002 { 1003 in: `nums:[-42, -0x2a, -052]`, 1004 want: []R{ 1005 {K: text.Name}, 1006 {K: text.ListOpen}, 1007 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1008 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1009 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1010 {K: text.ListClose}, 1011 }, 1012 }, 1013 { 1014 in: `nums:[-42, -0x2a, -052]`, 1015 want: []R{ 1016 {K: text.Name}, 1017 {K: text.ListOpen}, 1018 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1019 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1020 {K: text.Scalar, T: ST{ok: Int64{-42}}}, 1021 {K: text.ListClose}, 1022 }, 1023 }, 1024 { 1025 in: `nums: [0,0x0,00,-9876543210,9876543210,0x0123456789abcdef,-0x0123456789abcdef,01234567,-01234567]`, 1026 want: []R{ 1027 {K: text.Name}, 1028 {K: text.ListOpen}, 1029 {K: text.Scalar, T: ST{ok: Uint64{0}}}, 1030 {K: text.Scalar, T: ST{ok: Int64{0}}}, 1031 {K: text.Scalar, T: ST{ok: Uint64{0}}}, 1032 {K: text.Scalar, T: ST{ok: Int64{-9876543210}}}, 1033 {K: text.Scalar, T: ST{ok: Uint64{9876543210}}}, 1034 {K: text.Scalar, T: ST{ok: Uint64{0x0123456789abcdef}}}, 1035 {K: text.Scalar, T: ST{ok: Int64{-0x0123456789abcdef}}}, 1036 {K: text.Scalar, T: ST{ok: Uint64{01234567}}}, 1037 {K: text.Scalar, T: ST{ok: Int64{-01234567}}}, 1038 {K: text.ListClose}, 1039 }, 1040 }, 1041 { 1042 in: `nums: [0,0x0,00,-876543210,876543210,0x01234,-0x01234,01234567,-01234567]`, 1043 want: []R{ 1044 {K: text.Name}, 1045 {K: text.ListOpen}, 1046 {K: text.Scalar, T: ST{ok: Uint32{0}}}, 1047 {K: text.Scalar, T: ST{ok: Int32{0}}}, 1048 {K: text.Scalar, T: ST{ok: Uint32{0}}}, 1049 {K: text.Scalar, T: ST{ok: Int32{-876543210}}}, 1050 {K: text.Scalar, T: ST{ok: Uint32{876543210}}}, 1051 {K: text.Scalar, T: ST{ok: Uint32{0x01234}}}, 1052 {K: text.Scalar, T: ST{ok: Int32{-0x01234}}}, 1053 {K: text.Scalar, T: ST{ok: Uint32{01234567}}}, 1054 {K: text.Scalar, T: ST{ok: Int32{-01234567}}}, 1055 {K: text.ListClose}, 1056 }, 1057 }, 1058 { 1059 in: `nums: [` + 1060 fmt.Sprintf("%d", uint64(math.MaxUint64)) + `,` + 1061 fmt.Sprintf("%d", uint32(math.MaxUint32)) + `,` + 1062 fmt.Sprintf("%d", int64(math.MaxInt64)) + `,` + 1063 fmt.Sprintf("%d", int64(math.MinInt64)) + `,` + 1064 fmt.Sprintf("%d", int32(math.MaxInt32)) + `,` + 1065 fmt.Sprintf("%d", int32(math.MinInt32)) + 1066 `]`, 1067 want: []R{ 1068 {K: text.Name}, 1069 {K: text.ListOpen}, 1070 {K: text.Scalar, T: ST{ok: Uint64{math.MaxUint64}}}, 1071 {K: text.Scalar, T: ST{ok: Uint32{math.MaxUint32}}}, 1072 {K: text.Scalar, T: ST{ok: Int64{math.MaxInt64}}}, 1073 {K: text.Scalar, T: ST{ok: Int64{math.MinInt64}}}, 1074 {K: text.Scalar, T: ST{ok: Int32{math.MaxInt32}}}, 1075 {K: text.Scalar, T: ST{ok: Int32{math.MinInt32}}}, 1076 {K: text.ListClose}, 1077 }, 1078 }, 1079 { 1080 // Integer exceeds range. 1081 in: `nums: [` + 1082 `18446744073709551616,` + // max uint64 + 1 1083 fmt.Sprintf("%d", uint64(math.MaxUint32+1)) + `,` + 1084 fmt.Sprintf("%d", uint64(math.MaxInt64+1)) + `,` + 1085 `-9223372036854775809,` + // min int64 - 1 1086 fmt.Sprintf("%d", uint64(math.MaxInt32+1)) + `,` + 1087 fmt.Sprintf("%d", int64(math.MinInt32-1)) + `` + 1088 `]`, 1089 want: []R{ 1090 {K: text.Name}, 1091 {K: text.ListOpen}, 1092 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1093 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1094 {K: text.Scalar, T: ST{nok: Int64{}}}, 1095 {K: text.Scalar, T: ST{nok: Int64{}}}, 1096 {K: text.Scalar, T: ST{nok: Int32{}}}, 1097 {K: text.Scalar, T: ST{nok: Int32{}}}, 1098 {K: text.ListClose}, 1099 }, 1100 }, 1101 { 1102 in: `nums: [0xbeefbeef, 0xbeefbeefbeefbeef]`, 1103 want: []R{ 1104 {K: text.Name}, 1105 {K: text.ListOpen}, 1106 { 1107 K: text.Scalar, 1108 T: func() ST { 1109 if flags.ProtoLegacy { 1110 return ST{ok: Int32{-1091584273}} 1111 } 1112 return ST{nok: Int32{}} 1113 }(), 1114 }, 1115 { 1116 K: text.Scalar, 1117 T: func() ST { 1118 if flags.ProtoLegacy { 1119 return ST{ok: Int64{-4688318750159552785}} 1120 } 1121 return ST{nok: Int64{}} 1122 }(), 1123 }, 1124 {K: text.ListClose}, 1125 }, 1126 }, 1127 { 1128 in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, 1129 want: []R{ 1130 {K: text.Name}, 1131 {K: text.ListOpen}, 1132 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1133 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1134 {K: text.Scalar, T: ST{ok: Float64{1.0}}}, 1135 {K: text.Scalar, T: ST{ok: Float64{10.0}}}, 1136 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1137 {K: text.Scalar, T: ST{ok: Float64{-1.0}}}, 1138 {K: text.Scalar, T: ST{ok: Float64{-10.0}}}, 1139 {K: text.Scalar, T: ST{ok: Float64{1.0}}}, 1140 {K: text.Scalar, T: ST{ok: Float64{0.1e-3}}}, 1141 {K: text.Scalar, T: ST{ok: Float64{1.5e+5}}}, 1142 {K: text.Scalar, T: ST{ok: Float64{1.0e+10}}}, 1143 {K: text.Scalar, T: ST{ok: Float64{0.0}}}, 1144 {K: text.ListClose}, 1145 }, 1146 }, 1147 { 1148 in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, 1149 want: []R{ 1150 {K: text.Name}, 1151 {K: text.ListOpen}, 1152 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1153 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1154 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1155 {K: text.Scalar, T: ST{ok: Float32{10.0}}}, 1156 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1157 {K: text.Scalar, T: ST{ok: Float32{-1.0}}}, 1158 {K: text.Scalar, T: ST{ok: Float32{-10.0}}}, 1159 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1160 {K: text.Scalar, T: ST{ok: Float32{0.1e-3}}}, 1161 {K: text.Scalar, T: ST{ok: Float32{1.5e+5}}}, 1162 {K: text.Scalar, T: ST{ok: Float32{1.0e+10}}}, 1163 {K: text.Scalar, T: ST{ok: Float32{0.0}}}, 1164 {K: text.ListClose}, 1165 }, 1166 }, 1167 { 1168 in: `nums: [0.,1f,10F,1e1,1.10]`, 1169 want: []R{ 1170 {K: text.Name}, 1171 {K: text.ListOpen}, 1172 {K: text.Scalar, T: ST{nok: Int64{}}}, 1173 {K: text.Scalar, T: ST{nok: Int64{}}}, 1174 {K: text.Scalar, T: ST{nok: Int64{}}}, 1175 {K: text.Scalar, T: ST{nok: Int64{}}}, 1176 {K: text.Scalar, T: ST{nok: Int64{}}}, 1177 {K: text.ListClose}, 1178 }, 1179 }, 1180 { 1181 in: `nums: [0.,1f,10F,1e1,1.10]`, 1182 want: []R{ 1183 {K: text.Name}, 1184 {K: text.ListOpen}, 1185 {K: text.Scalar, T: ST{nok: Int32{}}}, 1186 {K: text.Scalar, T: ST{nok: Int32{}}}, 1187 {K: text.Scalar, T: ST{nok: Int32{}}}, 1188 {K: text.Scalar, T: ST{nok: Int32{}}}, 1189 {K: text.Scalar, T: ST{nok: Int32{}}}, 1190 {K: text.ListClose}, 1191 }, 1192 }, 1193 { 1194 in: `nums: [0.,1f,10F,1e1,1.10]`, 1195 want: []R{ 1196 {K: text.Name}, 1197 {K: text.ListOpen}, 1198 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1199 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1200 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1201 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1202 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1203 {K: text.ListClose}, 1204 }, 1205 }, 1206 { 1207 in: `nums: [0.,1f,10F,1e1,1.10]`, 1208 want: []R{ 1209 {K: text.Name}, 1210 {K: text.ListOpen}, 1211 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1212 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1213 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1214 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1215 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1216 {K: text.ListClose}, 1217 }, 1218 }, 1219 { 1220 in: `nums: [` + 1221 fmt.Sprintf("%g", math.MaxFloat32) + `,` + 1222 fmt.Sprintf("%g", -math.MaxFloat32) + `,` + 1223 fmt.Sprintf("%g", math.MaxFloat32*2) + `,` + 1224 fmt.Sprintf("%g", -math.MaxFloat32*2) + `,` + 1225 `3.59539e+308,` + // math.MaxFloat64 * 2 1226 `-3.59539e+308,` + // -math.MaxFloat64 * 2 1227 fmt.Sprintf("%d000", uint64(math.MaxUint64)) + 1228 `]`, 1229 want: []R{ 1230 {K: text.Name}, 1231 {K: text.ListOpen}, 1232 {K: text.Scalar, T: ST{ok: Float32{float32(math.MaxFloat32)}}}, 1233 {K: text.Scalar, T: ST{ok: Float32{float32(-math.MaxFloat32)}}}, 1234 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1235 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1236 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1237 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1238 {K: text.Scalar, T: ST{ok: Float32{float32(math.MaxUint64) * 1000}}}, 1239 {K: text.ListClose}, 1240 }, 1241 }, 1242 { 1243 in: `nums: [` + 1244 fmt.Sprintf("%g", math.MaxFloat64) + `,` + 1245 fmt.Sprintf("%g", -math.MaxFloat64) + `,` + 1246 `3.59539e+308,` + // math.MaxFloat64 * 2 1247 `-3.59539e+308,` + // -math.MaxFloat64 * 2 1248 fmt.Sprintf("%d000", uint64(math.MaxUint64)) + 1249 `]`, 1250 want: []R{ 1251 {K: text.Name}, 1252 {K: text.ListOpen}, 1253 {K: text.Scalar, T: ST{ok: Float64{math.MaxFloat64}}}, 1254 {K: text.Scalar, T: ST{ok: Float64{-math.MaxFloat64}}}, 1255 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1256 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1257 {K: text.Scalar, T: ST{ok: Float64{float64(math.MaxUint64) * 1000}}}, 1258 {K: text.ListClose}, 1259 }, 1260 }, 1261 { 1262 // -0 is only valid for signed types. It is not valid for unsigned types. 1263 in: `num: [-0, -0]`, 1264 want: []R{ 1265 {K: text.Name}, 1266 {K: text.ListOpen}, 1267 {K: text.Scalar, T: ST{nok: Uint32{}}}, 1268 {K: text.Scalar, T: ST{nok: Uint64{}}}, 1269 {K: text.ListClose}, 1270 }, 1271 }, 1272 { 1273 // -0 is only valid for signed types. It is not valid for unsigned types. 1274 in: `num: [-0, -0]`, 1275 want: []R{ 1276 {K: text.Name}, 1277 {K: text.ListOpen}, 1278 {K: text.Scalar, T: ST{ok: Int32{0}}}, 1279 {K: text.Scalar, T: ST{ok: Int64{0}}}, 1280 {K: text.ListClose}, 1281 }, 1282 }, 1283 { 1284 // Negative zeros on float64 should preserve sign bit. 1285 in: `num: [-0, -.0]`, 1286 want: []R{ 1287 {K: text.Name}, 1288 {K: text.ListOpen}, 1289 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1290 {K: text.Scalar, T: ST{ok: Float64{math.Copysign(0, -1)}}}, 1291 {K: text.ListClose}, 1292 }, 1293 }, 1294 { 1295 // Negative zeros on float32 should preserve sign bit. 1296 in: `num: [-0, -.0]`, 1297 want: []R{ 1298 {K: text.Name}, 1299 {K: text.ListOpen}, 1300 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1301 {K: text.Scalar, T: ST{ok: Float32{float32(math.Copysign(0, -1))}}}, 1302 {K: text.ListClose}, 1303 }, 1304 }, 1305 { 1306 in: `num: +0`, 1307 want: []R{ 1308 {K: text.Name}, 1309 {E: `invalid scalar value: +`}, 1310 }, 1311 }, 1312 { 1313 in: `num: 01.1234`, 1314 want: []R{ 1315 {K: text.Name}, 1316 {E: `invalid scalar value: 01.1234`}, 1317 }, 1318 }, 1319 { 1320 in: `num: 0x`, 1321 want: []R{ 1322 {K: text.Name}, 1323 {E: `invalid scalar value: 0x`}, 1324 }, 1325 }, 1326 { 1327 in: `num: 0xX`, 1328 want: []R{ 1329 {K: text.Name}, 1330 {E: `invalid scalar value: 0xX`}, 1331 }, 1332 }, 1333 { 1334 in: `num: 0800`, 1335 want: []R{ 1336 {K: text.Name}, 1337 {E: `invalid scalar value: 0800`}, 1338 }, 1339 }, 1340 { 1341 in: `num: 1.`, 1342 want: []R{ 1343 {K: text.Name}, 1344 {K: text.Scalar, T: ST{ok: Float32{1.0}}}, 1345 }, 1346 }, 1347 { 1348 in: `num: -.`, 1349 want: []R{ 1350 {K: text.Name}, 1351 {E: `invalid scalar value: -.`}, 1352 }, 1353 }, 1354 1355 // Float special literal values, case-insensitive match. 1356 { 1357 in: `name:[nan, NaN, Nan, NAN]`, 1358 want: []R{ 1359 {K: text.Name}, 1360 {K: text.ListOpen}, 1361 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1362 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1363 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1364 {K: text.Scalar, T: ST{ok: Float64{math.NaN()}}}, 1365 {K: text.ListClose}, 1366 }, 1367 }, 1368 { 1369 in: `name:[inf, INF, infinity, Infinity, INFinity]`, 1370 want: []R{ 1371 {K: text.Name}, 1372 {K: text.ListOpen}, 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.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1376 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1377 {K: text.Scalar, T: ST{ok: Float64{math.Inf(1)}}}, 1378 {K: text.ListClose}, 1379 }, 1380 }, 1381 { 1382 in: `name:[-inf, -INF, -infinity, -Infinity, -INFinity]`, 1383 want: []R{ 1384 {K: text.Name}, 1385 {K: text.ListOpen}, 1386 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1387 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1388 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1389 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1390 {K: text.Scalar, T: ST{ok: Float64{math.Inf(-1)}}}, 1391 {K: text.ListClose}, 1392 }, 1393 }, 1394 { 1395 in: `name:[nan, NaN, Nan, NAN]`, 1396 want: []R{ 1397 {K: text.Name}, 1398 {K: text.ListOpen}, 1399 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1400 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1401 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1402 {K: text.Scalar, T: ST{ok: Float32{float32(math.NaN())}}}, 1403 {K: text.ListClose}, 1404 }, 1405 }, 1406 { 1407 in: `name:[inf, INF, infinity, Infinity, INFinity]`, 1408 want: []R{ 1409 {K: text.Name}, 1410 {K: text.ListOpen}, 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.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1414 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1415 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(1))}}}, 1416 {K: text.ListClose}, 1417 }, 1418 }, 1419 { 1420 in: `name:[-inf, -INF, -infinity, -Infinity, -INFinity]`, 1421 want: []R{ 1422 {K: text.Name}, 1423 {K: text.ListOpen}, 1424 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1425 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1426 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1427 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1428 {K: text.Scalar, T: ST{ok: Float32{float32(math.Inf(-1))}}}, 1429 {K: text.ListClose}, 1430 }, 1431 }, 1432 { 1433 // C++ permits this, but we currently reject this. It is easy to add 1434 // if needed. 1435 in: `name: -nan`, 1436 want: []R{ 1437 {K: text.Name}, 1438 {K: text.Scalar, T: ST{nok: Float64{}}}, 1439 }, 1440 }, 1441 // Messages. 1442 { 1443 in: `m: {}`, 1444 want: []R{ 1445 {K: text.Name}, 1446 {K: text.MessageOpen}, 1447 {K: text.MessageClose}, 1448 {K: text.EOF}, 1449 }, 1450 }, 1451 { 1452 in: `m: <>`, 1453 want: []R{ 1454 {K: text.Name}, 1455 {K: text.MessageOpen}, 1456 {K: text.MessageClose}, 1457 {K: text.EOF}, 1458 }, 1459 }, 1460 { 1461 in: space + `m {` + space + "\n# comment\n" + `}` + space, 1462 want: []R{ 1463 {K: text.Name}, 1464 {K: text.MessageOpen}, 1465 {K: text.MessageClose}, 1466 }, 1467 }, 1468 { 1469 in: `m { foo: < bar: "hello" > }`, 1470 want: []R{ 1471 {K: text.Name, RS: "m"}, 1472 {K: text.MessageOpen}, 1473 1474 {K: text.Name, RS: "foo"}, 1475 {K: text.MessageOpen}, 1476 1477 {K: text.Name, RS: "bar"}, 1478 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1479 1480 {K: text.MessageClose}, 1481 1482 {K: text.MessageClose}, 1483 }, 1484 }, 1485 { 1486 in: `list [ <s:"hello">, {s:"world"} ]`, 1487 want: []R{ 1488 {K: text.Name, RS: "list"}, 1489 {K: text.ListOpen}, 1490 1491 {K: text.MessageOpen}, 1492 {K: text.Name, RS: "s"}, 1493 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1494 {K: text.MessageClose}, 1495 1496 {K: text.MessageOpen}, 1497 {K: text.Name, RS: "s"}, 1498 {K: text.Scalar, T: ST{ok: Str{"world"}}}, 1499 {K: text.MessageClose}, 1500 1501 {K: text.ListClose}, 1502 {K: text.EOF}, 1503 }, 1504 }, 1505 { 1506 in: `m: { >`, 1507 want: []R{ 1508 {K: text.Name}, 1509 {K: text.MessageOpen}, 1510 {E: `mismatched close character '>'`}, 1511 }, 1512 }, 1513 { 1514 in: `m: <s: "hello"}`, 1515 want: []R{ 1516 {K: text.Name}, 1517 {K: text.MessageOpen}, 1518 1519 {K: text.Name}, 1520 {K: text.Scalar, T: ST{ok: Str{"hello"}}}, 1521 1522 {E: `mismatched close character '}'`}, 1523 }, 1524 }, 1525 { 1526 in: `{}`, 1527 want: []R{{E: `invalid field name: {`}}, 1528 }, 1529 { 1530 in: ` 1531m: { 1532 foo: true; 1533 bar: { 1534 enum: ENUM 1535 list: [ < >, { } ] ; 1536 } 1537 [qux]: "end" 1538} 1539 `, 1540 want: []R{ 1541 {K: text.Name}, 1542 {K: text.MessageOpen}, 1543 1544 {K: text.Name, RS: "foo"}, 1545 {K: text.Scalar, T: ST{ok: Bool{true}}}, 1546 1547 {K: text.Name, RS: "bar"}, 1548 {K: text.MessageOpen}, 1549 1550 {K: text.Name, RS: "enum"}, 1551 {K: text.Scalar, T: ST{ok: Enum{"ENUM"}}}, 1552 1553 {K: text.Name, RS: "list"}, 1554 {K: text.ListOpen}, 1555 {K: text.MessageOpen}, 1556 {K: text.MessageClose}, 1557 {K: text.MessageOpen}, 1558 {K: text.MessageClose}, 1559 {K: text.ListClose}, 1560 1561 {K: text.MessageClose}, 1562 1563 {K: text.Name, RS: "[qux]"}, 1564 {K: text.Scalar, T: ST{ok: Str{"end"}}}, 1565 1566 {K: text.MessageClose}, 1567 {K: text.EOF}, 1568 }, 1569 }, 1570 1571 // Other syntax errors. 1572 { 1573 in: "x: -", 1574 want: []R{ 1575 {K: text.Name}, 1576 {E: `syntax error (line 1:4): invalid scalar value: -`}, 1577 }, 1578 }, 1579 { 1580 in: "x:[\"\"x", 1581 want: []R{ 1582 {K: text.Name}, 1583 {K: text.ListOpen}, 1584 {K: text.Scalar, T: ST{ok: Str{""}}, P: 3}, 1585 {E: `syntax error (line 1:7)`}, 1586 }, 1587 }, 1588 { 1589 in: "x:\n\n[\"\"x", 1590 want: []R{ 1591 {K: text.Name}, 1592 {K: text.ListOpen}, 1593 {K: text.Scalar, T: ST{ok: Str{""}}, P: 5}, 1594 {E: `syntax error (line 3:7)`}, 1595 }, 1596 }, 1597 { 1598 // multi-rune emojis; could be column:8 1599 in: "x:[\"\"x", 1600 want: []R{ 1601 {K: text.Name}, 1602 {K: text.ListOpen}, 1603 {K: text.Scalar, T: ST{ok: Str{""}}, P: 3}, 1604 {E: `syntax error (line 1:10)`}, 1605 }, 1606 }, 1607 } 1608 1609 for _, tc := range tests { 1610 t.Run("", func(t *testing.T) { 1611 tc := tc 1612 in := []byte(tc.in) 1613 dec := text.NewDecoder(in[:len(in):len(in)]) 1614 for i, want := range tc.want { 1615 peekTok, peekErr := dec.Peek() 1616 tok, err := dec.Read() 1617 if err != nil { 1618 if want.E == "" { 1619 errorf(t, tc.in, "Read() got unexpected error: %v", err) 1620 } else if !strings.Contains(err.Error(), want.E) { 1621 errorf(t, tc.in, "Read() got %q, want %q", err, want.E) 1622 } 1623 return 1624 } 1625 if want.E != "" { 1626 errorf(t, tc.in, "Read() got nil error, want %q", want.E) 1627 return 1628 } 1629 gotK := tok.Kind() 1630 if gotK != want.K { 1631 errorf(t, tc.in, "Read() got %v, want %v", gotK, want.K) 1632 return 1633 } 1634 checkToken(t, tok, i, want, tc.in) 1635 if !cmp.Equal(tok, peekTok, cmp.Comparer(text.TokenEquals)) { 1636 errorf(t, tc.in, "Peek() %+v != Read() token %+v", peekTok, tok) 1637 } 1638 if err != peekErr { 1639 errorf(t, tc.in, "Peek() error %v != Read() error %v", err, peekErr) 1640 } 1641 } 1642 }) 1643 } 1644} 1645 1646func checkToken(t *testing.T, tok text.Token, idx int, r R, in string) { 1647 // Validate Token.Pos() if R.P is set. 1648 if r.P > 0 { 1649 got := tok.Pos() 1650 if got != r.P { 1651 errorf(t, in, "want#%d: Token.Pos() got %v want %v", idx, got, r.P) 1652 } 1653 } 1654 1655 // Validate Token.RawString if R.RS is set. 1656 if len(r.RS) > 0 { 1657 got := tok.RawString() 1658 if got != r.RS { 1659 errorf(t, in, "want#%d: Token.RawString() got %v want %v", idx, got, r.P) 1660 } 1661 } 1662 1663 // Skip checking for Token details if r.T is not set. 1664 if r.T == nil { 1665 return 1666 } 1667 1668 switch tok.Kind() { 1669 case text.Name: 1670 want := r.T.(NT) 1671 kind := tok.NameKind() 1672 if kind != want.K { 1673 errorf(t, in, "want#%d: Token.NameKind() got %v want %v", idx, kind, want.K) 1674 return 1675 } 1676 switch kind { 1677 case text.IdentName: 1678 got := tok.IdentName() 1679 if got != want.S { 1680 errorf(t, in, "want#%d: Token.IdentName() got %v want %v", idx, got, want.S) 1681 } 1682 case text.TypeName: 1683 got := tok.TypeName() 1684 if got != want.S { 1685 errorf(t, in, "want#%d: Token.TypeName() got %v want %v", idx, got, want.S) 1686 } 1687 case text.FieldNumber: 1688 got := tok.FieldNumber() 1689 if got != want.N { 1690 errorf(t, in, "want#%d: Token.FieldNumber() got %v want %v", idx, got, want.N) 1691 } 1692 } 1693 1694 case text.Scalar: 1695 want := r.T.(ST) 1696 if ok := want.ok; ok != nil { 1697 if err := ok.checkOk(tok); err != "" { 1698 errorf(t, in, "want#%d: %s", idx, err) 1699 } 1700 } 1701 if nok := want.nok; nok != nil { 1702 if err := nok.checkNok(tok); err != "" { 1703 errorf(t, in, "want#%d: %s", idx, err) 1704 } 1705 } 1706 } 1707} 1708 1709func errorf(t *testing.T, in string, fmtStr string, args ...interface{}) { 1710 t.Helper() 1711 vargs := []interface{}{in} 1712 for _, arg := range args { 1713 vargs = append(vargs, arg) 1714 } 1715 t.Errorf("input:\n%s\n~end~\n"+fmtStr, vargs...) 1716} 1717 1718func TestUnmarshalString(t *testing.T) { 1719 tests := []struct { 1720 in string 1721 // want is expected string result. 1722 want string 1723 // err is expected error substring from calling DecodeString if set. 1724 err string 1725 }{ 1726 { 1727 in: func() string { 1728 var b []byte 1729 for i := 0; i < utf8.RuneSelf; i++ { 1730 switch i { 1731 case 0, '\\', '\n', '\'': // these must be escaped, so ignore them 1732 default: 1733 b = append(b, byte(i)) 1734 } 1735 } 1736 return "'" + string(b) + "'" 1737 }(), 1738 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", 1739 }, 1740 { 1741 in: "'\xde\xad\xbe\xef'", 1742 err: `invalid UTF-8 detected`, 1743 }, 1744 { 1745 // Valid UTF-8 wire encoding, but sub-optimal encoding. 1746 in: "'\xc0\x80'", 1747 err: "invalid UTF-8 detected", 1748 }, 1749 { 1750 // Valid UTF-8 wire encoding, but invalid rune (surrogate pair). 1751 in: "'\xed\xa0\x80'", 1752 err: "invalid UTF-8 detected", 1753 }, 1754 { 1755 // Valid UTF-8 wire encoding, but invalid rune (above max rune). 1756 in: "'\xf7\xbf\xbf\xbf'", 1757 err: "invalid UTF-8 detected", 1758 }, 1759 { 1760 // Valid UTF-8 wire encoding of the RuneError rune. 1761 in: "'\xef\xbf\xbd'", 1762 want: string(utf8.RuneError), 1763 }, 1764 { 1765 in: "'hello\u1234world'", 1766 want: "hello\u1234world", 1767 }, 1768 { 1769 in: `'\"\'\\\?\a\b\n\r\t\v\f\1\12\123\xA\xaB\x12\uAb8f\U0010FFFF'`, 1770 want: "\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff", 1771 }, 1772 { 1773 in: `str: '\8'`, 1774 err: `invalid escape code "\\8" in string`, 1775 }, 1776 { 1777 in: `'\1x'`, 1778 want: "\001x", 1779 }, 1780 { 1781 in: `'\12x'`, 1782 want: "\012x", 1783 }, 1784 { 1785 in: `'\123x'`, 1786 want: "\123x", 1787 }, 1788 { 1789 in: `'\1234x'`, 1790 want: "\1234x", 1791 }, 1792 { 1793 in: `'\1'`, 1794 want: "\001", 1795 }, 1796 { 1797 in: `'\12'`, 1798 want: "\012", 1799 }, 1800 { 1801 in: `'\123'`, 1802 want: "\123", 1803 }, 1804 { 1805 in: `'\1234'`, 1806 want: "\1234", 1807 }, 1808 { 1809 in: `'\377'`, 1810 want: "\377", 1811 }, 1812 { 1813 // Overflow octal escape. 1814 in: `'\400'`, 1815 err: `invalid octal escape code "\\400" in string`, 1816 }, 1817 { 1818 in: `'\xfx'`, 1819 want: "\x0fx", 1820 }, 1821 { 1822 in: `'\xffx'`, 1823 want: "\xffx", 1824 }, 1825 { 1826 in: `'\xfffx'`, 1827 want: "\xfffx", 1828 }, 1829 { 1830 in: `'\xf'`, 1831 want: "\x0f", 1832 }, 1833 { 1834 in: `'\xff'`, 1835 want: "\xff", 1836 }, 1837 { 1838 in: `'\xfff'`, 1839 want: "\xfff", 1840 }, 1841 { 1842 in: `'\xz'`, 1843 err: `invalid hex escape code "\\x" in string`, 1844 }, 1845 { 1846 in: `'\uPo'`, 1847 err: eofErr, 1848 }, 1849 { 1850 in: `'\uPoo'`, 1851 err: `invalid Unicode escape code "\\uPoo'" in string`, 1852 }, 1853 { 1854 in: `str: '\uPoop'`, 1855 err: `invalid Unicode escape code "\\uPoop" in string`, 1856 }, 1857 { 1858 // Unmatched surrogate pair. 1859 in: `str: '\uDEAD'`, 1860 err: `unexpected EOF`, // trying to reader other half 1861 }, 1862 { 1863 // Surrogate pair with invalid other half. 1864 in: `str: '\uDEAD\u0000'`, 1865 err: `invalid Unicode escape code "\\u0000" in string`, 1866 }, 1867 { 1868 // Properly matched surrogate pair. 1869 in: `'\uD800\uDEAD'`, 1870 want: "", 1871 }, 1872 { 1873 // Overflow on Unicode rune. 1874 in: `'\U00110000'`, 1875 err: `invalid Unicode escape code "\\U00110000" in string`, 1876 }, 1877 { 1878 in: `'\z'`, 1879 err: `invalid escape code "\\z" in string`, 1880 }, 1881 { 1882 // Strings cannot have NUL literal since C-style strings forbid them. 1883 in: "'\x00'", 1884 err: `invalid character '\x00' in string`, 1885 }, 1886 { 1887 // Strings cannot have newline literal. The C++ permits them if an 1888 // option is specified to allow them. In Go, we always forbid them. 1889 in: "'\n'", 1890 err: `invalid character '\n' in string`, 1891 }, 1892 } 1893 1894 for _, tc := range tests { 1895 t.Run("", func(t *testing.T) { 1896 got, err := text.UnmarshalString(tc.in) 1897 if err != nil { 1898 if tc.err == "" { 1899 errorf(t, tc.in, "UnmarshalString() got unexpected error: %q", err) 1900 } else if !strings.Contains(err.Error(), tc.err) { 1901 errorf(t, tc.in, "UnmarshalString() error got %q, want %q", err, tc.err) 1902 } 1903 return 1904 } 1905 if tc.err != "" { 1906 errorf(t, tc.in, "UnmarshalString() got nil error, want %q", tc.err) 1907 return 1908 } 1909 if got != tc.want { 1910 errorf(t, tc.in, "UnmarshalString()\n[got]\n%s\n[want]\n%s", got, tc.want) 1911 } 1912 }) 1913 } 1914} 1915 1916// Tests line and column number produced by Decoder.Position. 1917func TestPosition(t *testing.T) { 1918 dec := text.NewDecoder([]byte("0123456789\n12345\n789")) 1919 1920 tests := []struct { 1921 pos int 1922 row int 1923 col int 1924 }{ 1925 { 1926 pos: 0, 1927 row: 1, 1928 col: 1, 1929 }, 1930 { 1931 pos: 10, 1932 row: 1, 1933 col: 11, 1934 }, 1935 { 1936 pos: 11, 1937 row: 2, 1938 col: 1, 1939 }, 1940 { 1941 pos: 18, 1942 row: 3, 1943 col: 2, 1944 }, 1945 } 1946 1947 for _, tc := range tests { 1948 t.Run("", func(t *testing.T) { 1949 row, col := dec.Position(tc.pos) 1950 if row != tc.row || col != tc.col { 1951 t.Errorf("Position(%d) got (%d,%d) want (%d,%d)", tc.pos, row, col, tc.row, tc.col) 1952 } 1953 }) 1954 } 1955} 1956