1package hclwrite 2 3import ( 4 "fmt" 5 "testing" 6 7 "reflect" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/hashicorp/hcl2/hcl/hclsyntax" 11) 12 13func TestFormat(t *testing.T) { 14 tests := []struct { 15 input string 16 want string 17 }{ 18 { 19 ``, 20 ``, 21 }, 22 { 23 `a=1`, 24 `a = 1`, 25 }, 26 { 27 `a=b.c`, 28 `a = b.c`, 29 }, 30 { 31 `a=b[c]`, 32 `a = b[c]`, 33 }, 34 { 35 `a=b()[c]`, 36 `a = b()[c]`, 37 }, 38 { 39 `a=["hello"][0]`, 40 `a = ["hello"][0]`, 41 }, 42 { 43 `( a+2 )`, 44 `(a + 2)`, 45 }, 46 { 47 `( a*2 )`, 48 `(a * 2)`, 49 }, 50 { 51 `( a+-2 )`, 52 `(a + -2)`, 53 }, 54 { 55 `( a*-2 )`, 56 `(a * -2)`, 57 }, 58 { 59 `(-2+1)`, 60 `(-2 + 1)`, 61 }, 62 { 63 `foo(1, -2,a*b, b,c)`, 64 `foo(1, -2, a * b, b, c)`, 65 }, 66 { 67 `foo(a,b...)`, 68 `foo(a, b...)`, 69 }, 70 { 71 `a="hello ${ name }"`, 72 `a = "hello ${name}"`, 73 }, 74 { 75 `a="hello ${~ name ~}"`, 76 `a = "hello ${~name~}"`, 77 }, 78 { 79 `a="${b}${c}${ d } ${e}"`, 80 `a = "${b}${c}${d} ${e}"`, 81 }, 82 { 83 `"%{if true}${var.foo}%{endif}"`, 84 `"%{if true}${var.foo}%{endif}"`, 85 }, 86 { 87 `b{}`, 88 `b {}`, 89 }, 90 { 91 ` 92"${ 93hello 94}" 95`, 96 ` 97"${ 98 hello 99}" 100`, 101 }, 102 { 103 ` 104foo( 1051, 106- 2, 107a*b, 108b, 109c, 110) 111`, 112 ` 113foo( 114 1, 115 -2, 116 a * b, 117 b, 118 c, 119) 120`, 121 }, 122 { 123 `a?b:c`, 124 `a ? b : c`, 125 }, 126 { 127 `[ [ ] ]`, 128 `[[]]`, 129 }, 130 { 131 `[for x in y : x]`, 132 `[for x in y : x]`, 133 }, 134 { 135 `[for x in [y] : x]`, 136 `[for x in [y] : x]`, 137 }, 138 { 139 ` 140[ 141[ 142a 143] 144] 145`, 146 ` 147[ 148 [ 149 a 150 ] 151] 152`, 153 }, 154 { 155 ` 156[[ 157a 158]] 159`, 160 ` 161[[ 162 a 163]] 164`, 165 }, 166 { 167 ` 168[[ 169[ 170a 171] 172]] 173`, 174 ` 175[[ 176 [ 177 a 178 ] 179]] 180`, 181 }, 182 { 183 // degenerate case with asymmetrical brackets 184 ` 185[[ 186[ 187a 188]] 189] 190`, 191 ` 192[[ 193 [ 194 a 195 ]] 196] 197`, 198 }, 199 { 200 ` 201b { 202a = 1 203} 204`, 205 ` 206b { 207 a = 1 208} 209`, 210 }, 211 { 212 ` 213b {a = 1} 214`, 215 ` 216b { a = 1 } 217`, 218 }, 219 { 220 ` 221a = 1 222bungle = 2 223`, 224 ` 225a = 1 226bungle = 2 227`, 228 }, 229 { 230 ` 231a = 1 232 233bungle = 2 234`, 235 ` 236a = 1 237 238bungle = 2 239`, 240 }, 241 { 242 ` 243a = 1 # foo 244bungle = 2 245`, 246 ` 247a = 1 # foo 248bungle = 2 249`, 250 }, 251 { 252 ` 253a = 1 # foo 254bungle = "bonce" # baz 255`, 256 ` 257a = 1 # foo 258bungle = "bonce" # baz 259`, 260 }, 261 { 262 ` 263# here we go 264a = 1 # foo 265bungle = "bonce" # baz 266`, 267 ` 268# here we go 269a = 1 # foo 270bungle = "bonce" # baz 271`, 272 }, 273 { 274 ` 275foo {} # here we go 276a = 1 # foo 277bungle = "bonce" # baz 278`, 279 ` 280foo {} # here we go 281a = 1 # foo 282bungle = "bonce" # baz 283`, 284 }, 285 { 286 ` 287a = 1 # foo 288bungle = "bonce" # baz 289zebra = "striped" # baz 290`, 291 ` 292a = 1 # foo 293bungle = "bonce" # baz 294zebra = "striped" # baz 295`, 296 }, 297 { 298 ` 299a = 1 # foo 300bungle = ( 301 "bonce" 302) # baz 303zebra = "striped" # baz 304`, 305 ` 306a = 1 # foo 307bungle = ( 308 "bonce" 309) # baz 310zebra = "striped" # baz 311`, 312 }, 313 { 314 ` 315a="apple"# foo 316bungle=(# woo parens 317"bonce" 318)# baz 319zebra="striped"# baz 320`, 321 ` 322a = "apple" # foo 323bungle = ( # woo parens 324 "bonce" 325) # baz 326zebra = "striped" # baz 327`, 328 }, 329 { 330 ` 331 = 1 # foo 332bungle = "" # baz 333zebra = "striped" # baz 334`, 335 ` 336 = 1 # foo 337bungle = "" # baz 338zebra = "striped" # baz 339`, 340 }, 341 { 342 ` 343foo { 344# ... 345} 346`, 347 ` 348foo { 349 # ... 350} 351`, 352 }, 353 { 354 ` 355foo = { 356# ... 357} 358`, 359 ` 360foo = { 361 # ... 362} 363`, 364 }, 365 { 366 ` 367foo = [ 368# ... 369] 370`, 371 ` 372foo = [ 373 # ... 374] 375`, 376 }, 377 { 378 ` 379foo = [{ 380# ... 381}] 382`, 383 ` 384foo = [{ 385 # ... 386}] 387`, 388 }, 389 { 390 ` 391foo { 392bar { 393# ... 394} 395} 396`, 397 ` 398foo { 399 bar { 400 # ... 401 } 402} 403`, 404 }, 405 { 406 ` 407foo { 408bar = { 409# ... 410} 411} 412`, 413 ` 414foo { 415 bar = { 416 # ... 417 } 418} 419`, 420 }, 421 { 422 ` 423foo { 424bar = [ 425# ... 426] 427} 428`, 429 ` 430foo { 431 bar = [ 432 # ... 433 ] 434} 435`, 436 }, 437 { 438 ` 439foo { 440bar = <<EOT 441Foo bar baz 442EOT 443} 444`, 445 ` 446foo { 447 bar = <<EOT 448Foo bar baz 449EOT 450} 451`, 452 }, 453 { 454 ` 455foo { 456bar = <<-EOT 457Foo bar baz 458EOT 459} 460`, 461 ` 462foo { 463 bar = <<-EOT 464Foo bar baz 465EOT 466} 467`, 468 }, 469 { 470 ` 471foo { 472bar = <<-EOT 473 Foo bar baz 474EOT 475} 476`, 477 ` 478foo { 479 bar = <<-EOT 480 Foo bar baz 481EOT 482} 483`, 484 }, 485 { 486 ` 487foo { 488bar = <<-EOT 489 blahblahblah = x 490EOT 491} 492`, 493 ` 494foo { 495 bar = <<-EOT 496 blahblahblah = x 497EOT 498} 499`, 500 }, 501 { 502 ` 503foo { 504bar = <<-EOT 505 ${{ blahblahblah = x }} 506EOT 507} 508`, 509 ` 510foo { 511 bar = <<-EOT 512 ${ { blahblahblah = x } } 513EOT 514} 515`, 516 }, 517 { 518 ` 519foo { 520 bar = <<-EOT 521 ${a}${b}${ c } ${d} 522EOT 523} 524`, 525 ` 526foo { 527 bar = <<-EOT 528 ${a}${b}${c} ${d} 529EOT 530} 531`, 532 }, 533 { 534 ` 535foo { 536bar = <<EOT 537Foo bar baz 538EOT 539} 540 541baz { 542default="string" 543} 544`, 545 ` 546foo { 547 bar = <<EOT 548Foo bar baz 549EOT 550} 551 552baz { 553 default = "string" 554} 555`, 556 }, 557 { 558 ` 559foo { 560bar = <<EOT 561Foo bar baz 562EOT 563baz = <<EOT 564Foo bar baz 565EOT 566} 567 568bar { 569foo = "bar" 570} 571`, 572 ` 573foo { 574 bar = <<EOT 575Foo bar baz 576EOT 577 baz = <<EOT 578Foo bar baz 579EOT 580} 581 582bar { 583 foo = "bar" 584} 585`, 586 }, 587 { 588 ` 589module "foo" { 590foo = <<EOF 5915 592EOF 593} 594 595module "x" { 596a = "b" 597abcde = "456" 598}`, 599 ` 600module "foo" { 601 foo = <<EOF 6025 603EOF 604} 605 606module "x" { 607 a = "b" 608 abcde = "456" 609}`, 610 }, 611 } 612 613 for i, test := range tests { 614 t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { 615 tokens := lexConfig([]byte(test.input)) 616 format(tokens) 617 t.Logf("tokens %s\n", spew.Sdump(tokens)) 618 got := string(tokens.Bytes()) 619 620 if got != test.want { 621 t.Errorf("wrong result\ninput:\n%s\ngot:\n%s\nwant:\n%s", test.input, got, test.want) 622 } 623 }) 624 } 625 626} 627 628func TestLinesForFormat(t *testing.T) { 629 tests := []struct { 630 tokens Tokens 631 want []formatLine 632 }{ 633 { 634 Tokens{ 635 &Token{Type: hclsyntax.TokenEOF}, 636 }, 637 []formatLine{ 638 { 639 lead: Tokens{}, 640 }, 641 }, 642 }, 643 { 644 Tokens{ 645 &Token{Type: hclsyntax.TokenIdent}, 646 &Token{Type: hclsyntax.TokenEOF}, 647 }, 648 []formatLine{ 649 { 650 lead: Tokens{ 651 &Token{Type: hclsyntax.TokenIdent}, 652 }, 653 }, 654 }, 655 }, 656 { 657 Tokens{ 658 &Token{Type: hclsyntax.TokenIdent}, 659 &Token{Type: hclsyntax.TokenNewline}, 660 &Token{Type: hclsyntax.TokenNumberLit}, 661 &Token{Type: hclsyntax.TokenEOF}, 662 }, 663 []formatLine{ 664 { 665 lead: Tokens{ 666 &Token{Type: hclsyntax.TokenIdent}, 667 &Token{Type: hclsyntax.TokenNewline}, 668 }, 669 }, 670 { 671 lead: Tokens{ 672 &Token{Type: hclsyntax.TokenNumberLit}, 673 }, 674 }, 675 }, 676 }, 677 { 678 Tokens{ 679 &Token{Type: hclsyntax.TokenIdent}, 680 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 681 &Token{Type: hclsyntax.TokenNumberLit}, 682 &Token{Type: hclsyntax.TokenEOF}, 683 }, 684 []formatLine{ 685 { 686 lead: Tokens{ 687 &Token{Type: hclsyntax.TokenIdent}, 688 }, 689 comment: Tokens{ 690 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 691 }, 692 }, 693 { 694 lead: Tokens{ 695 &Token{Type: hclsyntax.TokenNumberLit}, 696 }, 697 }, 698 }, 699 }, 700 { 701 Tokens{ 702 &Token{Type: hclsyntax.TokenIdent}, 703 &Token{Type: hclsyntax.TokenEqual}, 704 &Token{Type: hclsyntax.TokenNumberLit}, 705 &Token{Type: hclsyntax.TokenEOF}, 706 }, 707 []formatLine{ 708 { 709 lead: Tokens{ 710 &Token{Type: hclsyntax.TokenIdent}, 711 }, 712 assign: Tokens{ 713 &Token{Type: hclsyntax.TokenEqual}, 714 &Token{Type: hclsyntax.TokenNumberLit}, 715 }, 716 }, 717 }, 718 }, 719 { 720 Tokens{ 721 &Token{Type: hclsyntax.TokenIdent}, 722 &Token{Type: hclsyntax.TokenEqual}, 723 &Token{Type: hclsyntax.TokenNumberLit}, 724 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 725 &Token{Type: hclsyntax.TokenEOF}, 726 }, 727 []formatLine{ 728 { 729 lead: Tokens{ 730 &Token{Type: hclsyntax.TokenIdent}, 731 }, 732 assign: Tokens{ 733 &Token{Type: hclsyntax.TokenEqual}, 734 &Token{Type: hclsyntax.TokenNumberLit}, 735 }, 736 comment: Tokens{ 737 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 738 }, 739 }, 740 { 741 lead: Tokens{}, 742 }, 743 }, 744 }, 745 { 746 Tokens{ 747 // A comment goes into a comment cell only if it is after 748 // some non-comment tokens, since whole-line comments must 749 // stay flush with the indent level. 750 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 751 &Token{Type: hclsyntax.TokenEOF}, 752 }, 753 []formatLine{ 754 { 755 lead: Tokens{ 756 &Token{Type: hclsyntax.TokenComment, Bytes: []byte("#foo\n")}, 757 }, 758 }, 759 { 760 lead: Tokens{}, 761 }, 762 }, 763 }, 764 } 765 766 for i, test := range tests { 767 t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { 768 got := linesForFormat(test.tokens) 769 770 if !reflect.DeepEqual(got, test.want) { 771 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) 772 } 773 }) 774 } 775} 776