github.com/hashicorp/hcl/v2@v2.20.0/hclwrite/parser_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hclwrite 5 6 import ( 7 "fmt" 8 "reflect" 9 "testing" 10 11 "github.com/davecgh/go-spew/spew" 12 13 "github.com/google/go-cmp/cmp" 14 15 "github.com/kylelemons/godebug/pretty" 16 17 "github.com/hashicorp/hcl/v2" 18 "github.com/hashicorp/hcl/v2/hclsyntax" 19 ) 20 21 func TestParse(t *testing.T) { 22 tests := []struct { 23 src string 24 want TestTreeNode 25 }{ 26 { 27 "", 28 TestTreeNode{ 29 Type: "Body", 30 }, 31 }, 32 { 33 "a = 1\n", 34 TestTreeNode{ 35 Type: "Body", 36 Children: []TestTreeNode{ 37 { 38 Type: "Attribute", 39 Children: []TestTreeNode{ 40 { 41 Type: "comments", 42 }, 43 { 44 Type: "identifier", 45 Val: "a", 46 }, 47 { 48 Type: "Tokens", 49 Val: " =", 50 }, 51 { 52 Type: "Expression", 53 Children: []TestTreeNode{ 54 { 55 Type: "Tokens", 56 Val: " 1", 57 }, 58 }, 59 }, 60 { 61 Type: "comments", 62 }, 63 { 64 Type: "Tokens", 65 Val: "\n", 66 }, 67 }, 68 }, 69 }, 70 }, 71 }, 72 { 73 "# aye aye aye\na = 1\n", 74 TestTreeNode{ 75 Type: "Body", 76 Children: []TestTreeNode{ 77 { 78 Type: "Attribute", 79 Children: []TestTreeNode{ 80 { 81 Type: "comments", 82 Val: "# aye aye aye\n", 83 }, 84 { 85 Type: "identifier", 86 Val: "a", 87 }, 88 { 89 Type: "Tokens", 90 Val: " =", 91 }, 92 { 93 Type: "Expression", 94 Children: []TestTreeNode{ 95 { 96 Type: "Tokens", 97 Val: " 1", 98 }, 99 }, 100 }, 101 { 102 Type: "comments", 103 }, 104 { 105 Type: "Tokens", 106 Val: "\n", 107 }, 108 }, 109 }, 110 }, 111 }, 112 }, 113 { 114 "a = 1 # because it is\n", 115 TestTreeNode{ 116 Type: "Body", 117 Children: []TestTreeNode{ 118 { 119 Type: "Attribute", 120 Children: []TestTreeNode{ 121 { 122 Type: "comments", 123 }, 124 { 125 Type: "identifier", 126 Val: "a", 127 }, 128 { 129 Type: "Tokens", 130 Val: " =", 131 }, 132 { 133 Type: "Expression", 134 Children: []TestTreeNode{ 135 { 136 Type: "Tokens", 137 Val: " 1", 138 }, 139 }, 140 }, 141 { 142 Type: "comments", 143 Val: " # because it is\n", 144 }, 145 }, 146 }, 147 }, 148 }, 149 }, 150 { 151 "# bee bee bee\n\nb = 1\n", // two newlines separate the comment from the attribute 152 TestTreeNode{ 153 Type: "Body", 154 Children: []TestTreeNode{ 155 { 156 Type: "Tokens", // Only lead/line comments attached to an object have type "comments" 157 Val: "# bee bee bee\n\n", 158 }, 159 { 160 Type: "Attribute", 161 Children: []TestTreeNode{ 162 { 163 Type: "comments", 164 }, 165 { 166 Type: "identifier", 167 Val: "b", 168 }, 169 { 170 Type: "Tokens", 171 Val: " =", 172 }, 173 { 174 Type: "Expression", 175 Children: []TestTreeNode{ 176 { 177 Type: "Tokens", 178 Val: " 1", 179 }, 180 }, 181 }, 182 { 183 Type: "comments", 184 Val: "", 185 }, 186 { 187 Type: "Tokens", 188 Val: "\n", 189 }, 190 }, 191 }, 192 }, 193 }, 194 }, 195 { 196 "a = (\n 1 + 2\n)\nb = 3\n", 197 TestTreeNode{ 198 Type: "Body", 199 Children: []TestTreeNode{ 200 { 201 Type: "Attribute", 202 Children: []TestTreeNode{ 203 {Type: "comments"}, 204 { 205 Type: "identifier", 206 Val: "a", 207 }, 208 { 209 Type: "Tokens", 210 Val: " =", 211 }, 212 { 213 Type: "Expression", 214 Children: []TestTreeNode{ 215 { 216 Type: "Tokens", 217 Val: " (\n 1 + 2\n)", 218 }, 219 }, 220 }, 221 {Type: "comments"}, 222 { 223 Type: "Tokens", 224 Val: "\n", 225 }, 226 }, 227 }, 228 { 229 Type: "Attribute", 230 Children: []TestTreeNode{ 231 {Type: "comments"}, 232 { 233 Type: "identifier", 234 Val: "b", 235 }, 236 { 237 Type: "Tokens", 238 Val: " =", 239 }, 240 { 241 Type: "Expression", 242 Children: []TestTreeNode{ 243 { 244 Type: "Tokens", 245 Val: " 3", 246 }, 247 }, 248 }, 249 {Type: "comments"}, 250 { 251 Type: "Tokens", 252 Val: "\n", 253 }, 254 }, 255 }, 256 }, 257 }, 258 }, 259 { 260 "b {}\n", 261 TestTreeNode{ 262 Type: "Body", 263 Children: []TestTreeNode{ 264 { 265 Type: "Block", 266 Children: []TestTreeNode{ 267 { 268 Type: "comments", 269 }, 270 { 271 Type: "identifier", 272 Val: "b", 273 }, 274 { 275 Type: "blockLabels", 276 }, 277 { 278 Type: "Tokens", 279 Val: " {", 280 }, 281 { 282 Type: "Body", 283 }, 284 { 285 Type: "Tokens", 286 Val: "}", 287 }, 288 { 289 Type: "Tokens", 290 Val: "\n", 291 }, 292 }, 293 }, 294 }, 295 }, 296 }, 297 { 298 "b label {}\n", 299 TestTreeNode{ 300 Type: "Body", 301 Children: []TestTreeNode{ 302 { 303 Type: "Block", 304 Children: []TestTreeNode{ 305 { 306 Type: "comments", 307 }, 308 { 309 Type: "identifier", 310 Val: "b", 311 }, 312 { 313 Type: "blockLabels", 314 Children: []TestTreeNode{ 315 { 316 Type: "identifier", 317 Val: " label", 318 }, 319 }, 320 }, 321 { 322 Type: "Tokens", 323 Val: " {", 324 }, 325 { 326 Type: "Body", 327 }, 328 { 329 Type: "Tokens", 330 Val: "}", 331 }, 332 { 333 Type: "Tokens", 334 Val: "\n", 335 }, 336 }, 337 }, 338 }, 339 }, 340 }, 341 { 342 "b \"label\" {}\n", 343 TestTreeNode{ 344 Type: "Body", 345 Children: []TestTreeNode{ 346 { 347 Type: "Block", 348 Children: []TestTreeNode{ 349 { 350 Type: "comments", 351 }, 352 { 353 Type: "identifier", 354 Val: "b", 355 }, 356 { 357 Type: "blockLabels", 358 Children: []TestTreeNode{ 359 { 360 Type: "quoted", 361 Val: ` "label"`, 362 }, 363 }, 364 }, 365 { 366 Type: "Tokens", 367 Val: " {", 368 }, 369 { 370 Type: "Body", 371 }, 372 { 373 Type: "Tokens", 374 Val: "}", 375 }, 376 { 377 Type: "Tokens", 378 Val: "\n", 379 }, 380 }, 381 }, 382 }, 383 }, 384 }, 385 { 386 "b \"label1\" /* foo */ \"label2\" {}\n", 387 TestTreeNode{ 388 Type: "Body", 389 Children: []TestTreeNode{ 390 { 391 Type: "Block", 392 Children: []TestTreeNode{ 393 { 394 Type: "comments", 395 }, 396 { 397 Type: "identifier", 398 Val: "b", 399 }, 400 { 401 Type: "blockLabels", 402 Children: []TestTreeNode{ 403 { 404 Type: "quoted", 405 Val: ` "label1"`, 406 }, 407 { 408 // The comment between the labels just 409 // becomes an "unstructured tokens" 410 // node, because this isn't a place 411 // where we expect comments to attach 412 // to a particular object as 413 // documentation. 414 Type: "Tokens", 415 Val: ` /* foo */`, 416 }, 417 { 418 Type: "quoted", 419 Val: ` "label2"`, 420 }, 421 }, 422 }, 423 { 424 Type: "Tokens", 425 Val: " {", 426 }, 427 { 428 Type: "Body", 429 }, 430 { 431 Type: "Tokens", 432 Val: "}", 433 }, 434 { 435 Type: "Tokens", 436 Val: "\n", 437 }, 438 }, 439 }, 440 }, 441 }, 442 }, 443 { 444 "b {\n a = 1\n}\n", 445 TestTreeNode{ 446 Type: "Body", 447 Children: []TestTreeNode{ 448 { 449 Type: "Block", 450 Children: []TestTreeNode{ 451 { 452 Type: "comments", 453 }, 454 { 455 Type: "identifier", 456 Val: "b", 457 }, 458 { 459 Type: "blockLabels", 460 }, 461 { 462 Type: "Tokens", 463 Val: " {", 464 }, 465 { 466 Type: "Body", 467 Children: []TestTreeNode{ 468 { 469 Type: "Tokens", 470 Val: "\n", 471 }, 472 { 473 Type: "Attribute", 474 Children: []TestTreeNode{ 475 { 476 Type: "comments", 477 }, 478 { 479 Type: "identifier", 480 Val: " a", 481 }, 482 { 483 Type: "Tokens", 484 Val: " =", 485 }, 486 { 487 Type: "Expression", 488 Children: []TestTreeNode{ 489 { 490 Type: "Tokens", 491 Val: " 1", 492 }, 493 }, 494 }, 495 { 496 Type: "comments", 497 }, 498 { 499 Type: "Tokens", 500 Val: "\n", 501 }, 502 }, 503 }, 504 }, 505 }, 506 { 507 Type: "Tokens", 508 Val: "}", 509 }, 510 { 511 Type: "Tokens", 512 Val: "\n", 513 }, 514 }, 515 }, 516 }, 517 }, 518 }, 519 { 520 "a = foo\n", 521 TestTreeNode{ 522 Type: "Body", 523 Children: []TestTreeNode{ 524 { 525 Type: "Attribute", 526 Children: []TestTreeNode{ 527 { 528 Type: "comments", 529 }, 530 { 531 Type: "identifier", 532 Val: "a", 533 }, 534 { 535 Type: "Tokens", 536 Val: " =", 537 }, 538 { 539 Type: "Expression", 540 Children: []TestTreeNode{ 541 { 542 Type: "Traversal", 543 Children: []TestTreeNode{ 544 { 545 Type: "TraverseName", 546 Children: []TestTreeNode{ 547 { 548 Type: "identifier", 549 Val: " foo", 550 }, 551 }, 552 }, 553 }, 554 }, 555 }, 556 }, 557 { 558 Type: "comments", 559 }, 560 { 561 Type: "Tokens", 562 Val: "\n", 563 }, 564 }, 565 }, 566 }, 567 }, 568 }, 569 { 570 "a = foo.bar\n", 571 TestTreeNode{ 572 Type: "Body", 573 Children: []TestTreeNode{ 574 { 575 Type: "Attribute", 576 Children: []TestTreeNode{ 577 { 578 Type: "comments", 579 }, 580 { 581 Type: "identifier", 582 Val: "a", 583 }, 584 { 585 Type: "Tokens", 586 Val: " =", 587 }, 588 { 589 Type: "Expression", 590 Children: []TestTreeNode{ 591 { 592 Type: "Traversal", 593 Children: []TestTreeNode{ 594 { 595 Type: "TraverseName", 596 Children: []TestTreeNode{ 597 { 598 Type: "identifier", 599 Val: " foo", 600 }, 601 }, 602 }, 603 { 604 Type: "TraverseName", 605 Children: []TestTreeNode{ 606 { 607 Type: "Tokens", 608 Val: ".", 609 }, 610 { 611 Type: "identifier", 612 Val: "bar", 613 }, 614 }, 615 }, 616 }, 617 }, 618 }, 619 }, 620 { 621 Type: "comments", 622 }, 623 { 624 Type: "Tokens", 625 Val: "\n", 626 }, 627 }, 628 }, 629 }, 630 }, 631 }, 632 { 633 "a = foo[0]\n", 634 TestTreeNode{ 635 Type: "Body", 636 Children: []TestTreeNode{ 637 { 638 Type: "Attribute", 639 Children: []TestTreeNode{ 640 { 641 Type: "comments", 642 }, 643 { 644 Type: "identifier", 645 Val: "a", 646 }, 647 { 648 Type: "Tokens", 649 Val: " =", 650 }, 651 { 652 Type: "Expression", 653 Children: []TestTreeNode{ 654 { 655 Type: "Traversal", 656 Children: []TestTreeNode{ 657 { 658 Type: "TraverseName", 659 Children: []TestTreeNode{ 660 { 661 Type: "identifier", 662 Val: " foo", 663 }, 664 }, 665 }, 666 { 667 Type: "TraverseIndex", 668 Children: []TestTreeNode{ 669 { 670 Type: "Tokens", 671 Val: "[", 672 }, 673 { 674 Type: "number", 675 Val: "0", 676 }, 677 { 678 Type: "Tokens", 679 Val: "]", 680 }, 681 }, 682 }, 683 }, 684 }, 685 }, 686 }, 687 { 688 Type: "comments", 689 }, 690 { 691 Type: "Tokens", 692 Val: "\n", 693 }, 694 }, 695 }, 696 }, 697 }, 698 }, 699 { 700 "a = foo.0\n", 701 TestTreeNode{ 702 Type: "Body", 703 Children: []TestTreeNode{ 704 { 705 Type: "Attribute", 706 Children: []TestTreeNode{ 707 { 708 Type: "comments", 709 }, 710 { 711 Type: "identifier", 712 Val: "a", 713 }, 714 { 715 Type: "Tokens", 716 Val: " =", 717 }, 718 { 719 Type: "Expression", 720 Children: []TestTreeNode{ 721 { 722 Type: "Traversal", 723 Children: []TestTreeNode{ 724 { 725 Type: "TraverseName", 726 Children: []TestTreeNode{ 727 { 728 Type: "identifier", 729 Val: " foo", 730 }, 731 }, 732 }, 733 { 734 Type: "TraverseIndex", 735 Children: []TestTreeNode{ 736 { 737 Type: "Tokens", 738 Val: ".", 739 }, 740 { 741 Type: "number", 742 Val: "0", 743 }, 744 }, 745 }, 746 }, 747 }, 748 }, 749 }, 750 { 751 Type: "comments", 752 }, 753 { 754 Type: "Tokens", 755 Val: "\n", 756 }, 757 }, 758 }, 759 }, 760 }, 761 }, 762 { 763 "a = foo.*\n", 764 TestTreeNode{ 765 Type: "Body", 766 Children: []TestTreeNode{ 767 { 768 Type: "Attribute", 769 Children: []TestTreeNode{ 770 { 771 Type: "comments", 772 }, 773 { 774 Type: "identifier", 775 Val: "a", 776 }, 777 { 778 Type: "Tokens", 779 Val: " =", 780 }, 781 { 782 Type: "Expression", 783 Children: []TestTreeNode{ 784 { 785 Type: "Traversal", 786 Children: []TestTreeNode{ 787 { 788 Type: "TraverseName", 789 Children: []TestTreeNode{ 790 { 791 Type: "identifier", 792 Val: " foo", 793 }, 794 }, 795 }, 796 }, 797 }, 798 { 799 Type: "Tokens", 800 Val: ".*", 801 }, 802 }, 803 }, 804 { 805 Type: "comments", 806 }, 807 { 808 Type: "Tokens", 809 Val: "\n", 810 }, 811 }, 812 }, 813 }, 814 }, 815 }, 816 { 817 "a = foo.*.bar\n", 818 TestTreeNode{ 819 Type: "Body", 820 Children: []TestTreeNode{ 821 { 822 Type: "Attribute", 823 Children: []TestTreeNode{ 824 { 825 Type: "comments", 826 }, 827 { 828 Type: "identifier", 829 Val: "a", 830 }, 831 { 832 Type: "Tokens", 833 Val: " =", 834 }, 835 { 836 Type: "Expression", 837 Children: []TestTreeNode{ 838 { 839 Type: "Traversal", 840 Children: []TestTreeNode{ 841 { 842 Type: "TraverseName", 843 Children: []TestTreeNode{ 844 { 845 Type: "identifier", 846 Val: " foo", 847 }, 848 }, 849 }, 850 }, 851 }, 852 { 853 Type: "Tokens", 854 Val: ".*.bar", 855 }, 856 }, 857 }, 858 { 859 Type: "comments", 860 }, 861 { 862 Type: "Tokens", 863 Val: "\n", 864 }, 865 }, 866 }, 867 }, 868 }, 869 }, 870 { 871 "a = foo[*]\n", 872 TestTreeNode{ 873 Type: "Body", 874 Children: []TestTreeNode{ 875 { 876 Type: "Attribute", 877 Children: []TestTreeNode{ 878 { 879 Type: "comments", 880 }, 881 { 882 Type: "identifier", 883 Val: "a", 884 }, 885 { 886 Type: "Tokens", 887 Val: " =", 888 }, 889 { 890 Type: "Expression", 891 Children: []TestTreeNode{ 892 { 893 Type: "Traversal", 894 Children: []TestTreeNode{ 895 { 896 Type: "TraverseName", 897 Children: []TestTreeNode{ 898 { 899 Type: "identifier", 900 Val: " foo", 901 }, 902 }, 903 }, 904 }, 905 }, 906 { 907 Type: "Tokens", 908 Val: "[*]", 909 }, 910 }, 911 }, 912 { 913 Type: "comments", 914 }, 915 { 916 Type: "Tokens", 917 Val: "\n", 918 }, 919 }, 920 }, 921 }, 922 }, 923 }, 924 { 925 "a = foo[*].bar\n", 926 TestTreeNode{ 927 Type: "Body", 928 Children: []TestTreeNode{ 929 { 930 Type: "Attribute", 931 Children: []TestTreeNode{ 932 { 933 Type: "comments", 934 }, 935 { 936 Type: "identifier", 937 Val: "a", 938 }, 939 { 940 Type: "Tokens", 941 Val: " =", 942 }, 943 { 944 Type: "Expression", 945 Children: []TestTreeNode{ 946 { 947 Type: "Traversal", 948 Children: []TestTreeNode{ 949 { 950 Type: "TraverseName", 951 Children: []TestTreeNode{ 952 { 953 Type: "identifier", 954 Val: " foo", 955 }, 956 }, 957 }, 958 }, 959 }, 960 { 961 Type: "Tokens", 962 Val: "[*].bar", 963 }, 964 }, 965 }, 966 { 967 Type: "comments", 968 }, 969 { 970 Type: "Tokens", 971 Val: "\n", 972 }, 973 }, 974 }, 975 }, 976 }, 977 }, 978 { 979 "a = foo[bar]\n", 980 TestTreeNode{ 981 Type: "Body", 982 Children: []TestTreeNode{ 983 { 984 Type: "Attribute", 985 Children: []TestTreeNode{ 986 { 987 Type: "comments", 988 }, 989 { 990 Type: "identifier", 991 Val: "a", 992 }, 993 { 994 Type: "Tokens", 995 Val: " =", 996 }, 997 { 998 Type: "Expression", 999 Children: []TestTreeNode{ 1000 { 1001 Type: "Traversal", 1002 Children: []TestTreeNode{ 1003 { 1004 Type: "TraverseName", 1005 Children: []TestTreeNode{ 1006 { 1007 Type: "identifier", 1008 Val: " foo", 1009 }, 1010 }, 1011 }, 1012 }, 1013 }, 1014 { 1015 Type: "Tokens", 1016 Val: "[", 1017 }, 1018 { 1019 Type: "Traversal", 1020 Children: []TestTreeNode{ 1021 { 1022 Type: "TraverseName", 1023 Children: []TestTreeNode{ 1024 { 1025 Type: "identifier", 1026 Val: "bar", 1027 }, 1028 }, 1029 }, 1030 }, 1031 }, 1032 { 1033 Type: "Tokens", 1034 Val: "]", 1035 }, 1036 }, 1037 }, 1038 { 1039 Type: "comments", 1040 }, 1041 { 1042 Type: "Tokens", 1043 Val: "\n", 1044 }, 1045 }, 1046 }, 1047 }, 1048 }, 1049 }, 1050 { 1051 "a = foo[bar.baz]\n", 1052 TestTreeNode{ 1053 Type: "Body", 1054 Children: []TestTreeNode{ 1055 { 1056 Type: "Attribute", 1057 Children: []TestTreeNode{ 1058 { 1059 Type: "comments", 1060 }, 1061 { 1062 Type: "identifier", 1063 Val: "a", 1064 }, 1065 { 1066 Type: "Tokens", 1067 Val: " =", 1068 }, 1069 { 1070 Type: "Expression", 1071 Children: []TestTreeNode{ 1072 { 1073 Type: "Traversal", 1074 Children: []TestTreeNode{ 1075 { 1076 Type: "TraverseName", 1077 Children: []TestTreeNode{ 1078 { 1079 Type: "identifier", 1080 Val: " foo", 1081 }, 1082 }, 1083 }, 1084 }, 1085 }, 1086 { 1087 Type: "Tokens", 1088 Val: "[", 1089 }, 1090 { 1091 Type: "Traversal", 1092 Children: []TestTreeNode{ 1093 { 1094 Type: "TraverseName", 1095 Children: []TestTreeNode{ 1096 { 1097 Type: "identifier", 1098 Val: "bar", 1099 }, 1100 }, 1101 }, 1102 { 1103 Type: "TraverseName", 1104 Children: []TestTreeNode{ 1105 { 1106 Type: "Tokens", 1107 Val: ".", 1108 }, 1109 { 1110 Type: "identifier", 1111 Val: "baz", 1112 }, 1113 }, 1114 }, 1115 }, 1116 }, 1117 { 1118 Type: "Tokens", 1119 Val: "]", 1120 }, 1121 }, 1122 }, 1123 { 1124 Type: "comments", 1125 }, 1126 { 1127 Type: "Tokens", 1128 Val: "\n", 1129 }, 1130 }, 1131 }, 1132 }, 1133 }, 1134 }, 1135 { 1136 "a = foo[bar].baz\n", 1137 TestTreeNode{ 1138 Type: "Body", 1139 Children: []TestTreeNode{ 1140 { 1141 Type: "Attribute", 1142 Children: []TestTreeNode{ 1143 { 1144 Type: "comments", 1145 }, 1146 { 1147 Type: "identifier", 1148 Val: "a", 1149 }, 1150 { 1151 Type: "Tokens", 1152 Val: " =", 1153 }, 1154 { 1155 Type: "Expression", 1156 Children: []TestTreeNode{ 1157 { 1158 Type: "Traversal", 1159 Children: []TestTreeNode{ 1160 { 1161 Type: "TraverseName", 1162 Children: []TestTreeNode{ 1163 { 1164 Type: "identifier", 1165 Val: " foo", 1166 }, 1167 }, 1168 }, 1169 }, 1170 }, 1171 { 1172 Type: "Tokens", 1173 Val: "[", 1174 }, 1175 { 1176 Type: "Traversal", 1177 Children: []TestTreeNode{ 1178 { 1179 Type: "TraverseName", 1180 Children: []TestTreeNode{ 1181 { 1182 Type: "identifier", 1183 Val: "bar", 1184 }, 1185 }, 1186 }, 1187 }, 1188 }, 1189 { 1190 Type: "Tokens", 1191 Val: "].baz", 1192 }, 1193 }, 1194 }, 1195 { 1196 Type: "comments", 1197 }, 1198 { 1199 Type: "Tokens", 1200 Val: "\n", 1201 }, 1202 }, 1203 }, 1204 }, 1205 }, 1206 }, 1207 } 1208 1209 for _, test := range tests { 1210 t.Run(test.src, func(t *testing.T) { 1211 file, diags := parse([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 1212 if len(diags) > 0 { 1213 for _, diag := range diags { 1214 t.Logf(" - %s", diag.Error()) 1215 } 1216 t.Fatalf("unexpected diagnostics") 1217 } 1218 1219 got := makeTestTree(file.body) 1220 1221 if !cmp.Equal(got, test.want) { 1222 diff := cmp.Diff(got, test.want) 1223 t.Errorf( 1224 "wrong result\ninput:\n%s\n\ngot:\n%s\nwant:%s\n\ndiff:\n%s", 1225 test.src, 1226 spew.Sdump(got), 1227 spew.Sdump(test.want), 1228 diff, 1229 ) 1230 } 1231 }) 1232 } 1233 } 1234 1235 func TestPartitionTokens(t *testing.T) { 1236 tests := []struct { 1237 tokens hclsyntax.Tokens 1238 rng hcl.Range 1239 wantStart int 1240 wantEnd int 1241 }{ 1242 { 1243 hclsyntax.Tokens{}, 1244 hcl.Range{ 1245 Start: hcl.Pos{Byte: 0}, 1246 End: hcl.Pos{Byte: 0}, 1247 }, 1248 0, 1249 0, 1250 }, 1251 { 1252 hclsyntax.Tokens{ 1253 { 1254 Type: hclsyntax.TokenIdent, 1255 Range: hcl.Range{ 1256 Start: hcl.Pos{Byte: 0}, 1257 End: hcl.Pos{Byte: 4}, 1258 }, 1259 }, 1260 }, 1261 hcl.Range{ 1262 Start: hcl.Pos{Byte: 0}, 1263 End: hcl.Pos{Byte: 4}, 1264 }, 1265 0, 1266 1, 1267 }, 1268 { 1269 hclsyntax.Tokens{ 1270 { 1271 Type: hclsyntax.TokenIdent, 1272 Range: hcl.Range{ 1273 Start: hcl.Pos{Byte: 0}, 1274 End: hcl.Pos{Byte: 4}, 1275 }, 1276 }, 1277 { 1278 Type: hclsyntax.TokenIdent, 1279 Range: hcl.Range{ 1280 Start: hcl.Pos{Byte: 4}, 1281 End: hcl.Pos{Byte: 8}, 1282 }, 1283 }, 1284 { 1285 Type: hclsyntax.TokenIdent, 1286 Range: hcl.Range{ 1287 Start: hcl.Pos{Byte: 8}, 1288 End: hcl.Pos{Byte: 12}, 1289 }, 1290 }, 1291 }, 1292 hcl.Range{ 1293 Start: hcl.Pos{Byte: 4}, 1294 End: hcl.Pos{Byte: 8}, 1295 }, 1296 1, 1297 2, 1298 }, 1299 { 1300 hclsyntax.Tokens{ 1301 { 1302 Type: hclsyntax.TokenIdent, 1303 Range: hcl.Range{ 1304 Start: hcl.Pos{Byte: 0}, 1305 End: hcl.Pos{Byte: 4}, 1306 }, 1307 }, 1308 { 1309 Type: hclsyntax.TokenIdent, 1310 Range: hcl.Range{ 1311 Start: hcl.Pos{Byte: 4}, 1312 End: hcl.Pos{Byte: 8}, 1313 }, 1314 }, 1315 { 1316 Type: hclsyntax.TokenIdent, 1317 Range: hcl.Range{ 1318 Start: hcl.Pos{Byte: 8}, 1319 End: hcl.Pos{Byte: 12}, 1320 }, 1321 }, 1322 }, 1323 hcl.Range{ 1324 Start: hcl.Pos{Byte: 0}, 1325 End: hcl.Pos{Byte: 8}, 1326 }, 1327 0, 1328 2, 1329 }, 1330 { 1331 hclsyntax.Tokens{ 1332 { 1333 Type: hclsyntax.TokenIdent, 1334 Range: hcl.Range{ 1335 Start: hcl.Pos{Byte: 0}, 1336 End: hcl.Pos{Byte: 4}, 1337 }, 1338 }, 1339 { 1340 Type: hclsyntax.TokenIdent, 1341 Range: hcl.Range{ 1342 Start: hcl.Pos{Byte: 4}, 1343 End: hcl.Pos{Byte: 8}, 1344 }, 1345 }, 1346 { 1347 Type: hclsyntax.TokenIdent, 1348 Range: hcl.Range{ 1349 Start: hcl.Pos{Byte: 8}, 1350 End: hcl.Pos{Byte: 12}, 1351 }, 1352 }, 1353 }, 1354 hcl.Range{ 1355 Start: hcl.Pos{Byte: 4}, 1356 End: hcl.Pos{Byte: 12}, 1357 }, 1358 1, 1359 3, 1360 }, 1361 } 1362 1363 prettyConfig := &pretty.Config{ 1364 Diffable: true, 1365 IncludeUnexported: true, 1366 PrintStringers: true, 1367 } 1368 1369 for i, test := range tests { 1370 t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { 1371 gotStart, gotEnd := partitionTokens(test.tokens, test.rng) 1372 1373 if gotStart != test.wantStart || gotEnd != test.wantEnd { 1374 t.Errorf( 1375 "wrong result\ntokens: %s\nrange: %#v\ngot: %d, %d\nwant: %d, %d", 1376 prettyConfig.Sprint(test.tokens), test.rng, 1377 gotStart, test.wantStart, 1378 gotEnd, test.wantEnd, 1379 ) 1380 } 1381 }) 1382 } 1383 } 1384 1385 func TestPartitionLeadCommentTokens(t *testing.T) { 1386 tests := []struct { 1387 tokens hclsyntax.Tokens 1388 wantStart int 1389 }{ 1390 { 1391 hclsyntax.Tokens{}, 1392 0, 1393 }, 1394 { 1395 hclsyntax.Tokens{ 1396 { 1397 Type: hclsyntax.TokenComment, 1398 }, 1399 }, 1400 0, 1401 }, 1402 { 1403 hclsyntax.Tokens{ 1404 { 1405 Type: hclsyntax.TokenComment, 1406 }, 1407 { 1408 Type: hclsyntax.TokenComment, 1409 }, 1410 }, 1411 0, 1412 }, 1413 { 1414 hclsyntax.Tokens{ 1415 { 1416 Type: hclsyntax.TokenComment, 1417 }, 1418 { 1419 Type: hclsyntax.TokenNewline, 1420 }, 1421 }, 1422 2, 1423 }, 1424 { 1425 hclsyntax.Tokens{ 1426 { 1427 Type: hclsyntax.TokenComment, 1428 }, 1429 { 1430 Type: hclsyntax.TokenNewline, 1431 }, 1432 { 1433 Type: hclsyntax.TokenComment, 1434 }, 1435 }, 1436 2, 1437 }, 1438 } 1439 1440 prettyConfig := &pretty.Config{ 1441 Diffable: true, 1442 IncludeUnexported: true, 1443 PrintStringers: true, 1444 } 1445 1446 for i, test := range tests { 1447 t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { 1448 gotStart := partitionLeadCommentTokens(test.tokens) 1449 1450 if gotStart != test.wantStart { 1451 t.Errorf( 1452 "wrong result\ntokens: %s\ngot: %d\nwant: %d", 1453 prettyConfig.Sprint(test.tokens), 1454 gotStart, test.wantStart, 1455 ) 1456 } 1457 }) 1458 } 1459 } 1460 1461 func TestLexConfig(t *testing.T) { 1462 tests := []struct { 1463 input string 1464 want Tokens 1465 }{ 1466 { 1467 `a b `, 1468 Tokens{ 1469 { 1470 Type: hclsyntax.TokenIdent, 1471 Bytes: []byte(`a`), 1472 SpacesBefore: 0, 1473 }, 1474 { 1475 Type: hclsyntax.TokenIdent, 1476 Bytes: []byte(`b`), 1477 SpacesBefore: 2, 1478 }, 1479 { 1480 Type: hclsyntax.TokenEOF, 1481 Bytes: []byte{}, 1482 SpacesBefore: 1, 1483 }, 1484 }, 1485 }, 1486 { 1487 ` 1488 foo "bar" "baz" { 1489 pizza = " cheese " 1490 } 1491 `, 1492 Tokens{ 1493 { 1494 Type: hclsyntax.TokenNewline, 1495 Bytes: []byte{'\n'}, 1496 SpacesBefore: 0, 1497 }, 1498 { 1499 Type: hclsyntax.TokenIdent, 1500 Bytes: []byte(`foo`), 1501 SpacesBefore: 0, 1502 }, 1503 { 1504 Type: hclsyntax.TokenOQuote, 1505 Bytes: []byte(`"`), 1506 SpacesBefore: 1, 1507 }, 1508 { 1509 Type: hclsyntax.TokenQuotedLit, 1510 Bytes: []byte(`bar`), 1511 SpacesBefore: 0, 1512 }, 1513 { 1514 Type: hclsyntax.TokenCQuote, 1515 Bytes: []byte(`"`), 1516 SpacesBefore: 0, 1517 }, 1518 { 1519 Type: hclsyntax.TokenOQuote, 1520 Bytes: []byte(`"`), 1521 SpacesBefore: 1, 1522 }, 1523 { 1524 Type: hclsyntax.TokenQuotedLit, 1525 Bytes: []byte(`baz`), 1526 SpacesBefore: 0, 1527 }, 1528 { 1529 Type: hclsyntax.TokenCQuote, 1530 Bytes: []byte(`"`), 1531 SpacesBefore: 0, 1532 }, 1533 { 1534 Type: hclsyntax.TokenOBrace, 1535 Bytes: []byte(`{`), 1536 SpacesBefore: 1, 1537 }, 1538 { 1539 Type: hclsyntax.TokenNewline, 1540 Bytes: []byte("\n"), 1541 SpacesBefore: 0, 1542 }, 1543 { 1544 Type: hclsyntax.TokenIdent, 1545 Bytes: []byte(`pizza`), 1546 SpacesBefore: 4, 1547 }, 1548 { 1549 Type: hclsyntax.TokenEqual, 1550 Bytes: []byte(`=`), 1551 SpacesBefore: 1, 1552 }, 1553 { 1554 Type: hclsyntax.TokenOQuote, 1555 Bytes: []byte(`"`), 1556 SpacesBefore: 1, 1557 }, 1558 { 1559 Type: hclsyntax.TokenQuotedLit, 1560 Bytes: []byte(` cheese `), 1561 SpacesBefore: 0, 1562 }, 1563 { 1564 Type: hclsyntax.TokenCQuote, 1565 Bytes: []byte(`"`), 1566 SpacesBefore: 0, 1567 }, 1568 { 1569 Type: hclsyntax.TokenNewline, 1570 Bytes: []byte("\n"), 1571 SpacesBefore: 0, 1572 }, 1573 { 1574 Type: hclsyntax.TokenCBrace, 1575 Bytes: []byte(`}`), 1576 SpacesBefore: 0, 1577 }, 1578 { 1579 Type: hclsyntax.TokenNewline, 1580 Bytes: []byte("\n"), 1581 SpacesBefore: 0, 1582 }, 1583 { 1584 Type: hclsyntax.TokenEOF, 1585 Bytes: []byte{}, 1586 SpacesBefore: 0, 1587 }, 1588 }, 1589 }, 1590 } 1591 1592 prettyConfig := &pretty.Config{ 1593 Diffable: true, 1594 IncludeUnexported: true, 1595 PrintStringers: true, 1596 } 1597 1598 for _, test := range tests { 1599 t.Run(test.input, func(t *testing.T) { 1600 got := lexConfig([]byte(test.input)) 1601 1602 if !reflect.DeepEqual(got, test.want) { 1603 diff := prettyConfig.Compare(test.want, got) 1604 t.Errorf( 1605 "wrong result\ninput: %s\ndiff: %s", test.input, diff, 1606 ) 1607 } 1608 }) 1609 } 1610 }