github.com/cilki/sh@v2.6.4+incompatible/syntax/printer_test.go (about)

     1  // Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package syntax
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"strings"
    12  	"testing"
    13  )
    14  
    15  func TestPrintCompact(t *testing.T) {
    16  	t.Parallel()
    17  	parserBash := NewParser(KeepComments)
    18  	parserPosix := NewParser(KeepComments, Variant(LangPOSIX))
    19  	parserMirBSD := NewParser(KeepComments, Variant(LangMirBSDKorn))
    20  	printer := NewPrinter()
    21  	for i, c := range fileTests {
    22  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
    23  			in := c.Strs[0]
    24  			parser := parserPosix
    25  			if c.Bash != nil {
    26  				parser = parserBash
    27  			} else if c.MirBSDKorn != nil {
    28  				parser = parserMirBSD
    29  			}
    30  			printTest(t, parser, printer, in, in)
    31  		})
    32  	}
    33  }
    34  
    35  func strPrint(p *Printer, node Node) (string, error) {
    36  	var buf bytes.Buffer
    37  	err := p.Print(&buf, node)
    38  	return buf.String(), err
    39  }
    40  
    41  type printCase struct {
    42  	in, want string
    43  }
    44  
    45  func samePrint(s string) printCase { return printCase{in: s, want: s} }
    46  
    47  var printTests = []printCase{
    48  	samePrint(`fo○ b\år`),
    49  	samePrint(`"fo○ b\år"`),
    50  	samePrint(`'fo○ b\år'`),
    51  	samePrint(`${a#fo○ b\år}`),
    52  	samePrint(`#fo○ b\år`),
    53  	samePrint("<<EOF\nfo○ b\\år\nEOF"),
    54  	samePrint(`$'○ b\år'`),
    55  	samePrint("${a/b//○}"),
    56  	{strings.Repeat(" ", bufSize-3) + "○", "○"}, // at the end of a chunk
    57  	{strings.Repeat(" ", bufSize-0) + "○", "○"}, // at the start of a chunk
    58  	{strings.Repeat(" ", bufSize-2) + "○", "○"}, // split after 1st byte
    59  	{strings.Repeat(" ", bufSize-1) + "○", "○"}, // split after 2nd byte
    60  	// peekByte that would (but cannot) go to the next chunk
    61  	{strings.Repeat(" ", bufSize-2) + ">(a)", ">(a)"},
    62  	// escaped newline at end of chunk
    63  	{"a" + strings.Repeat(" ", bufSize-2) + "\\\nb", "a \\\n\tb"},
    64  	// panics if padding is only 4 (utf8.UTFMax)
    65  	{strings.Repeat(" ", bufSize-10) + "${a/b//○}", "${a/b//○}"},
    66  	// multiple p.fill calls
    67  	{"a" + strings.Repeat(" ", bufSize*4) + "b", "a b"},
    68  	// newline at the beginning of second chunk
    69  	{"a" + strings.Repeat(" ", bufSize-2) + "\nb", "a\nb"},
    70  	{"foo; bar", "foo\nbar"},
    71  	{"foo\n\n\nbar", "foo\n\nbar"},
    72  	{"foo\n\n", "foo"},
    73  	{"\n\nfoo", "foo"},
    74  	{"# foo \n # bar\t", "# foo\n# bar"},
    75  	samePrint("#"),
    76  	samePrint("#c1\\\n#c2"),
    77  	samePrint("#\\\n#"),
    78  	samePrint("foo\\\\\nbar"),
    79  	samePrint("a=b # inline\nbar"),
    80  	samePrint("a=$(b) # inline"),
    81  	samePrint("foo # inline\n# after"),
    82  	samePrint("$(a) $(b)"),
    83  	{"if a\nthen\n\tb\nfi", "if a; then\n\tb\nfi"},
    84  	{"if a; then\nb\nelse\nfi", "if a; then\n\tb\nfi"},
    85  	{"if a; then b\nelse c\nfi", "if a; then\n\tb\nelse\n\tc\nfi"},
    86  	samePrint("foo >&2 <f bar"),
    87  	samePrint("foo >&2 bar <f"),
    88  	{"foo >&2 bar <f bar2", "foo >&2 bar bar2 <f"},
    89  	{"foo <<EOF bar\nl1\nEOF", "foo bar <<EOF\nl1\nEOF"},
    90  	samePrint("foo <<\\\\\\\\EOF\nbar\n\\\\EOF"),
    91  	samePrint("foo <<\"\\EOF\"\nbar\n\\EOF"),
    92  	samePrint("foo <<EOF && bar\nl1\nEOF"),
    93  	samePrint("foo <<EOF &&\nl1\nEOF\n\tbar"),
    94  	samePrint("foo <<EOF\nl1\nEOF\n\nfoo2"),
    95  	samePrint("<<EOF\nEOF"),
    96  	samePrint("foo <<EOF\nEOF\n\nbar"),
    97  	samePrint("foo <<'EOF'\nEOF\n\nbar"),
    98  	{
    99  		"{ foo; bar; }",
   100  		"{\n\tfoo\n\tbar\n}",
   101  	},
   102  	{
   103  		"{ foo; bar; }\n#etc",
   104  		"{\n\tfoo\n\tbar\n}\n#etc",
   105  	},
   106  	{
   107  		"{\n\tfoo; }",
   108  		"{\n\tfoo\n}",
   109  	},
   110  	{
   111  		"{ foo\n}",
   112  		"{\n\tfoo\n}",
   113  	},
   114  	{
   115  		"(foo\n)",
   116  		"(\n\tfoo\n)",
   117  	},
   118  	{
   119  		"$(foo\n)",
   120  		"$(\n\tfoo\n)",
   121  	},
   122  	{
   123  		"a\n\n\n# etc\nb",
   124  		"a\n\n# etc\nb",
   125  	},
   126  	{
   127  		"a b\\\nc d",
   128  		"a bc \\\n\td",
   129  	},
   130  	{
   131  		"a bb\\\ncc d",
   132  		"a bbcc \\\n\td",
   133  	},
   134  	samePrint("a \\\n\tb \\\n\tc \\\n\t;"),
   135  	samePrint("a=1 \\\n\tb=2 \\\n\tc=3 \\\n\t;"),
   136  	samePrint("if a \\\n\t; then b; fi"),
   137  	samePrint("a 'b\nb' c"),
   138  	samePrint("a $'b\nb' c"),
   139  	{
   140  		"(foo; bar)",
   141  		"(\n\tfoo\n\tbar\n)",
   142  	},
   143  	{
   144  		"{\nfoo\nbar; }",
   145  		"{\n\tfoo\n\tbar\n}",
   146  	},
   147  	samePrint("\"$foo\"\n{\n\tbar\n}"),
   148  	{
   149  		"{\nbar\n# extra\n}",
   150  		"{\n\tbar\n\t# extra\n}",
   151  	},
   152  	{
   153  		"foo\nbar  # extra",
   154  		"foo\nbar # extra",
   155  	},
   156  	{
   157  		"foo # 1\nfooo # 2\nfo # 3",
   158  		"foo  # 1\nfooo # 2\nfo   # 3",
   159  	},
   160  	{
   161  		" foo # 1\n fooo # 2\n fo # 3",
   162  		"foo  # 1\nfooo # 2\nfo   # 3",
   163  	},
   164  	{
   165  		"foo   # 1\nfooo  # 2\nfo    # 3",
   166  		"foo  # 1\nfooo # 2\nfo   # 3",
   167  	},
   168  	{
   169  		"foooooa\nfoo # 1\nfooo # 2\nfo # 3\nfooooo",
   170  		"foooooa\nfoo  # 1\nfooo # 2\nfo   # 3\nfooooo",
   171  	},
   172  	{
   173  		"foo\nbar\nfoo # 1\nfooo # 2",
   174  		"foo\nbar\nfoo  # 1\nfooo # 2",
   175  	},
   176  	samePrint("foobar # 1\nfoo\nfoo # 2"),
   177  	samePrint("foobar # 1\n#foo\nfoo # 2"),
   178  	samePrint("foobar # 1\n\nfoo # 2"),
   179  	{
   180  		"foo # 2\nfoo2 bar # 1",
   181  		"foo      # 2\nfoo2 bar # 1",
   182  	},
   183  	{
   184  		"foo bar # 1\n! foo # 2",
   185  		"foo bar # 1\n! foo   # 2",
   186  	},
   187  	{
   188  		"aa #b\nc  #d\ne\nf #g",
   189  		"aa #b\nc  #d\ne\nf #g",
   190  	},
   191  	{
   192  		"{ a; } #x\nbbb #y\n{ #z\n}",
   193  		"{ a; } #x\nbbb    #y\n{ #z\n}",
   194  	},
   195  	{
   196  		"foo; foooo # 1",
   197  		"foo\nfoooo # 1",
   198  	},
   199  	{
   200  		"aaa; b #1\nc #2",
   201  		"aaa\nb #1\nc #2",
   202  	},
   203  	{
   204  		"a #1\nbbb; c #2\nd #3",
   205  		"a #1\nbbb\nc #2\nd #3",
   206  	},
   207  	samePrint("aa #c1\n{ #c2\n\tb\n}"),
   208  	{
   209  		"aa #c1\n{ b; c; } #c2",
   210  		"aa #c1\n{\n\tb\n\tc\n} #c2",
   211  	},
   212  	samePrint("a #c1\n'b\ncc' #c2"),
   213  	{
   214  		"(\nbar\n# extra\n)",
   215  		"(\n\tbar\n\t# extra\n)",
   216  	},
   217  	{
   218  		"for a in 1 2\ndo\n\t# bar\ndone",
   219  		"for a in 1 2; do\n\t# bar\ndone",
   220  	},
   221  	samePrint("#before\nfoo | bar"),
   222  	samePrint("#before\nfoo && bar"),
   223  	samePrint("foo | bar # inline"),
   224  	samePrint("foo && bar # inline"),
   225  	samePrint("for a in 1 2; do\n\n\tbar\ndone"),
   226  	{
   227  		"a \\\n\t&& b",
   228  		"a &&\n\tb",
   229  	},
   230  	{
   231  		"a \\\n\t&& b\nc",
   232  		"a &&\n\tb\nc",
   233  	},
   234  	{
   235  		"{\n(a \\\n&& b)\nc\n}",
   236  		"{\n\t(a &&\n\t\tb)\n\tc\n}",
   237  	},
   238  	{
   239  		"a && b \\\n&& c",
   240  		"a && b &&\n\tc",
   241  	},
   242  	{
   243  		"a \\\n&& $(b) && c \\\n&& d",
   244  		"a &&\n\t$(b) && c &&\n\td",
   245  	},
   246  	{
   247  		"a \\\n&& b\nc \\\n&& d",
   248  		"a &&\n\tb\nc &&\n\td",
   249  	},
   250  	{
   251  		"a \\\n&&\n#c\nb",
   252  		"a &&\n\t#c\n\tb",
   253  	},
   254  	{
   255  		"a | {\nb \\\n| c\n}",
   256  		"a | {\n\tb |\n\t\tc\n}",
   257  	},
   258  	{
   259  		"a \\\n\t&& if foo; then\nbar\nfi",
   260  		"a &&\n\tif foo; then\n\t\tbar\n\tfi",
   261  	},
   262  	{
   263  		"if\nfoo\nthen\nbar\nfi",
   264  		"if\n\tfoo\nthen\n\tbar\nfi",
   265  	},
   266  	{
   267  		"if foo \\\nbar\nthen\nbar\nfi",
   268  		"if foo \\\n\tbar; then\n\tbar\nfi",
   269  	},
   270  	{
   271  		"if foo \\\n&& bar\nthen\nbar\nfi",
   272  		"if foo &&\n\tbar; then\n\tbar\nfi",
   273  	},
   274  	{
   275  		"a |\nb |\nc",
   276  		"a |\n\tb |\n\tc",
   277  	},
   278  	samePrint("a |\n\tb | c |\n\td"),
   279  	samePrint("a | b |\n\tc |\n\td"),
   280  	{
   281  		"foo |\n# misplaced\nbar",
   282  		"foo |\n\t# misplaced\n\tbar",
   283  	},
   284  	samePrint("{\n\tfoo\n\t#a\n\tbar\n} | etc"),
   285  	{
   286  		"foo &&\n#a1\n#a2\n$(bar)",
   287  		"foo &&\n\t#a1\n\t#a2\n\t$(bar)",
   288  	},
   289  	{
   290  		"{\n\tfoo\n\t#a\n} |\n# misplaced\nbar",
   291  		"{\n\tfoo\n\t#a\n} |\n\t# misplaced\n\tbar",
   292  	},
   293  	samePrint("foo | bar\n#after"),
   294  	{
   295  		"a |\nb | #c2\nc",
   296  		"a |\n\tb | #c2\n\tc",
   297  	},
   298  	{
   299  		"{\nfoo &&\n#a1\n#a2\n$(bar)\n}",
   300  		"{\n\tfoo &&\n\t\t#a1\n\t\t#a2\n\t\t$(bar)\n}",
   301  	},
   302  	{
   303  		"foo | while read l; do\nbar\ndone",
   304  		"foo | while read l; do\n\tbar\ndone",
   305  	},
   306  	samePrint("while x; do\n\t#comment\ndone"),
   307  	{
   308  		"while x\ndo\n\ty\ndone",
   309  		"while x; do\n\ty\ndone",
   310  	},
   311  	samePrint("\"\\\nfoo\""),
   312  	samePrint("'\\\nfoo'"),
   313  	samePrint("\"foo\\\n  bar\""),
   314  	samePrint("'foo\\\n  bar'"),
   315  	{
   316  		"foo \\\n>bar\netc",
   317  		"foo \\\n\t>bar\netc",
   318  	},
   319  	{
   320  		"foo \\\nfoo2 \\\n>bar",
   321  		"foo \\\n\tfoo2 \\\n\t>bar",
   322  	},
   323  	samePrint("> >(foo)"),
   324  	samePrint("x > >(foo) y"),
   325  	samePrint("a | () |\n\tb"),
   326  	samePrint("a | (\n\tx\n\ty\n) |\n\tb"),
   327  	samePrint("a |\n\tif foo; then\n\t\tbar\n\tfi |\n\tb"),
   328  	samePrint("a | if foo; then\n\tbar\nfi"),
   329  	samePrint("a | b | if foo; then\n\tbar\nfi"),
   330  	{
   331  		"case $i in\n1)\nfoo\n;;\nesac",
   332  		"case $i in\n1)\n\tfoo\n\t;;\nesac",
   333  	},
   334  	{
   335  		"case $i in\n1)\nfoo\nesac",
   336  		"case $i in\n1)\n\tfoo\n\t;;\nesac",
   337  	},
   338  	{
   339  		"case $i in\n1) foo\nesac",
   340  		"case $i in\n1) foo ;;\nesac",
   341  	},
   342  	{
   343  		"case $i in\n1) foo; bar\nesac",
   344  		"case $i in\n1)\n\tfoo\n\tbar\n\t;;\nesac",
   345  	},
   346  	{
   347  		"case $i in\n1) foo; bar;;\nesac",
   348  		"case $i in\n1)\n\tfoo\n\tbar\n\t;;\nesac",
   349  	},
   350  	{
   351  		"case $i in\n1)\n#foo \t\n;;\nesac",
   352  		"case $i in\n1)\n\t#foo\n\t;;\nesac",
   353  	},
   354  	samePrint("case $i in\n1)\n\ta\n\t#b\n\t;;\nesac"),
   355  	samePrint("case $i in\n1) foo() { bar; } ;;\nesac"),
   356  	samePrint("case $i in\n1) ;; #foo\nesac"),
   357  	samePrint("case $i in\n#foo\nesac"),
   358  	samePrint("case $i in\n#before\n1) ;;\nesac"),
   359  	samePrint("case $i in\n#bef\n1) ;; #inl\nesac"),
   360  	samePrint("case $i in\n1) ;; #inl1\n2) ;; #inl2\nesac"),
   361  	samePrint("case $i in\n#bef\n1) #inl\n\tfoo\n\t;;\nesac"),
   362  	samePrint("case $i in\n1) #inl\n\t;;\nesac"),
   363  	samePrint("case $i in\n1) a \\\n\tb ;;\nesac"),
   364  	samePrint("case $i in\n1 | 2 | \\\n\t3 | 4) a b ;;\nesac"),
   365  	samePrint("case $i in\n1 | 2 | \\\n\t3 | 4)\n\ta b\n\t;;\nesac"),
   366  	samePrint("case $i in\nx) ;;\ny) for n in 1; do echo $n; done ;;\nesac"),
   367  	{
   368  		"a=(\nb\nc\n) b=c",
   369  		"a=(\n\tb\n\tc\n) b=c",
   370  	},
   371  	samePrint("a=(\n\t#before\n\tb #inline\n)"),
   372  	samePrint("a=(\n\tb #foo\n\tc #bar\n)"),
   373  	samePrint("a=(\n\tb\n\n\t#foo\n\t#bar\n\tc\n)"),
   374  	samePrint("a=(\n\t#foo\n\t#bar\n\tc\n)"),
   375  	samePrint("a=(\n\t#lone\n)"),
   376  	samePrint("a=(\n\n)"),
   377  	samePrint("foo <<EOF | $(bar)\n3\nEOF"),
   378  	{
   379  		"a <<EOF\n$(\n\tb\n\tc)\nEOF",
   380  		"a <<EOF\n$(\n\tb\n\tc\n)\nEOF",
   381  	},
   382  	{
   383  		"<(<<EOF\nbody\nEOF\n)",
   384  		"<(\n\t<<EOF\nbody\nEOF\n)",
   385  	},
   386  	{
   387  		"( (foo) )\n$( (foo) )\n<( (foo) )",
   388  		"( (foo))\n$( (foo))\n<((foo))",
   389  	},
   390  	samePrint("\"foo\n$(bar)\""),
   391  	samePrint("\"foo\\\n$(bar)\""),
   392  	samePrint("((foo++)) || bar"),
   393  	{
   394  		"a=b \\\nc=d \\\nfoo",
   395  		"a=b \\\n\tc=d \\\n\tfoo",
   396  	},
   397  	{
   398  		"a=b \\\nc=d \\\nfoo \\\nbar",
   399  		"a=b \\\n\tc=d \\\n\tfoo \\\n\tbar",
   400  	},
   401  	samePrint("a $(x) \\\n\tb"),
   402  	samePrint("\"foo\nbar\"\netc"),
   403  	samePrint("\"foo\nbar\nbar2\"\netc"),
   404  	samePrint("a=\"$b\n\"\nd=e"),
   405  	samePrint("\"\n\"\n\nfoo"),
   406  	samePrint("$\"\n\"\n\nfoo"),
   407  	samePrint("'\n'\n\nfoo"),
   408  	samePrint("$'\n'\n\nfoo"),
   409  	samePrint("foo <<EOF\na\nb\nc\nd\nEOF\n{\n\tbar\n}"),
   410  	samePrint("foo bar # one\nif a; then\n\tb\nfi # two"),
   411  	{
   412  		"# foo\n\n\nbar",
   413  		"# foo\n\nbar",
   414  	},
   415  	{
   416  		"# foo\n\n\nbar\nbaz",
   417  		"# foo\n\nbar\nbaz",
   418  	},
   419  	samePrint("#foo\n#\n#bar"),
   420  	{
   421  		"(0 #\n0)#\n0",
   422  		"(\n\t0 #\n\t0\n) #\n0",
   423  	},
   424  	samePrint("a | #c1\n\t(\n\t\tb\n\t)"),
   425  	samePrint("a | #c1\n\t{\n\t\tb\n\t}"),
   426  	samePrint("a | #c1\n\tif b; then\n\t\tc\n\tfi"),
   427  	samePrint("a | #c1\n\t#c2\n\t#c3\n\tb"),
   428  	samePrint("a && #c1\n\t(\n\t\tb\n\t)"),
   429  	samePrint("f() body # comment"),
   430  	samePrint("f <<EOF\nbody\nEOF"),
   431  	samePrint("f <<EOF\nEOF"),
   432  	samePrint("f <<-EOF\n\tbody\nEOF"),
   433  	{
   434  		"f <<-EOF\nbody\nEOF",
   435  		"f <<-EOF\n\tbody\nEOF",
   436  	},
   437  	samePrint("f <<-EOF\nEOF"),
   438  	samePrint("{\n\tf <<EOF\nEOF\n}"),
   439  	samePrint("{\n\tf <<-EOF\n\t\tbody\n\tEOF\n}"),
   440  	samePrint("{\n\tf <<-EOF\n\t\tbody\n\tEOF\n\tf2\n}"),
   441  	samePrint("f <<-EOF\n\t{\n\t\tnicely indented\n\t}\nEOF"),
   442  	samePrint("f <<-EOF\n\t{\n\t\tnicely indented\n\t}\nEOF"),
   443  	{
   444  		"f <<-EOF\n\t{\nbadly indented\n\t}\nEOF",
   445  		"f <<-EOF\n\t{\n\tbadly indented\n\t}\nEOF",
   446  	},
   447  	{
   448  		"f <<-EOF\n\t\t{\n\t\t\ttoo indented\n\t\t}\nEOF",
   449  		"f <<-EOF\n\t{\n\t\ttoo indented\n\t}\nEOF",
   450  	},
   451  	{
   452  		"f <<-EOF\n{\n\ttoo little indented\n}\nEOF",
   453  		"f <<-EOF\n\t{\n\t\ttoo little indented\n\t}\nEOF",
   454  	},
   455  	samePrint("f <<EOF\nEOF\n# comment"),
   456  	samePrint("f <<EOF\nEOF\n# comment\nbar"),
   457  	samePrint("f <<EOF # inline\n$(\n\t# inside\n)\nEOF\n# outside\nbar"),
   458  	{
   459  		"if foo # inline\nthen\n\tbar\nfi",
   460  		"if foo; then # inline\n\tbar\nfi",
   461  	},
   462  	samePrint("for i; do echo $i; done"),
   463  	samePrint("for i in; do echo $i; done"),
   464  	{
   465  		"for foo in a b # inline\ndo\n\tbar\ndone",
   466  		"for foo in a b; do # inline\n\tbar\ndone",
   467  	},
   468  	{
   469  		"if x # inline\nthen bar; fi",
   470  		"if x; then # inline\n\tbar\nfi",
   471  	},
   472  	{
   473  		"for i in a b # inline\ndo bar; done",
   474  		"for i in a b; do # inline\n\tbar\ndone",
   475  	},
   476  	{
   477  		"for i #a\n\tin 1; do #b\ndone",
   478  		"for i in \\\n\t1; do #a\n\t#b\ndone",
   479  	},
   480  	{
   481  		"foo() # inline\n{\n\tbar\n}",
   482  		"foo() { # inline\n\tbar\n}",
   483  	},
   484  	samePrint("if foo; then\n\tbar\n\t# comment\nfi"),
   485  	samePrint("if foo; then\n\tbar\n# else commented out\nfi"),
   486  	samePrint("if foo; then\n\tx\nelse\n\tbar\n\t# comment\nfi"),
   487  	samePrint("if foo; then\n\tx\n#comment\nelse\n\ty\nfi"),
   488  	samePrint("if foo; then\n\tx\n\t#comment\nelse\n\ty\nfi"),
   489  	{
   490  		"if foo; then\n\tx\n#a\n\t#b\n\t#c\nelse\n\ty\nfi",
   491  		"if foo; then\n\tx\n\t#a\n\t#b\n\t#c\nelse\n\ty\nfi",
   492  	},
   493  	samePrint("if foo; then\n\tx\nelse #comment\n\ty\nfi"),
   494  	samePrint("if foo; then\n\tx\n#comment\nelif bar; then\n\ty\nfi"),
   495  	samePrint("if foo; then\n\tx\n\t#comment\nelif bar; then\n\ty\nfi"),
   496  	samePrint("case i in\nx)\n\ta\n\t;;\n#comment\ny) ;;\nesac"),
   497  	samePrint("case i in\nx)\n\ta\n\t;;\n\t#comment\ny) ;;\nesac"),
   498  	{
   499  		"case i in\nx)\n\ta\n\t;;\n\t#a\n#b\n\t#c\ny) ;;\nesac",
   500  		"case i in\nx)\n\ta\n\t;;\n\t#a\n\t#b\n\t#c\ny) ;;\nesac",
   501  	},
   502  }
   503  
   504  func TestPrintWeirdFormat(t *testing.T) {
   505  	t.Parallel()
   506  	parser := NewParser(KeepComments)
   507  	printer := NewPrinter()
   508  	for i, tc := range printTests {
   509  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   510  			printTest(t, parser, printer, tc.in, tc.want)
   511  		})
   512  		t.Run(fmt.Sprintf("%03d-nl", i), func(t *testing.T) {
   513  			printTest(t, parser, printer, "\n"+tc.in+"\n", tc.want)
   514  		})
   515  		t.Run(fmt.Sprintf("%03d-redo", i), func(t *testing.T) {
   516  			printTest(t, parser, printer, tc.want, tc.want)
   517  		})
   518  	}
   519  }
   520  
   521  func parsePath(tb testing.TB, path string) *File {
   522  	f, err := os.Open(path)
   523  	if err != nil {
   524  		tb.Fatal(err)
   525  	}
   526  	defer f.Close()
   527  	prog, err := NewParser(KeepComments).Parse(f, "")
   528  	if err != nil {
   529  		tb.Fatal(err)
   530  	}
   531  	return prog
   532  }
   533  
   534  const canonicalPath = "canonical.sh"
   535  
   536  func TestPrintMultiline(t *testing.T) {
   537  	t.Parallel()
   538  	prog := parsePath(t, canonicalPath)
   539  	got, err := strPrint(NewPrinter(), prog)
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  
   544  	wantBs, err := ioutil.ReadFile(canonicalPath)
   545  	if err != nil {
   546  		t.Fatal(err)
   547  	}
   548  
   549  	// If we're on Windows and it was set up to automatically replace LF
   550  	// with CRLF, that might make this test fail. Just ignore \r characters.
   551  	want := strings.Replace(string(wantBs), "\r", "", -1)
   552  	got = strings.Replace(got, "\r", "", -1)
   553  	if got != want {
   554  		t.Fatalf("Print mismatch in canonical.sh")
   555  	}
   556  }
   557  
   558  func BenchmarkPrint(b *testing.B) {
   559  	prog := parsePath(b, canonicalPath)
   560  	printer := NewPrinter()
   561  	for i := 0; i < b.N; i++ {
   562  		if err := printer.Print(ioutil.Discard, prog); err != nil {
   563  			b.Fatal(err)
   564  		}
   565  	}
   566  }
   567  
   568  func TestPrintSpaces(t *testing.T) {
   569  	t.Parallel()
   570  	var spaceFormats = [...]struct {
   571  		spaces   uint
   572  		in, want string
   573  	}{
   574  		{
   575  			0,
   576  			"{\nfoo \\\nbar\n}",
   577  			"{\n\tfoo \\\n\t\tbar\n}",
   578  		},
   579  		{
   580  			2,
   581  			"{\nfoo \\\nbar\n}",
   582  			"{\n  foo \\\n    bar\n}",
   583  		},
   584  		{
   585  			4,
   586  			"{\nfoo \\\nbar\n}",
   587  			"{\n    foo \\\n        bar\n}",
   588  		},
   589  	}
   590  
   591  	parser := NewParser(KeepComments)
   592  	for i, tc := range spaceFormats {
   593  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   594  			printer := NewPrinter(Indent(tc.spaces))
   595  			printTest(t, parser, printer, tc.in, tc.want)
   596  		})
   597  	}
   598  }
   599  
   600  var errBadWriter = fmt.Errorf("write: expected error")
   601  
   602  type badWriter struct{}
   603  
   604  func (b badWriter) Write(p []byte) (int, error) { return 0, errBadWriter }
   605  
   606  func TestWriteErr(t *testing.T) {
   607  	t.Parallel()
   608  	_ = (*byteCounter)(nil).Flush()
   609  	f := &File{StmtList: StmtList{Stmts: []*Stmt{
   610  		{
   611  			Redirs: []*Redirect{{
   612  				Op:   RdrOut,
   613  				Word: litWord("foo"),
   614  			}},
   615  			Cmd: &Subshell{},
   616  		},
   617  	}}}
   618  	err := NewPrinter().Print(badWriter{}, f)
   619  	if err == nil {
   620  		t.Fatalf("Expected error with bad writer")
   621  	}
   622  	if err != errBadWriter {
   623  		t.Fatalf("Error mismatch with bad writer:\nwant: %v\ngot:  %v",
   624  			errBadWriter, err)
   625  	}
   626  }
   627  
   628  func TestPrintBinaryNextLine(t *testing.T) {
   629  	t.Parallel()
   630  	var tests = [...]printCase{
   631  		{
   632  			"foo <<EOF &&\nl1\nEOF\nbar",
   633  			"foo <<EOF && bar\nl1\nEOF",
   634  		},
   635  		samePrint("a \\\n\t&& b"),
   636  		samePrint("a \\\n\t&& b\nc"),
   637  		{
   638  			"{\n(a \\\n&& b)\nc\n}",
   639  			"{\n\t(a \\\n\t\t&& b)\n\tc\n}",
   640  		},
   641  		{
   642  			"a && b \\\n&& c",
   643  			"a && b \\\n\t&& c",
   644  		},
   645  		{
   646  			"a \\\n&& $(b) && c \\\n&& d",
   647  			"a \\\n\t&& $(b) && c \\\n\t&& d",
   648  		},
   649  		{
   650  			"a \\\n&& b\nc \\\n&& d",
   651  			"a \\\n\t&& b\nc \\\n\t&& d",
   652  		},
   653  		{
   654  			"a | {\nb \\\n| c\n}",
   655  			"a | {\n\tb \\\n\t\t| c\n}",
   656  		},
   657  		{
   658  			"a \\\n\t&& if foo; then\nbar\nfi",
   659  			"a \\\n\t&& if foo; then\n\t\tbar\n\tfi",
   660  		},
   661  		{
   662  			"if foo \\\n&& bar\nthen\nbar\nfi",
   663  			"if foo \\\n\t&& bar; then\n\tbar\nfi",
   664  		},
   665  		{
   666  			"a |\nb |\nc",
   667  			"a \\\n\t| b \\\n\t| c",
   668  		},
   669  		{
   670  			"foo |\n# misplaced\nbar",
   671  			"foo \\\n\t|\n\t# misplaced\n\tbar",
   672  		},
   673  		samePrint("{\n\tfoo\n\t#a\n\tbar\n} | etc"),
   674  		{
   675  			"foo &&\n#a1\n#a2\n$(bar)",
   676  			"foo \\\n\t&&\n\t#a1\n\t#a2\n\t$(bar)",
   677  		},
   678  		{
   679  			"{\n\tfoo\n\t#a\n} |\n# misplaced\nbar",
   680  			"{\n\tfoo\n\t#a\n} \\\n\t|\n\t# misplaced\n\tbar",
   681  		},
   682  		samePrint("foo | bar\n#after"),
   683  		{
   684  			"a |\nb | #c2\nc",
   685  			"a \\\n\t| b \\\n\t|\n\t#c2\n\tc",
   686  		},
   687  	}
   688  	parser := NewParser(KeepComments)
   689  	printer := NewPrinter(BinaryNextLine)
   690  	for i, tc := range tests {
   691  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   692  			printTest(t, parser, printer, tc.in, tc.want)
   693  		})
   694  	}
   695  }
   696  
   697  func TestPrintSwitchCaseIndent(t *testing.T) {
   698  	t.Parallel()
   699  	var tests = [...]printCase{
   700  		{
   701  			"case $i in\n1)\nfoo\n;;\nesac",
   702  			"case $i in\n\t1)\n\t\tfoo\n\t\t;;\nesac",
   703  		},
   704  		{
   705  			"case $i in\n1)\na\n;;\n2)\nb\n;;\nesac",
   706  			"case $i in\n\t1)\n\t\ta\n\t\t;;\n\t2)\n\t\tb\n\t\t;;\nesac",
   707  		},
   708  		samePrint("case $i in\n\t#foo\nesac"),
   709  	}
   710  	parser := NewParser(KeepComments)
   711  	printer := NewPrinter(SwitchCaseIndent)
   712  	for i, tc := range tests {
   713  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   714  			printTest(t, parser, printer, tc.in, tc.want)
   715  		})
   716  	}
   717  }
   718  
   719  func TestPrintSpaceRedirects(t *testing.T) {
   720  	t.Parallel()
   721  	var tests = [...]printCase{
   722  		samePrint("echo foo bar > f"),
   723  		samePrint("echo > f foo bar"),
   724  		samePrint("echo >(cmd)"),
   725  		samePrint("echo > >(cmd)"),
   726  		samePrint("<< EOF\nfoo\nEOF"),
   727  		samePrint("echo 2> f"),
   728  		samePrint("echo foo bar >&1"),
   729  		samePrint("echo 2<&1 foo bar"),
   730  	}
   731  	parser := NewParser(KeepComments)
   732  	printer := NewPrinter(SpaceRedirects)
   733  	for i, tc := range tests {
   734  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   735  			printTest(t, parser, printer, tc.in, tc.want)
   736  		})
   737  	}
   738  }
   739  
   740  func TestPrintKeepPadding(t *testing.T) {
   741  	t.Parallel()
   742  	var tests = [...]printCase{
   743  		samePrint("echo foo bar"),
   744  		samePrint("echo  foo   bar"),
   745  		samePrint("a=b  c=d   bar"),
   746  		samePrint("echo foo    >bar"),
   747  		samePrint("echo foo    2>bar"),
   748  		samePrint("{ foo;  }"),
   749  		samePrint("a()   { foo; }"),
   750  		samePrint("a   && b"),
   751  		samePrint("a   | b"),
   752  		samePrint("a |  b"),
   753  		samePrint("{  a b c; }"),
   754  		samePrint("foo    # x\nbaaar  # y"),
   755  		samePrint("{ { a; }; }"),
   756  		samePrint("{  a;  }"),
   757  		samePrint("(  a   )"),
   758  		samePrint("'foo\nbar'   # x"),
   759  		{"\tfoo", "foo"},
   760  		{"  if foo; then bar; fi", "if   foo; then bar; fi"},
   761  	}
   762  	parser := NewParser(KeepComments)
   763  	printer := NewPrinter(KeepPadding)
   764  	for i, tc := range tests {
   765  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   766  			// ensure that Reset does properly reset colCounter
   767  			printer.WriteByte('x')
   768  			printer.Reset(nil)
   769  			printTest(t, parser, printer, tc.in, tc.want)
   770  		})
   771  	}
   772  }
   773  
   774  func TestPrintMinify(t *testing.T) {
   775  	t.Parallel()
   776  	var tests = [...]printCase{
   777  		samePrint("echo foo bar $a $(b)"),
   778  		{
   779  			"#comment",
   780  			"",
   781  		},
   782  		{
   783  			"foo #comment",
   784  			"foo",
   785  		},
   786  		{
   787  			"foo\n\nbar",
   788  			"foo\nbar",
   789  		},
   790  		{
   791  			"foo &",
   792  			"foo&",
   793  		},
   794  		samePrint("foo >bar 2>baz <etc"),
   795  		{
   796  			"{\n\tfoo\n}",
   797  			"{\nfoo\n}",
   798  		},
   799  		{
   800  			"(\n\ta\n)\n(\n\tb\n\tc\n)",
   801  			"(a)\n(b\nc)",
   802  		},
   803  		{
   804  			"$(\n\ta\n)\n$(\n\tb\n\tc\n)",
   805  			"$(a)\n$(b\nc)",
   806  		},
   807  		{
   808  			"f() { x; }",
   809  			"f(){ x;}",
   810  		},
   811  		{
   812  			"((1 + 2))",
   813  			"((1+2))",
   814  		},
   815  		{
   816  			"echo $a ${b} ${c}-d ${e}f ${g}_h",
   817  			"echo $a $b $c-d ${e}f ${g}_h",
   818  		},
   819  		{
   820  			"echo ${0} ${3} ${10} ${22}",
   821  			"echo $0 $3 ${10} ${22}",
   822  		},
   823  		{
   824  			"case $a in\nx) c ;;\ny | z)\n\td\n\t;;\nesac",
   825  			"case $a in\nx)c;;\ny|z)d\nesac",
   826  		},
   827  		{
   828  			"a && b | c",
   829  			"a&&b|c",
   830  		},
   831  		{
   832  			"a &&\n\tb |\n\tc",
   833  			"a&&b|c",
   834  		},
   835  		{
   836  			"${0/${a}\\\n}",
   837  			"${0/$a/}",
   838  		},
   839  	}
   840  	parser := NewParser(KeepComments)
   841  	printer := NewPrinter(Minify)
   842  	for i, tc := range tests {
   843  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   844  			printTest(t, parser, printer, tc.in, tc.want)
   845  		})
   846  	}
   847  }
   848  
   849  func TestPrintMinifyNotBroken(t *testing.T) {
   850  	t.Parallel()
   851  	parserBash := NewParser(KeepComments)
   852  	parserPosix := NewParser(KeepComments, Variant(LangPOSIX))
   853  	parserMirBSD := NewParser(KeepComments, Variant(LangMirBSDKorn))
   854  	printer := NewPrinter(Minify)
   855  	for i, tc := range fileTests {
   856  		t.Run(fmt.Sprintf("File%03d", i), func(t *testing.T) {
   857  			parser := parserPosix
   858  			if tc.Bash != nil {
   859  				parser = parserBash
   860  			} else if tc.MirBSDKorn != nil {
   861  				parser = parserMirBSD
   862  			}
   863  			in := tc.Strs[0]
   864  			prog, err := parser.Parse(strings.NewReader(in), "")
   865  			if err != nil {
   866  				t.Fatal(err)
   867  			}
   868  			got, err := strPrint(printer, prog)
   869  			if err != nil {
   870  				t.Fatal(err)
   871  			}
   872  			_, err = parser.Parse(strings.NewReader(got), "")
   873  			if err != nil {
   874  				t.Fatalf("minified program was broken: %v\n%s", err, got)
   875  			}
   876  		})
   877  	}
   878  	for i, tc := range printTests {
   879  		t.Run(fmt.Sprintf("Print%03d", i), func(t *testing.T) {
   880  			prog, err := parserBash.Parse(strings.NewReader(tc.in), "")
   881  			if err != nil {
   882  				t.Fatal(err)
   883  			}
   884  			got, err := strPrint(printer, prog)
   885  			if err != nil {
   886  				t.Fatal(err)
   887  			}
   888  			_, err = parserBash.Parse(strings.NewReader(got), "")
   889  			if err != nil {
   890  				t.Fatalf("minified program was broken: %v\n%s", err, got)
   891  			}
   892  		})
   893  	}
   894  }
   895  
   896  func printTest(t *testing.T, parser *Parser, printer *Printer, in, want string) {
   897  	prog, err := parser.Parse(strings.NewReader(in), "")
   898  	if err != nil {
   899  		t.Fatal(err)
   900  	}
   901  	wantNewl := want + "\n"
   902  	got, err := strPrint(printer, prog)
   903  	if err != nil {
   904  		t.Fatal(err)
   905  	}
   906  	if got != wantNewl {
   907  		t.Fatalf("Print mismatch:\nin:\n%s\nwant:\n%sgot:\n%s",
   908  			in, wantNewl, got)
   909  	}
   910  	_, err = parser.Parse(strings.NewReader(want), "")
   911  	if err != nil {
   912  		t.Fatalf("Result is not valid shell:\n%s", want)
   913  	}
   914  }
   915  
   916  func TestPrintNodeTypes(t *testing.T) {
   917  	t.Parallel()
   918  
   919  	multiline, err := NewParser().Parse(strings.NewReader(`
   920  		echo foo
   921  	`), "")
   922  	if err != nil {
   923  		t.Fatal(err)
   924  	}
   925  
   926  	var tests = [...]struct {
   927  		in      Node
   928  		want    string
   929  		wantErr bool
   930  	}{
   931  		{
   932  			in:   &File{StmtList: litStmts("foo")},
   933  			want: "foo\n",
   934  		},
   935  		{
   936  			in:   &File{StmtList: litStmts("foo", "bar")},
   937  			want: "foo\nbar\n",
   938  		},
   939  		{
   940  			in:   litStmt("foo", "bar"),
   941  			want: "foo bar",
   942  		},
   943  		{
   944  			in:   litCall("foo", "bar"),
   945  			want: "foo bar",
   946  		},
   947  		{
   948  			in:   litWord("foo"),
   949  			want: "foo",
   950  		},
   951  		{
   952  			in:   lit("foo"),
   953  			want: "foo",
   954  		},
   955  		{
   956  			in:   sglQuoted("foo"),
   957  			want: "'foo'",
   958  		},
   959  		{
   960  			in:      &Comment{},
   961  			wantErr: true,
   962  		},
   963  		{
   964  			in:   multiline.Stmts[0],
   965  			want: "echo foo",
   966  		},
   967  		{
   968  			in:   multiline.Stmts[0].Cmd,
   969  			want: "echo foo",
   970  		},
   971  		{
   972  			in:   multiline.Stmts[0].Cmd.(*CallExpr).Args[0],
   973  			want: "echo",
   974  		},
   975  		{
   976  			in:   multiline.Stmts[0].Cmd.(*CallExpr).Args[0].Parts[0],
   977  			want: "echo",
   978  		},
   979  	}
   980  	printer := NewPrinter()
   981  	for i, tc := range tests {
   982  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
   983  			got, err := strPrint(printer, tc.in)
   984  			if err == nil && tc.wantErr {
   985  				t.Fatalf("wanted an error but found none")
   986  			} else if err != nil && !tc.wantErr {
   987  				t.Fatalf("didn't want an error but got %v", err)
   988  			}
   989  			if got != tc.want {
   990  				t.Fatalf("Print mismatch:\nwant:\n%s\ngot:\n%s",
   991  					tc.want, got)
   992  			}
   993  		})
   994  	}
   995  }
   996  
   997  func TestPrintManyStmts(t *testing.T) {
   998  	t.Parallel()
   999  	var tests = [...]struct {
  1000  		in, want string
  1001  	}{
  1002  		{"foo; bar", "foo\nbar\n"},
  1003  		{"foo\nbar", "foo\nbar\n"},
  1004  		{"\n\nfoo\nbar\n\n", "foo\nbar\n"},
  1005  		{"foo\nbar <<EOF\nbody\nEOF\n", "foo\nbar <<EOF\nbody\nEOF\n"},
  1006  		{"foo\nbar # inline", "foo\nbar # inline\n"},
  1007  		{"# comment before\nfoo bar", "# comment before\nfoo bar\n"},
  1008  	}
  1009  	parser := NewParser(KeepComments)
  1010  	printer := NewPrinter()
  1011  	for i, tc := range tests {
  1012  		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
  1013  			f, err := parser.Parse(strings.NewReader(tc.in), "")
  1014  			if err != nil {
  1015  				t.Fatal(err)
  1016  			}
  1017  			var buf bytes.Buffer
  1018  			for _, stmt := range f.Stmts {
  1019  				printer.Print(&buf, stmt)
  1020  				buf.WriteByte('\n')
  1021  			}
  1022  			got := buf.String()
  1023  			if got != tc.want {
  1024  				t.Fatalf("Print mismatch:\nwant:\n%s\ngot:\n%s",
  1025  					tc.want, got)
  1026  			}
  1027  		})
  1028  	}
  1029  }