1// Copyright 2014 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package parser 16 17import ( 18 "bytes" 19 "errors" 20 "reflect" 21 "strconv" 22 "strings" 23 "testing" 24 "text/scanner" 25) 26 27func mkpos(offset, line, column int) scanner.Position { 28 return scanner.Position{ 29 Offset: offset, 30 Line: line, 31 Column: column, 32 } 33} 34 35var validParseTestCases = []struct { 36 input string 37 defs []Definition 38 comments []*CommentGroup 39}{ 40 {` 41 foo {} 42 `, 43 []Definition{ 44 &Module{ 45 Type: "foo", 46 TypePos: mkpos(3, 2, 3), 47 Map: Map{ 48 LBracePos: mkpos(7, 2, 7), 49 RBracePos: mkpos(8, 2, 8), 50 }, 51 }, 52 }, 53 nil, 54 }, 55 56 {` 57 foo { 58 name: "abc", 59 } 60 `, 61 []Definition{ 62 &Module{ 63 Type: "foo", 64 TypePos: mkpos(3, 2, 3), 65 Map: Map{ 66 LBracePos: mkpos(7, 2, 7), 67 RBracePos: mkpos(27, 4, 3), 68 Properties: []*Property{ 69 { 70 Name: "name", 71 NamePos: mkpos(12, 3, 4), 72 ColonPos: mkpos(16, 3, 8), 73 Value: &String{ 74 LiteralPos: mkpos(18, 3, 10), 75 Value: "abc", 76 }, 77 }, 78 }, 79 }, 80 }, 81 }, 82 nil, 83 }, 84 85 {` 86 foo { 87 isGood: true, 88 } 89 `, 90 []Definition{ 91 &Module{ 92 Type: "foo", 93 TypePos: mkpos(3, 2, 3), 94 Map: Map{ 95 LBracePos: mkpos(7, 2, 7), 96 RBracePos: mkpos(28, 4, 3), 97 Properties: []*Property{ 98 { 99 Name: "isGood", 100 NamePos: mkpos(12, 3, 4), 101 ColonPos: mkpos(18, 3, 10), 102 Value: &Bool{ 103 LiteralPos: mkpos(20, 3, 12), 104 Value: true, 105 Token: "true", 106 }, 107 }, 108 }, 109 }, 110 }, 111 }, 112 nil, 113 }, 114 115 {` 116 foo { 117 num: 4, 118 } 119 `, 120 []Definition{ 121 &Module{ 122 Type: "foo", 123 TypePos: mkpos(3, 2, 3), 124 Map: Map{ 125 LBracePos: mkpos(7, 2, 7), 126 RBracePos: mkpos(22, 4, 3), 127 Properties: []*Property{ 128 { 129 Name: "num", 130 NamePos: mkpos(12, 3, 4), 131 ColonPos: mkpos(15, 3, 7), 132 Value: &Int64{ 133 LiteralPos: mkpos(17, 3, 9), 134 Value: 4, 135 Token: "4", 136 }, 137 }, 138 }, 139 }, 140 }, 141 }, 142 nil, 143 }, 144 145 {` 146 foo { 147 stuff: ["asdf", "jkl;", "qwert", 148 "uiop", ` + "`bnm,\n`" + 149 `] 150 } 151 `, 152 []Definition{ 153 &Module{ 154 Type: "foo", 155 TypePos: mkpos(3, 2, 3), 156 Map: Map{ 157 LBracePos: mkpos(7, 2, 7), 158 RBracePos: mkpos(68, 6, 3), 159 Properties: []*Property{ 160 { 161 Name: "stuff", 162 NamePos: mkpos(12, 3, 4), 163 ColonPos: mkpos(17, 3, 9), 164 Value: &List{ 165 LBracePos: mkpos(19, 3, 11), 166 RBracePos: mkpos(64, 5, 2), 167 Values: []Expression{ 168 &String{ 169 LiteralPos: mkpos(20, 3, 12), 170 Value: "asdf", 171 }, 172 &String{ 173 LiteralPos: mkpos(28, 3, 20), 174 Value: "jkl;", 175 }, 176 &String{ 177 LiteralPos: mkpos(36, 3, 28), 178 Value: "qwert", 179 }, 180 &String{ 181 LiteralPos: mkpos(49, 4, 5), 182 Value: "uiop", 183 }, 184 &String{ 185 LiteralPos: mkpos(57, 4, 13), 186 Value: "bnm,\n", 187 }, 188 }, 189 }, 190 }, 191 }, 192 }, 193 }, 194 }, 195 nil, 196 }, 197 198 { 199 ` 200 foo { 201 list_of_maps: [ 202 { 203 var: true, 204 name: "a", 205 }, 206 { 207 var: false, 208 name: "b", 209 }, 210 ], 211 } 212`, 213 []Definition{ 214 &Module{ 215 Type: "foo", 216 TypePos: mkpos(3, 2, 3), 217 Map: Map{ 218 LBracePos: mkpos(7, 2, 7), 219 RBracePos: mkpos(127, 13, 3), 220 Properties: []*Property{ 221 { 222 Name: "list_of_maps", 223 NamePos: mkpos(12, 3, 4), 224 ColonPos: mkpos(24, 3, 16), 225 Value: &List{ 226 LBracePos: mkpos(26, 3, 18), 227 RBracePos: mkpos(122, 12, 4), 228 Values: []Expression{ 229 &Map{ 230 LBracePos: mkpos(32, 4, 5), 231 RBracePos: mkpos(70, 7, 5), 232 Properties: []*Property{ 233 { 234 Name: "var", 235 NamePos: mkpos(39, 5, 6), 236 ColonPos: mkpos(42, 5, 9), 237 Value: &Bool{ 238 LiteralPos: mkpos(44, 5, 11), 239 Value: true, 240 Token: "true", 241 }, 242 }, 243 { 244 Name: "name", 245 NamePos: mkpos(55, 6, 6), 246 ColonPos: mkpos(59, 6, 10), 247 Value: &String{ 248 LiteralPos: mkpos(61, 6, 12), 249 Value: "a", 250 }, 251 }, 252 }, 253 }, 254 &Map{ 255 LBracePos: mkpos(77, 8, 5), 256 RBracePos: mkpos(116, 11, 5), 257 Properties: []*Property{ 258 { 259 Name: "var", 260 NamePos: mkpos(84, 9, 6), 261 ColonPos: mkpos(87, 9, 9), 262 Value: &Bool{ 263 LiteralPos: mkpos(89, 9, 11), 264 Value: false, 265 Token: "false", 266 }, 267 }, 268 { 269 Name: "name", 270 NamePos: mkpos(101, 10, 6), 271 ColonPos: mkpos(105, 10, 10), 272 Value: &String{ 273 LiteralPos: mkpos(107, 10, 12), 274 Value: "b", 275 }, 276 }, 277 }, 278 }, 279 }, 280 }, 281 }, 282 }, 283 }, 284 }, 285 }, 286 nil, 287 }, 288 { 289 ` 290 foo { 291 list_of_lists: [ 292 [ "a", "b" ], 293 [ "c", "d" ] 294 ], 295 } 296`, 297 []Definition{ 298 &Module{ 299 Type: "foo", 300 TypePos: mkpos(3, 2, 3), 301 Map: Map{ 302 LBracePos: mkpos(7, 2, 7), 303 RBracePos: mkpos(72, 7, 3), 304 Properties: []*Property{ 305 { 306 Name: "list_of_lists", 307 NamePos: mkpos(12, 3, 4), 308 ColonPos: mkpos(25, 3, 17), 309 Value: &List{ 310 LBracePos: mkpos(27, 3, 19), 311 RBracePos: mkpos(67, 6, 4), 312 Values: []Expression{ 313 &List{ 314 LBracePos: mkpos(33, 4, 5), 315 RBracePos: mkpos(44, 4, 16), 316 Values: []Expression{ 317 &String{ 318 LiteralPos: mkpos(35, 4, 7), 319 Value: "a", 320 }, 321 &String{ 322 LiteralPos: mkpos(40, 4, 12), 323 Value: "b", 324 }, 325 }, 326 }, 327 &List{ 328 LBracePos: mkpos(51, 5, 5), 329 RBracePos: mkpos(62, 5, 16), 330 Values: []Expression{ 331 &String{ 332 LiteralPos: mkpos(53, 5, 7), 333 Value: "c", 334 }, 335 &String{ 336 LiteralPos: mkpos(58, 5, 12), 337 Value: "d", 338 }, 339 }, 340 }, 341 }, 342 }, 343 }, 344 }, 345 }, 346 }, 347 }, 348 nil, 349 }, 350 {` 351 foo { 352 stuff: { 353 isGood: true, 354 name: "bar", 355 num: 36, 356 } 357 } 358 `, 359 []Definition{ 360 &Module{ 361 Type: "foo", 362 TypePos: mkpos(3, 2, 3), 363 Map: Map{ 364 LBracePos: mkpos(7, 2, 7), 365 RBracePos: mkpos(76, 8, 3), 366 Properties: []*Property{ 367 { 368 Name: "stuff", 369 NamePos: mkpos(12, 3, 4), 370 ColonPos: mkpos(17, 3, 9), 371 Value: &Map{ 372 LBracePos: mkpos(19, 3, 11), 373 RBracePos: mkpos(72, 7, 4), 374 Properties: []*Property{ 375 { 376 Name: "isGood", 377 NamePos: mkpos(25, 4, 5), 378 ColonPos: mkpos(31, 4, 11), 379 Value: &Bool{ 380 LiteralPos: mkpos(33, 4, 13), 381 Value: true, 382 Token: "true", 383 }, 384 }, 385 { 386 Name: "name", 387 NamePos: mkpos(43, 5, 5), 388 ColonPos: mkpos(47, 5, 9), 389 Value: &String{ 390 LiteralPos: mkpos(49, 5, 11), 391 Value: "bar", 392 }, 393 }, 394 { 395 Name: "num", 396 NamePos: mkpos(60, 6, 5), 397 ColonPos: mkpos(63, 6, 8), 398 Value: &Int64{ 399 LiteralPos: mkpos(65, 6, 10), 400 Value: 36, 401 Token: "36", 402 }, 403 }, 404 }, 405 }, 406 }, 407 }, 408 }, 409 }, 410 }, 411 nil, 412 }, 413 414 {` 415 // comment1 416 foo /* test */ { 417 // comment2 418 isGood: true, // comment3 419 } 420 `, 421 []Definition{ 422 &Module{ 423 Type: "foo", 424 TypePos: mkpos(17, 3, 3), 425 Map: Map{ 426 LBracePos: mkpos(32, 3, 18), 427 RBracePos: mkpos(81, 6, 3), 428 Properties: []*Property{ 429 { 430 Name: "isGood", 431 NamePos: mkpos(52, 5, 4), 432 ColonPos: mkpos(58, 5, 10), 433 Value: &Bool{ 434 LiteralPos: mkpos(60, 5, 12), 435 Value: true, 436 Token: "true", 437 }, 438 }, 439 }, 440 }, 441 }, 442 }, 443 []*CommentGroup{ 444 { 445 Comments: []*Comment{ 446 &Comment{ 447 Comment: []string{"// comment1"}, 448 Slash: mkpos(3, 2, 3), 449 }, 450 }, 451 }, 452 { 453 Comments: []*Comment{ 454 &Comment{ 455 Comment: []string{"/* test */"}, 456 Slash: mkpos(21, 3, 7), 457 }, 458 }, 459 }, 460 { 461 Comments: []*Comment{ 462 &Comment{ 463 Comment: []string{"// comment2"}, 464 Slash: mkpos(37, 4, 4), 465 }, 466 }, 467 }, 468 { 469 Comments: []*Comment{ 470 &Comment{ 471 Comment: []string{"// comment3"}, 472 Slash: mkpos(67, 5, 19), 473 }, 474 }, 475 }, 476 }, 477 }, 478 479 {` 480 foo { 481 name: "abc", 482 num: 4, 483 } 484 485 bar { 486 name: "def", 487 num: -5, 488 } 489 `, 490 []Definition{ 491 &Module{ 492 Type: "foo", 493 TypePos: mkpos(3, 2, 3), 494 Map: Map{ 495 LBracePos: mkpos(7, 2, 7), 496 RBracePos: mkpos(38, 5, 3), 497 Properties: []*Property{ 498 { 499 Name: "name", 500 NamePos: mkpos(12, 3, 4), 501 ColonPos: mkpos(16, 3, 8), 502 Value: &String{ 503 LiteralPos: mkpos(18, 3, 10), 504 Value: "abc", 505 }, 506 }, 507 { 508 Name: "num", 509 NamePos: mkpos(28, 4, 4), 510 ColonPos: mkpos(31, 4, 7), 511 Value: &Int64{ 512 LiteralPos: mkpos(33, 4, 9), 513 Value: 4, 514 Token: "4", 515 }, 516 }, 517 }, 518 }, 519 }, 520 &Module{ 521 Type: "bar", 522 TypePos: mkpos(43, 7, 3), 523 Map: Map{ 524 LBracePos: mkpos(47, 7, 7), 525 RBracePos: mkpos(79, 10, 3), 526 Properties: []*Property{ 527 { 528 Name: "name", 529 NamePos: mkpos(52, 8, 4), 530 ColonPos: mkpos(56, 8, 8), 531 Value: &String{ 532 LiteralPos: mkpos(58, 8, 10), 533 Value: "def", 534 }, 535 }, 536 { 537 Name: "num", 538 NamePos: mkpos(68, 9, 4), 539 ColonPos: mkpos(71, 9, 7), 540 Value: &Int64{ 541 LiteralPos: mkpos(73, 9, 9), 542 Value: -5, 543 Token: "-5", 544 }, 545 }, 546 }, 547 }, 548 }, 549 }, 550 nil, 551 }, 552 553 {` 554 foo = "stuff" 555 bar = foo 556 baz = foo + bar 557 boo = baz 558 boo += foo 559 `, 560 []Definition{ 561 &Assignment{ 562 Name: "foo", 563 NamePos: mkpos(3, 2, 3), 564 EqualsPos: mkpos(7, 2, 7), 565 Value: &String{ 566 LiteralPos: mkpos(9, 2, 9), 567 Value: "stuff", 568 }, 569 Assigner: "=", 570 }, 571 &Assignment{ 572 Name: "bar", 573 NamePos: mkpos(19, 3, 3), 574 EqualsPos: mkpos(23, 3, 7), 575 Value: &Variable{ 576 Name: "foo", 577 NamePos: mkpos(25, 3, 9), 578 }, 579 Assigner: "=", 580 }, 581 &Assignment{ 582 Name: "baz", 583 NamePos: mkpos(31, 4, 3), 584 EqualsPos: mkpos(35, 4, 7), 585 Value: &Operator{ 586 OperatorPos: mkpos(41, 4, 13), 587 Operator: '+', 588 Args: [2]Expression{ 589 &Variable{ 590 Name: "foo", 591 NamePos: mkpos(37, 4, 9), 592 }, 593 &Variable{ 594 Name: "bar", 595 NamePos: mkpos(43, 4, 15), 596 }, 597 }, 598 }, 599 Assigner: "=", 600 }, 601 &Assignment{ 602 Name: "boo", 603 NamePos: mkpos(49, 5, 3), 604 EqualsPos: mkpos(53, 5, 7), 605 Value: &Variable{ 606 Name: "baz", 607 NamePos: mkpos(55, 5, 9), 608 }, 609 Assigner: "=", 610 }, 611 &Assignment{ 612 Name: "boo", 613 NamePos: mkpos(61, 6, 3), 614 EqualsPos: mkpos(66, 6, 8), 615 Value: &Variable{ 616 Name: "foo", 617 NamePos: mkpos(68, 6, 10), 618 }, 619 Assigner: "+=", 620 }, 621 }, 622 nil, 623 }, 624 625 {` 626 baz = -4 + -5 + 6 627 `, 628 []Definition{ 629 &Assignment{ 630 Name: "baz", 631 NamePos: mkpos(3, 2, 3), 632 EqualsPos: mkpos(7, 2, 7), 633 Value: &Operator{ 634 OperatorPos: mkpos(12, 2, 12), 635 Operator: '+', 636 Args: [2]Expression{ 637 &Int64{ 638 LiteralPos: mkpos(9, 2, 9), 639 Value: -4, 640 Token: "-4", 641 }, 642 &Operator{ 643 OperatorPos: mkpos(17, 2, 17), 644 Operator: '+', 645 Args: [2]Expression{ 646 &Int64{ 647 LiteralPos: mkpos(14, 2, 14), 648 Value: -5, 649 Token: "-5", 650 }, 651 &Int64{ 652 LiteralPos: mkpos(19, 2, 19), 653 Value: 6, 654 Token: "6", 655 }, 656 }, 657 }, 658 }, 659 }, 660 Assigner: "=", 661 }, 662 }, 663 nil, 664 }, 665 666 {` 667 foo = 1000000 668 bar = foo 669 baz = foo + bar 670 boo = baz 671 boo += foo 672 `, 673 []Definition{ 674 &Assignment{ 675 Name: "foo", 676 NamePos: mkpos(3, 2, 3), 677 EqualsPos: mkpos(7, 2, 7), 678 Value: &Int64{ 679 LiteralPos: mkpos(9, 2, 9), 680 Value: 1000000, 681 Token: "1000000", 682 }, 683 Assigner: "=", 684 }, 685 &Assignment{ 686 Name: "bar", 687 NamePos: mkpos(19, 3, 3), 688 EqualsPos: mkpos(23, 3, 7), 689 Value: &Variable{ 690 Name: "foo", 691 NamePos: mkpos(25, 3, 9), 692 }, 693 Assigner: "=", 694 }, 695 &Assignment{ 696 Name: "baz", 697 NamePos: mkpos(31, 4, 3), 698 EqualsPos: mkpos(35, 4, 7), 699 Value: &Operator{ 700 OperatorPos: mkpos(41, 4, 13), 701 Operator: '+', 702 Args: [2]Expression{ 703 &Variable{ 704 Name: "foo", 705 NamePos: mkpos(37, 4, 9), 706 }, 707 &Variable{ 708 Name: "bar", 709 NamePos: mkpos(43, 4, 15), 710 }, 711 }, 712 }, 713 Assigner: "=", 714 }, 715 &Assignment{ 716 Name: "boo", 717 NamePos: mkpos(49, 5, 3), 718 EqualsPos: mkpos(53, 5, 7), 719 Value: &Variable{ 720 Name: "baz", 721 NamePos: mkpos(55, 5, 9), 722 }, 723 Assigner: "=", 724 }, 725 &Assignment{ 726 Name: "boo", 727 NamePos: mkpos(61, 6, 3), 728 EqualsPos: mkpos(66, 6, 8), 729 Value: &Variable{ 730 Name: "foo", 731 NamePos: mkpos(68, 6, 10), 732 }, 733 Assigner: "+=", 734 }, 735 }, 736 nil, 737 }, 738 739 {` 740 // comment1 741 // comment2 742 743 /* comment3 744 comment4 */ 745 // comment5 746 747 /* comment6 */ /* comment7 */ // comment8 748 `, 749 nil, 750 []*CommentGroup{ 751 { 752 Comments: []*Comment{ 753 &Comment{ 754 Comment: []string{"// comment1"}, 755 Slash: mkpos(3, 2, 3), 756 }, 757 &Comment{ 758 Comment: []string{"// comment2"}, 759 Slash: mkpos(17, 3, 3), 760 }, 761 }, 762 }, 763 { 764 Comments: []*Comment{ 765 &Comment{ 766 Comment: []string{"/* comment3", " comment4 */"}, 767 Slash: mkpos(32, 5, 3), 768 }, 769 &Comment{ 770 Comment: []string{"// comment5"}, 771 Slash: mkpos(63, 7, 3), 772 }, 773 }, 774 }, 775 { 776 Comments: []*Comment{ 777 &Comment{ 778 Comment: []string{"/* comment6 */"}, 779 Slash: mkpos(78, 9, 3), 780 }, 781 &Comment{ 782 Comment: []string{"/* comment7 */"}, 783 Slash: mkpos(93, 9, 18), 784 }, 785 &Comment{ 786 Comment: []string{"// comment8"}, 787 Slash: mkpos(108, 9, 33), 788 }, 789 }, 790 }, 791 }, 792 }, 793} 794 795func TestParseValidInput(t *testing.T) { 796 for i, testCase := range validParseTestCases { 797 t.Run(strconv.Itoa(i), func(t *testing.T) { 798 r := bytes.NewBufferString(testCase.input) 799 file, errs := Parse("", r) 800 if len(errs) != 0 { 801 t.Errorf("test case: %s", testCase.input) 802 t.Errorf("unexpected errors:") 803 for _, err := range errs { 804 t.Errorf(" %s", err) 805 } 806 t.FailNow() 807 } 808 809 if len(file.Defs) == len(testCase.defs) { 810 for i := range file.Defs { 811 if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) { 812 t.Errorf("test case: %s", testCase.input) 813 t.Errorf("incorrect definition %d:", i) 814 t.Errorf(" expected: %s", testCase.defs[i]) 815 t.Errorf(" got: %s", file.Defs[i]) 816 } 817 } 818 } else { 819 t.Errorf("test case: %s", testCase.input) 820 t.Errorf("length mismatch, expected %d definitions, got %d", 821 len(testCase.defs), len(file.Defs)) 822 } 823 824 if len(file.Comments) == len(testCase.comments) { 825 for i := range file.Comments { 826 if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) { 827 t.Errorf("test case: %s", testCase.input) 828 t.Errorf("incorrect comment %d:", i) 829 t.Errorf(" expected: %s", testCase.comments[i]) 830 t.Errorf(" got: %s", file.Comments[i]) 831 } 832 } 833 } else { 834 t.Errorf("test case: %s", testCase.input) 835 t.Errorf("length mismatch, expected %d comments, got %d", 836 len(testCase.comments), len(file.Comments)) 837 } 838 }) 839 } 840} 841 842func TestParseSelectWithoutTrailingComma(t *testing.T) { 843 r := bytes.NewBufferString(` 844 m { 845 foo: select(arch(), { 846 "arm64": true, 847 default: false 848 }), 849 } 850 `) 851 file, errs := ParseAndEval("", r, NewScope(nil)) 852 if len(errs) != 0 { 853 t.Fatalf("%s", errors.Join(errs...).Error()) 854 } 855 _, ok := file.Defs[0].(*Module).Properties[0].Value.(*Select) 856 if !ok { 857 t.Fatalf("did not parse to select") 858 } 859} 860 861func TestParserError(t *testing.T) { 862 testcases := []struct { 863 name string 864 input string 865 err string 866 }{ 867 { 868 name: "invalid first token", 869 input: "\x00", 870 err: "invalid character NUL", 871 }, 872 { 873 name: "select with duplicate condition", 874 input: ` 875 m { 876 foo: select((arch(), arch()), { 877 (default, default): true, 878 }), 879 } 880 `, 881 err: "Duplicate select condition found: arch()", 882 }, 883 { 884 name: "select with duplicate binding", 885 input: ` 886 m { 887 foo: select((arch(), os()), { 888 (any @ bar, any @ bar): true, 889 }), 890 } 891 `, 892 err: "Found duplicate select pattern binding: bar", 893 }, 894 // TODO: test more parser errors 895 } 896 897 for _, tt := range testcases { 898 t.Run(tt.name, func(t *testing.T) { 899 r := bytes.NewBufferString(tt.input) 900 _, errs := ParseAndEval("", r, NewScope(nil)) 901 if len(errs) == 0 { 902 t.Fatalf("missing expected error") 903 } 904 if g, w := errs[0], tt.err; !strings.Contains(g.Error(), w) { 905 t.Errorf("expected error %q, got %q", w, g) 906 } 907 for _, err := range errs[1:] { 908 t.Errorf("got unexpected extra error %q", err) 909 } 910 }) 911 } 912} 913 914func TestParserEndPos(t *testing.T) { 915 in := ` 916 module { 917 string: "string", 918 stringexp: "string1" + "string2", 919 int: -1, 920 intexp: -1 + 2, 921 list: ["a", "b"], 922 listexp: ["c"] + ["d"], 923 multilinelist: [ 924 "e", 925 "f", 926 ], 927 map: { 928 prop: "abc", 929 }, 930 } 931 ` 932 933 // Strip each line to make it easier to compute the previous "," from each property 934 lines := strings.Split(in, "\n") 935 for i := range lines { 936 lines[i] = strings.TrimSpace(lines[i]) 937 } 938 in = strings.Join(lines, "\n") 939 940 r := bytes.NewBufferString(in) 941 942 file, errs := Parse("", r) 943 if len(errs) != 0 { 944 t.Errorf("unexpected errors:") 945 for _, err := range errs { 946 t.Errorf(" %s", err) 947 } 948 t.FailNow() 949 } 950 951 mod := file.Defs[0].(*Module) 952 modEnd := mkpos(len(in)-1, len(lines)-1, 2) 953 if mod.End() != modEnd { 954 t.Errorf("expected mod.End() %s, got %s", modEnd, mod.End()) 955 } 956 957 nextPos := make([]scanner.Position, len(mod.Properties)) 958 for i := 0; i < len(mod.Properties)-1; i++ { 959 nextPos[i] = mod.Properties[i+1].Pos() 960 } 961 nextPos[len(mod.Properties)-1] = mod.RBracePos 962 963 for i, cur := range mod.Properties { 964 endOffset := nextPos[i].Offset - len(",\n") 965 endLine := nextPos[i].Line - 1 966 endColumn := len(lines[endLine-1]) // scanner.Position.Line is starts at 1 967 endPos := mkpos(endOffset, endLine, endColumn) 968 if cur.End() != endPos { 969 t.Errorf("expected property %s End() %s@%d, got %s@%d", cur.Name, endPos, endPos.Offset, cur.End(), cur.End().Offset) 970 } 971 } 972} 973 974func TestParserNotEvaluated(t *testing.T) { 975 // When parsing without evaluation, create variables correctly 976 input := "FOO=abc\n" 977 file, errs := Parse("", bytes.NewBufferString(input)) 978 if errs != nil { 979 t.Errorf("unexpected errors:") 980 for _, err := range errs { 981 t.Errorf(" %s", err) 982 } 983 t.FailNow() 984 } 985 assignment, ok := file.Defs[0].(*Assignment) 986 if !ok || assignment.Name != "FOO" { 987 t.Fatalf("Expected to find FOO after parsing %s", input) 988 } 989 if assignment.Value.String() != "abc" { 990 t.Errorf("Attempt to print FOO returned %s", assignment.Value.String()) 991 } 992} 993