test_data_reader.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright 2018 The Cockroach Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. // implied. See the License for the specific language governing
  13. // permissions and limitations under the License.
  14. package datadriven
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io"
  19. "regexp"
  20. "strings"
  21. "testing"
  22. )
  23. type testDataReader struct {
  24. sourceName string
  25. reader io.Reader
  26. scanner *lineScanner
  27. data TestData
  28. rewrite *bytes.Buffer
  29. }
  30. func newTestDataReader(
  31. t *testing.T, sourceName string, file io.Reader, record bool,
  32. ) *testDataReader {
  33. t.Helper()
  34. var rewrite *bytes.Buffer
  35. if record {
  36. rewrite = &bytes.Buffer{}
  37. }
  38. return &testDataReader{
  39. sourceName: sourceName,
  40. reader: file,
  41. scanner: newLineScanner(file),
  42. rewrite: rewrite,
  43. }
  44. }
  45. func (r *testDataReader) Next(t *testing.T) bool {
  46. t.Helper()
  47. r.data = TestData{}
  48. for r.scanner.Scan() {
  49. line := r.scanner.Text()
  50. r.emit(line)
  51. line = strings.TrimSpace(line)
  52. if strings.HasPrefix(line, "#") {
  53. // Skip comment lines.
  54. continue
  55. }
  56. // Support wrapping directive lines using \, for example:
  57. // build-scalar \
  58. // vars(int)
  59. for strings.HasSuffix(line, `\`) && r.scanner.Scan() {
  60. nextLine := r.scanner.Text()
  61. r.emit(nextLine)
  62. line = strings.TrimSuffix(line, `\`) + " " + strings.TrimSpace(nextLine)
  63. }
  64. fields := splitDirectives(t, line)
  65. if len(fields) == 0 {
  66. continue
  67. }
  68. cmd := fields[0]
  69. r.data.Pos = fmt.Sprintf("%s:%d", r.sourceName, r.scanner.line)
  70. r.data.Cmd = cmd
  71. for _, arg := range fields[1:] {
  72. key := arg
  73. var vals []string
  74. if pos := strings.IndexByte(key, '='); pos >= 0 {
  75. key = arg[:pos]
  76. val := arg[pos+1:]
  77. if len(val) > 2 && val[0] == '(' && val[len(val)-1] == ')' {
  78. vals = strings.Split(val[1:len(val)-1], ",")
  79. for i := range vals {
  80. vals[i] = strings.TrimSpace(vals[i])
  81. }
  82. } else {
  83. vals = []string{val}
  84. }
  85. }
  86. r.data.CmdArgs = append(r.data.CmdArgs, CmdArg{Key: key, Vals: vals})
  87. }
  88. var buf bytes.Buffer
  89. var separator bool
  90. for r.scanner.Scan() {
  91. line := r.scanner.Text()
  92. if line == "----" {
  93. separator = true
  94. break
  95. }
  96. r.emit(line)
  97. fmt.Fprintln(&buf, line)
  98. }
  99. r.data.Input = strings.TrimSpace(buf.String())
  100. if separator {
  101. r.readExpected()
  102. }
  103. return true
  104. }
  105. return false
  106. }
  107. func (r *testDataReader) readExpected() {
  108. var buf bytes.Buffer
  109. var line string
  110. var allowBlankLines bool
  111. if r.scanner.Scan() {
  112. line = r.scanner.Text()
  113. if line == "----" {
  114. allowBlankLines = true
  115. }
  116. }
  117. if allowBlankLines {
  118. // Look for two successive lines of "----" before terminating.
  119. for r.scanner.Scan() {
  120. line = r.scanner.Text()
  121. if line == "----" {
  122. if r.scanner.Scan() {
  123. line2 := r.scanner.Text()
  124. if line2 == "----" {
  125. break
  126. }
  127. fmt.Fprintln(&buf, line)
  128. fmt.Fprintln(&buf, line2)
  129. continue
  130. }
  131. }
  132. fmt.Fprintln(&buf, line)
  133. }
  134. } else {
  135. // Terminate on first blank line.
  136. for {
  137. if strings.TrimSpace(line) == "" {
  138. break
  139. }
  140. fmt.Fprintln(&buf, line)
  141. if !r.scanner.Scan() {
  142. break
  143. }
  144. line = r.scanner.Text()
  145. }
  146. }
  147. r.data.Expected = buf.String()
  148. }
  149. func (r *testDataReader) emit(s string) {
  150. if r.rewrite != nil {
  151. r.rewrite.WriteString(s)
  152. r.rewrite.WriteString("\n")
  153. }
  154. }
  155. var splitDirectivesRE = regexp.MustCompile(`^ *[a-zA-Z0-9_,-\.]+(|=[-a-zA-Z0-9_@]+|=\([^)]*\))( |$)`)
  156. // splits a directive line into tokens, where each token is
  157. // either:
  158. // - a,list,of,things
  159. // - argument
  160. // - argument=value
  161. // - argument=(values, ...)
  162. func splitDirectives(t *testing.T, line string) []string {
  163. var res []string
  164. for line != "" {
  165. str := splitDirectivesRE.FindString(line)
  166. if len(str) == 0 {
  167. t.Fatalf("cannot parse directive %s\n", line)
  168. }
  169. res = append(res, strings.TrimSpace(line[0:len(str)]))
  170. line = line[len(str):]
  171. }
  172. return res
  173. }