utesting.go 8.0 KB

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