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