utesting.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. results := make([]Result, len(tests))
  61. for i, test := range tests {
  62. start := time.Now()
  63. results[i].Name = test.Name
  64. results[i].Failed, results[i].Output = Run(test)
  65. results[i].Duration = time.Since(start)
  66. if report != nil {
  67. printResult(results[i], report)
  68. }
  69. }
  70. return results
  71. }
  72. func printResult(r Result, w io.Writer) {
  73. pd := r.Duration.Truncate(100 * time.Microsecond)
  74. if r.Failed {
  75. fmt.Fprintf(w, "-- FAIL %s (%v)\n", r.Name, pd)
  76. fmt.Fprintln(w, r.Output)
  77. } else {
  78. fmt.Fprintf(w, "-- OK %s (%v)\n", r.Name, pd)
  79. }
  80. }
  81. // CountFailures returns the number of failed tests in the result slice.
  82. func CountFailures(rr []Result) int {
  83. count := 0
  84. for _, r := range rr {
  85. if r.Failed {
  86. count++
  87. }
  88. }
  89. return count
  90. }
  91. // Run executes a single test.
  92. func Run(test Test) (bool, string) {
  93. t := new(T)
  94. done := make(chan struct{})
  95. go func() {
  96. defer close(done)
  97. defer func() {
  98. if err := recover(); err != nil {
  99. buf := make([]byte, 4096)
  100. i := runtime.Stack(buf, false)
  101. t.Logf("panic: %v\n\n%s", err, buf[:i])
  102. t.Fail()
  103. }
  104. }()
  105. test.Fn(t)
  106. }()
  107. <-done
  108. return t.failed, t.output.String()
  109. }
  110. // T is the value given to the test function. The test can signal failures
  111. // and log output by calling methods on this object.
  112. type T struct {
  113. mu sync.Mutex
  114. failed bool
  115. output bytes.Buffer
  116. }
  117. // FailNow marks the test as having failed and stops its execution by calling
  118. // runtime.Goexit (which then runs all deferred calls in the current goroutine).
  119. func (t *T) FailNow() {
  120. t.Fail()
  121. runtime.Goexit()
  122. }
  123. // Fail marks the test as having failed but continues execution.
  124. func (t *T) Fail() {
  125. t.mu.Lock()
  126. defer t.mu.Unlock()
  127. t.failed = true
  128. }
  129. // Failed reports whether the test has failed.
  130. func (t *T) Failed() bool {
  131. t.mu.Lock()
  132. defer t.mu.Unlock()
  133. return t.failed
  134. }
  135. // Log formats its arguments using default formatting, analogous to Println, and records
  136. // the text in the error log.
  137. func (t *T) Log(vs ...interface{}) {
  138. t.mu.Lock()
  139. defer t.mu.Unlock()
  140. fmt.Fprintln(&t.output, vs...)
  141. }
  142. // Logf formats its arguments according to the format, analogous to Printf, and records
  143. // the text in the error log. A final newline is added if not provided.
  144. func (t *T) Logf(format string, vs ...interface{}) {
  145. t.mu.Lock()
  146. defer t.mu.Unlock()
  147. if len(format) == 0 || format[len(format)-1] != '\n' {
  148. format += "\n"
  149. }
  150. fmt.Fprintf(&t.output, format, vs...)
  151. }
  152. // Error is equivalent to Log followed by Fail.
  153. func (t *T) Error(vs ...interface{}) {
  154. t.Log(vs...)
  155. t.Fail()
  156. }
  157. // Errorf is equivalent to Logf followed by Fail.
  158. func (t *T) Errorf(format string, vs ...interface{}) {
  159. t.Logf(format, vs...)
  160. t.Fail()
  161. }
  162. // Fatal is equivalent to Log followed by FailNow.
  163. func (t *T) Fatal(vs ...interface{}) {
  164. t.Log(vs...)
  165. t.FailNow()
  166. }
  167. // Fatalf is equivalent to Logf followed by FailNow.
  168. func (t *T) Fatalf(format string, vs ...interface{}) {
  169. t.Logf(format, vs...)
  170. t.FailNow()
  171. }