parser_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package parser // import "github.com/docker/docker/builder/dockerfile/parser"
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "strings"
  11. "testing"
  12. "github.com/gotestyourself/gotestyourself/assert"
  13. is "github.com/gotestyourself/gotestyourself/assert/cmp"
  14. )
  15. const testDir = "testfiles"
  16. const negativeTestDir = "testfiles-negative"
  17. const testFileLineInfo = "testfile-line/Dockerfile"
  18. func getDirs(t *testing.T, dir string) []string {
  19. f, err := os.Open(dir)
  20. assert.NilError(t, err)
  21. defer f.Close()
  22. dirs, err := f.Readdirnames(0)
  23. assert.NilError(t, err)
  24. return dirs
  25. }
  26. func TestParseErrorCases(t *testing.T) {
  27. for _, dir := range getDirs(t, negativeTestDir) {
  28. dockerfile := filepath.Join(negativeTestDir, dir, "Dockerfile")
  29. df, err := os.Open(dockerfile)
  30. assert.NilError(t, err, dockerfile)
  31. defer df.Close()
  32. _, err = Parse(df)
  33. assert.Check(t, is.ErrorContains(err, ""), dockerfile)
  34. }
  35. }
  36. func TestParseCases(t *testing.T) {
  37. for _, dir := range getDirs(t, testDir) {
  38. dockerfile := filepath.Join(testDir, dir, "Dockerfile")
  39. resultfile := filepath.Join(testDir, dir, "result")
  40. df, err := os.Open(dockerfile)
  41. assert.NilError(t, err, dockerfile)
  42. defer df.Close()
  43. result, err := Parse(df)
  44. assert.NilError(t, err, dockerfile)
  45. content, err := ioutil.ReadFile(resultfile)
  46. assert.NilError(t, err, resultfile)
  47. if runtime.GOOS == "windows" {
  48. // CRLF --> CR to match Unix behavior
  49. content = bytes.Replace(content, []byte{'\x0d', '\x0a'}, []byte{'\x0a'}, -1)
  50. }
  51. assert.Check(t, is.Equal(result.AST.Dump()+"\n", string(content)), "In "+dockerfile)
  52. }
  53. }
  54. func TestParseWords(t *testing.T) {
  55. tests := []map[string][]string{
  56. {
  57. "input": {"foo"},
  58. "expect": {"foo"},
  59. },
  60. {
  61. "input": {"foo bar"},
  62. "expect": {"foo", "bar"},
  63. },
  64. {
  65. "input": {"foo\\ bar"},
  66. "expect": {"foo\\ bar"},
  67. },
  68. {
  69. "input": {"foo=bar"},
  70. "expect": {"foo=bar"},
  71. },
  72. {
  73. "input": {"foo bar 'abc xyz'"},
  74. "expect": {"foo", "bar", "'abc xyz'"},
  75. },
  76. {
  77. "input": {`foo bar "abc xyz"`},
  78. "expect": {"foo", "bar", `"abc xyz"`},
  79. },
  80. {
  81. "input": {"àöû"},
  82. "expect": {"àöû"},
  83. },
  84. {
  85. "input": {`föo bàr "âbc xÿz"`},
  86. "expect": {"föo", "bàr", `"âbc xÿz"`},
  87. },
  88. }
  89. for _, test := range tests {
  90. words := parseWords(test["input"][0], NewDefaultDirective())
  91. assert.Check(t, is.DeepEqual(test["expect"], words))
  92. }
  93. }
  94. func TestParseIncludesLineNumbers(t *testing.T) {
  95. df, err := os.Open(testFileLineInfo)
  96. assert.NilError(t, err)
  97. defer df.Close()
  98. result, err := Parse(df)
  99. assert.NilError(t, err)
  100. ast := result.AST
  101. assert.Check(t, is.Equal(5, ast.StartLine))
  102. assert.Check(t, is.Equal(31, ast.endLine))
  103. assert.Check(t, is.Len(ast.Children, 3))
  104. expected := [][]int{
  105. {5, 5},
  106. {11, 12},
  107. {17, 31},
  108. }
  109. for i, child := range ast.Children {
  110. msg := fmt.Sprintf("Child %d", i)
  111. assert.Check(t, is.DeepEqual(expected[i], []int{child.StartLine, child.endLine}), msg)
  112. }
  113. }
  114. func TestParseWarnsOnEmptyContinutationLine(t *testing.T) {
  115. dockerfile := bytes.NewBufferString(`
  116. FROM alpine:3.6
  117. RUN something \
  118. following \
  119. more
  120. RUN another \
  121. thing
  122. RUN non-indented \
  123. # this is a comment
  124. after-comment
  125. RUN indented \
  126. # this is an indented comment
  127. comment
  128. `)
  129. result, err := Parse(dockerfile)
  130. assert.NilError(t, err)
  131. warnings := result.Warnings
  132. assert.Check(t, is.Len(warnings, 3))
  133. assert.Check(t, is.Contains(warnings[0], "Empty continuation line found in"))
  134. assert.Check(t, is.Contains(warnings[0], "RUN something following more"))
  135. assert.Check(t, is.Contains(warnings[1], "RUN another thing"))
  136. assert.Check(t, is.Contains(warnings[2], "will become errors in a future release"))
  137. }
  138. func TestParseReturnsScannerErrors(t *testing.T) {
  139. label := strings.Repeat("a", bufio.MaxScanTokenSize)
  140. dockerfile := strings.NewReader(fmt.Sprintf(`
  141. FROM image
  142. LABEL test=%s
  143. `, label))
  144. _, err := Parse(dockerfile)
  145. assert.Check(t, is.Error(err, "dockerfile line greater than max allowed size of 65535"))
  146. }