utesting.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // Copyright 2020 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser 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. // The go-ethereum library 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 Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package utesting provides a standalone replacement for package testing.
  17. //
  18. // This package exists because package testing cannot easily be embedded into a
  19. // standalone go program. It provides an API that mirrors the standard library
  20. // testing API.
  21. package utesting
  22. import (
  23. "bytes"
  24. "fmt"
  25. "io"
  26. "io/ioutil"
  27. "regexp"
  28. "runtime"
  29. "sync"
  30. "time"
  31. )
  32. // Test represents a single test.
  33. type Test struct {
  34. Name string
  35. Fn func(*T)
  36. }
  37. // Result is the result of a test execution.
  38. type Result struct {
  39. Name string
  40. Failed bool
  41. Output string
  42. Duration time.Duration
  43. }
  44. // MatchTests returns the tests whose name matches a regular expression.
  45. func MatchTests(tests []Test, expr string) []Test {
  46. var results []Test
  47. re, err := regexp.Compile(expr)
  48. if err != nil {
  49. return nil
  50. }
  51. for _, test := range tests {
  52. if re.MatchString(test.Name) {
  53. results = append(results, test)
  54. }
  55. }
  56. return results
  57. }
  58. // RunTests executes all given tests in order and returns their results.
  59. // If the report writer is non-nil, a test report is written to it in real time.
  60. func RunTests(tests []Test, report io.Writer) []Result {
  61. if report == nil {
  62. report = ioutil.Discard
  63. }
  64. results := run(tests, newConsoleOutput(report))
  65. fails := CountFailures(results)
  66. fmt.Fprintf(report, "%v/%v tests passed.\n", len(tests)-fails, len(tests))
  67. return results
  68. }
  69. // RunTAP runs the given tests and writes Test Anything Protocol output
  70. // to the report writer.
  71. func RunTAP(tests []Test, report io.Writer) []Result {
  72. return run(tests, newTAP(report, len(tests)))
  73. }
  74. func run(tests []Test, output testOutput) []Result {
  75. var results = make([]Result, len(tests))
  76. for i, test := range tests {
  77. buffer := new(bytes.Buffer)
  78. logOutput := io.MultiWriter(buffer, output)
  79. output.testStart(test.Name)
  80. start := time.Now()
  81. results[i].Name = test.Name
  82. results[i].Failed = runTest(test, logOutput)
  83. results[i].Duration = time.Since(start)
  84. results[i].Output = buffer.String()
  85. output.testResult(results[i])
  86. }
  87. return results
  88. }
  89. // testOutput is implemented by output formats.
  90. type testOutput interface {
  91. testStart(name string)
  92. Write([]byte) (int, error)
  93. testResult(Result)
  94. }
  95. // consoleOutput prints test results similarly to go test.
  96. type consoleOutput struct {
  97. out io.Writer
  98. indented *indentWriter
  99. curTest string
  100. wroteHeader bool
  101. }
  102. func newConsoleOutput(w io.Writer) *consoleOutput {
  103. return &consoleOutput{
  104. out: w,
  105. indented: newIndentWriter(" ", w),
  106. }
  107. }
  108. // testStart signals the start of a new test.
  109. func (c *consoleOutput) testStart(name string) {
  110. c.curTest = name
  111. c.wroteHeader = false
  112. }
  113. // Write handles test log output.
  114. func (c *consoleOutput) Write(b []byte) (int, error) {
  115. if !c.wroteHeader {
  116. // This is the first output line from the test. Print a "-- RUN" header.
  117. fmt.Fprintln(c.out, "-- RUN", c.curTest)
  118. c.wroteHeader = true
  119. }
  120. return c.indented.Write(b)
  121. }
  122. // testResult prints the final test result line.
  123. func (c *consoleOutput) testResult(r Result) {
  124. c.indented.flush()
  125. pd := r.Duration.Truncate(100 * time.Microsecond)
  126. if r.Failed {
  127. fmt.Fprintf(c.out, "-- FAIL %s (%v)\n", r.Name, pd)
  128. } else {
  129. fmt.Fprintf(c.out, "-- OK %s (%v)\n", r.Name, pd)
  130. }
  131. }
  132. // tapOutput produces Test Anything Protocol v13 output.
  133. type tapOutput struct {
  134. out io.Writer
  135. indented *indentWriter
  136. counter int
  137. }
  138. func newTAP(out io.Writer, numTests int) *tapOutput {
  139. fmt.Fprintf(out, "1..%d\n", numTests)
  140. return &tapOutput{
  141. out: out,
  142. indented: newIndentWriter("# ", out),
  143. }
  144. }
  145. func (t *tapOutput) testStart(name string) {
  146. t.counter++
  147. }
  148. // Write does nothing for TAP because there is no real-time output of test logs.
  149. func (t *tapOutput) Write(b []byte) (int, error) {
  150. return len(b), nil
  151. }
  152. func (t *tapOutput) testResult(r Result) {
  153. status := "ok"
  154. if r.Failed {
  155. status = "not ok"
  156. }
  157. fmt.Fprintln(t.out, status, t.counter, r.Name)
  158. t.indented.Write([]byte(r.Output))
  159. t.indented.flush()
  160. }
  161. // indentWriter indents all written text.
  162. type indentWriter struct {
  163. out io.Writer
  164. indent string
  165. inLine bool
  166. }
  167. func newIndentWriter(indent string, out io.Writer) *indentWriter {
  168. return &indentWriter{out: out, indent: indent}
  169. }
  170. func (w *indentWriter) Write(b []byte) (n int, err error) {
  171. for len(b) > 0 {
  172. if !w.inLine {
  173. if _, err = io.WriteString(w.out, w.indent); err != nil {
  174. return n, err
  175. }
  176. w.inLine = true
  177. }
  178. end := bytes.IndexByte(b, '\n')
  179. if end == -1 {
  180. nn, err := w.out.Write(b)
  181. n += nn
  182. return n, err
  183. }
  184. line := b[:end+1]
  185. nn, err := w.out.Write(line)
  186. n += nn
  187. if err != nil {
  188. return n, err
  189. }
  190. b = b[end+1:]
  191. w.inLine = false
  192. }
  193. return n, err
  194. }
  195. // flush ensures the current line is terminated.
  196. func (w *indentWriter) flush() {
  197. if w.inLine {
  198. fmt.Println(w.out)
  199. w.inLine = false
  200. }
  201. }
  202. // CountFailures returns the number of failed tests in the result slice.
  203. func CountFailures(rr []Result) int {
  204. count := 0
  205. for _, r := range rr {
  206. if r.Failed {
  207. count++
  208. }
  209. }
  210. return count
  211. }
  212. // Run executes a single test.
  213. func Run(test Test) (bool, string) {
  214. output := new(bytes.Buffer)
  215. failed := runTest(test, output)
  216. return failed, output.String()
  217. }
  218. func runTest(test Test, output io.Writer) bool {
  219. t := &T{output: output}
  220. done := make(chan struct{})
  221. go func() {
  222. defer close(done)
  223. defer func() {
  224. if err := recover(); err != nil {
  225. buf := make([]byte, 4096)
  226. i := runtime.Stack(buf, false)
  227. t.Logf("panic: %v\n\n%s", err, buf[:i])
  228. t.Fail()
  229. }
  230. }()
  231. test.Fn(t)
  232. }()
  233. <-done
  234. return t.failed
  235. }
  236. // T is the value given to the test function. The test can signal failures
  237. // and log output by calling methods on this object.
  238. type T struct {
  239. mu sync.Mutex
  240. failed bool
  241. output io.Writer
  242. }
  243. // Helper exists for compatibility with testing.T.
  244. func (t *T) Helper() {}
  245. // FailNow marks the test as having failed and stops its execution by calling
  246. // runtime.Goexit (which then runs all deferred calls in the current goroutine).
  247. func (t *T) FailNow() {
  248. t.Fail()
  249. runtime.Goexit()
  250. }
  251. // Fail marks the test as having failed but continues execution.
  252. func (t *T) Fail() {
  253. t.mu.Lock()
  254. defer t.mu.Unlock()
  255. t.failed = true
  256. }
  257. // Failed reports whether the test has failed.
  258. func (t *T) Failed() bool {
  259. t.mu.Lock()
  260. defer t.mu.Unlock()
  261. return t.failed
  262. }
  263. // Log formats its arguments using default formatting, analogous to Println, and records
  264. // the text in the error log.
  265. func (t *T) Log(vs ...interface{}) {
  266. t.mu.Lock()
  267. defer t.mu.Unlock()
  268. fmt.Fprintln(t.output, vs...)
  269. }
  270. // Logf formats its arguments according to the format, analogous to Printf, and records
  271. // the text in the error log. A final newline is added if not provided.
  272. func (t *T) Logf(format string, vs ...interface{}) {
  273. t.mu.Lock()
  274. defer t.mu.Unlock()
  275. if len(format) == 0 || format[len(format)-1] != '\n' {
  276. format += "\n"
  277. }
  278. fmt.Fprintf(t.output, format, vs...)
  279. }
  280. // Error is equivalent to Log followed by Fail.
  281. func (t *T) Error(vs ...interface{}) {
  282. t.Log(vs...)
  283. t.Fail()
  284. }
  285. // Errorf is equivalent to Logf followed by Fail.
  286. func (t *T) Errorf(format string, vs ...interface{}) {
  287. t.Logf(format, vs...)
  288. t.Fail()
  289. }
  290. // Fatal is equivalent to Log followed by FailNow.
  291. func (t *T) Fatal(vs ...interface{}) {
  292. t.Log(vs...)
  293. t.FailNow()
  294. }
  295. // Fatalf is equivalent to Logf followed by FailNow.
  296. func (t *T) Fatalf(format string, vs ...interface{}) {
  297. t.Logf(format, vs...)
  298. t.FailNow()
  299. }