run_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "bufio"
  19. "bytes"
  20. "fmt"
  21. "io"
  22. "io/ioutil"
  23. "os"
  24. "os/exec"
  25. "regexp"
  26. "sync"
  27. "testing"
  28. "text/template"
  29. "time"
  30. )
  31. func tmpdir(t *testing.T) string {
  32. dir, err := ioutil.TempDir("", "geth-test")
  33. if err != nil {
  34. t.Fatal(err)
  35. }
  36. return dir
  37. }
  38. type testgeth struct {
  39. // For total convenience, all testing methods are available.
  40. *testing.T
  41. // template variables for expect
  42. Datadir string
  43. Executable string
  44. Func template.FuncMap
  45. removeDatadir bool
  46. cmd *exec.Cmd
  47. stdout *bufio.Reader
  48. stdin io.WriteCloser
  49. stderr *testlogger
  50. }
  51. func init() {
  52. // Run the app if we're the child process for runGeth.
  53. if os.Getenv("GETH_TEST_CHILD") != "" {
  54. app.RunAndExitOnError()
  55. os.Exit(0)
  56. }
  57. }
  58. // spawns geth with the given command line args. If the args don't set --datadir, the
  59. // child g gets a temporary data directory.
  60. func runGeth(t *testing.T, args ...string) *testgeth {
  61. tt := &testgeth{T: t, Executable: os.Args[0]}
  62. for i, arg := range args {
  63. if arg == "-datadir" || arg == "--datadir" {
  64. if i < len(args)-1 {
  65. tt.Datadir = args[i+1]
  66. }
  67. break
  68. }
  69. }
  70. if tt.Datadir == "" {
  71. tt.Datadir = tmpdir(t)
  72. tt.removeDatadir = true
  73. args = append([]string{"-datadir", tt.Datadir}, args...)
  74. // Remove the temporary datadir if something fails below.
  75. defer func() {
  76. if t.Failed() {
  77. os.RemoveAll(tt.Datadir)
  78. }
  79. }()
  80. }
  81. // Boot "geth". This actually runs the test binary but the init function
  82. // will prevent any tests from running.
  83. tt.stderr = &testlogger{t: t}
  84. tt.cmd = exec.Command(os.Args[0], args...)
  85. tt.cmd.Env = append(os.Environ(), "GETH_TEST_CHILD=1")
  86. tt.cmd.Stderr = tt.stderr
  87. stdout, err := tt.cmd.StdoutPipe()
  88. if err != nil {
  89. t.Fatal(err)
  90. }
  91. tt.stdout = bufio.NewReader(stdout)
  92. if tt.stdin, err = tt.cmd.StdinPipe(); err != nil {
  93. t.Fatal(err)
  94. }
  95. if err := tt.cmd.Start(); err != nil {
  96. t.Fatal(err)
  97. }
  98. return tt
  99. }
  100. // InputLine writes the given text to the childs stdin.
  101. // This method can also be called from an expect template, e.g.:
  102. //
  103. // geth.expect(`Passphrase: {{.InputLine "password"}}`)
  104. func (tt *testgeth) InputLine(s string) string {
  105. io.WriteString(tt.stdin, s+"\n")
  106. return ""
  107. }
  108. func (tt *testgeth) setTemplateFunc(name string, fn interface{}) {
  109. if tt.Func == nil {
  110. tt.Func = make(map[string]interface{})
  111. }
  112. tt.Func[name] = fn
  113. }
  114. // expect runs its argument as a template, then expects the
  115. // child process to output the result of the template within 5s.
  116. //
  117. // If the template starts with a newline, the newline is removed
  118. // before matching.
  119. func (tt *testgeth) expect(tplsource string) {
  120. // Generate the expected output by running the template.
  121. tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource))
  122. wantbuf := new(bytes.Buffer)
  123. if err := tpl.Execute(wantbuf, tt); err != nil {
  124. panic(err)
  125. }
  126. // Trim exactly one newline at the beginning. This makes tests look
  127. // much nicer because all expect strings are at column 0.
  128. want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n"))
  129. if err := tt.matchExactOutput(want); err != nil {
  130. tt.Fatal(err)
  131. }
  132. tt.Logf("Matched stdout text:\n%s", want)
  133. }
  134. func (tt *testgeth) matchExactOutput(want []byte) error {
  135. buf := make([]byte, len(want))
  136. n := 0
  137. tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) })
  138. buf = buf[:n]
  139. if n < len(want) || !bytes.Equal(buf, want) {
  140. // Grab any additional buffered output in case of mismatch
  141. // because it might help with debugging.
  142. buf = append(buf, make([]byte, tt.stdout.Buffered())...)
  143. tt.stdout.Read(buf[n:])
  144. // Find the mismatch position.
  145. for i := 0; i < n; i++ {
  146. if want[i] != buf[i] {
  147. return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
  148. buf[:i], buf[i:n], want)
  149. }
  150. }
  151. if n < len(want) {
  152. return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
  153. buf, want[:n], want[n:])
  154. }
  155. }
  156. return nil
  157. }
  158. // expectRegexp expects the child process to output text matching the
  159. // given regular expression within 5s.
  160. //
  161. // Note that an arbitrary amount of output may be consumed by the
  162. // regular expression. This usually means that expect cannot be used
  163. // after expectRegexp.
  164. func (tt *testgeth) expectRegexp(resource string) (*regexp.Regexp, []string) {
  165. var (
  166. re = regexp.MustCompile(resource)
  167. rtee = &runeTee{in: tt.stdout}
  168. matches []int
  169. )
  170. tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) })
  171. output := rtee.buf.Bytes()
  172. if matches == nil {
  173. tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s",
  174. output, resource)
  175. return re, nil
  176. }
  177. tt.Logf("Matched stdout text:\n%s", output)
  178. var submatch []string
  179. for i := 0; i < len(matches); i += 2 {
  180. submatch = append(submatch, string(output[i:i+1]))
  181. }
  182. return re, submatch
  183. }
  184. // expectExit expects the child process to exit within 5s without
  185. // printing any additional text on stdout.
  186. func (tt *testgeth) expectExit() {
  187. var output []byte
  188. tt.withKillTimeout(func() {
  189. output, _ = ioutil.ReadAll(tt.stdout)
  190. })
  191. tt.cmd.Wait()
  192. if tt.removeDatadir {
  193. os.RemoveAll(tt.Datadir)
  194. }
  195. if len(output) > 0 {
  196. tt.Errorf("Unmatched stdout text:\n%s", output)
  197. }
  198. }
  199. func (tt *testgeth) interrupt() {
  200. tt.cmd.Process.Signal(os.Interrupt)
  201. }
  202. // stderrText returns any stderr output written so far.
  203. // The returned text holds all log lines after expectExit has
  204. // returned.
  205. func (tt *testgeth) stderrText() string {
  206. tt.stderr.mu.Lock()
  207. defer tt.stderr.mu.Unlock()
  208. return tt.stderr.buf.String()
  209. }
  210. func (tt *testgeth) withKillTimeout(fn func()) {
  211. timeout := time.AfterFunc(5*time.Second, func() {
  212. tt.Log("killing the child process (timeout)")
  213. tt.cmd.Process.Kill()
  214. if tt.removeDatadir {
  215. os.RemoveAll(tt.Datadir)
  216. }
  217. })
  218. defer timeout.Stop()
  219. fn()
  220. }
  221. // testlogger logs all written lines via t.Log and also
  222. // collects them for later inspection.
  223. type testlogger struct {
  224. t *testing.T
  225. mu sync.Mutex
  226. buf bytes.Buffer
  227. }
  228. func (tl *testlogger) Write(b []byte) (n int, err error) {
  229. lines := bytes.Split(b, []byte("\n"))
  230. for _, line := range lines {
  231. if len(line) > 0 {
  232. tl.t.Logf("(stderr) %s", line)
  233. }
  234. }
  235. tl.mu.Lock()
  236. tl.buf.Write(b)
  237. tl.mu.Unlock()
  238. return len(b), err
  239. }
  240. // runeTee collects text read through it into buf.
  241. type runeTee struct {
  242. in interface {
  243. io.Reader
  244. io.ByteReader
  245. io.RuneReader
  246. }
  247. buf bytes.Buffer
  248. }
  249. func (rtee *runeTee) Read(b []byte) (n int, err error) {
  250. n, err = rtee.in.Read(b)
  251. rtee.buf.Write(b[:n])
  252. return n, err
  253. }
  254. func (rtee *runeTee) ReadRune() (r rune, size int, err error) {
  255. r, size, err = rtee.in.ReadRune()
  256. if err == nil {
  257. rtee.buf.WriteRune(r)
  258. }
  259. return r, size, err
  260. }
  261. func (rtee *runeTee) ReadByte() (b byte, err error) {
  262. b, err = rtee.in.ReadByte()
  263. if err == nil {
  264. rtee.buf.WriteByte(b)
  265. }
  266. return b, err
  267. }