github.com/aswedchain/aswed@v1.0.1/internal/utesting/utesting.go (about) 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 17 // Package utesting provides a standalone replacement for package testing. 18 // 19 // This package exists because package testing cannot easily be embedded into a 20 // standalone go program. It provides an API that mirrors the standard library 21 // testing API. 22 package utesting 23 24 import ( 25 "bytes" 26 "fmt" 27 "io" 28 "regexp" 29 "runtime" 30 "sync" 31 "time" 32 ) 33 34 // Test represents a single test. 35 type Test struct { 36 Name string 37 Fn func(*T) 38 } 39 40 // Result is the result of a test execution. 41 type Result struct { 42 Name string 43 Failed bool 44 Output string 45 Duration time.Duration 46 } 47 48 // MatchTests returns the tests whose name matches a regular expression. 49 func MatchTests(tests []Test, expr string) []Test { 50 var results []Test 51 re, err := regexp.Compile(expr) 52 if err != nil { 53 return nil 54 } 55 for _, test := range tests { 56 if re.MatchString(test.Name) { 57 results = append(results, test) 58 } 59 } 60 return results 61 } 62 63 // RunTests executes all given tests in order and returns their results. 64 // If the report writer is non-nil, a test report is written to it in real time. 65 func RunTests(tests []Test, report io.Writer) []Result { 66 results := make([]Result, len(tests)) 67 for i, test := range tests { 68 var output io.Writer 69 buffer := new(bytes.Buffer) 70 output = buffer 71 if report != nil { 72 output = io.MultiWriter(buffer, report) 73 } 74 start := time.Now() 75 results[i].Name = test.Name 76 results[i].Failed = run(test, output) 77 results[i].Duration = time.Since(start) 78 results[i].Output = buffer.String() 79 if report != nil { 80 printResult(results[i], report) 81 } 82 } 83 return results 84 } 85 86 func printResult(r Result, w io.Writer) { 87 pd := r.Duration.Truncate(100 * time.Microsecond) 88 if r.Failed { 89 fmt.Fprintf(w, "-- FAIL %s (%v)\n", r.Name, pd) 90 } else { 91 fmt.Fprintf(w, "-- OK %s (%v)\n", r.Name, pd) 92 } 93 } 94 95 // CountFailures returns the number of failed tests in the result slice. 96 func CountFailures(rr []Result) int { 97 count := 0 98 for _, r := range rr { 99 if r.Failed { 100 count++ 101 } 102 } 103 return count 104 } 105 106 // Run executes a single test. 107 func Run(test Test) (bool, string) { 108 output := new(bytes.Buffer) 109 failed := run(test, output) 110 return failed, output.String() 111 } 112 113 func run(test Test, output io.Writer) bool { 114 t := &T{output: output} 115 done := make(chan struct{}) 116 go func() { 117 defer close(done) 118 defer func() { 119 if err := recover(); err != nil { 120 buf := make([]byte, 4096) 121 i := runtime.Stack(buf, false) 122 t.Logf("panic: %v\n\n%s", err, buf[:i]) 123 t.Fail() 124 } 125 }() 126 test.Fn(t) 127 }() 128 <-done 129 return t.failed 130 } 131 132 // T is the value given to the test function. The test can signal failures 133 // and log output by calling methods on this object. 134 type T struct { 135 mu sync.Mutex 136 failed bool 137 output io.Writer 138 } 139 140 // FailNow marks the test as having failed and stops its execution by calling 141 // runtime.Goexit (which then runs all deferred calls in the current goroutine). 142 func (t *T) FailNow() { 143 t.Fail() 144 runtime.Goexit() 145 } 146 147 // Fail marks the test as having failed but continues execution. 148 func (t *T) Fail() { 149 t.mu.Lock() 150 defer t.mu.Unlock() 151 t.failed = true 152 } 153 154 // Failed reports whether the test has failed. 155 func (t *T) Failed() bool { 156 t.mu.Lock() 157 defer t.mu.Unlock() 158 return t.failed 159 } 160 161 // Log formats its arguments using default formatting, analogous to Println, and records 162 // the text in the error log. 163 func (t *T) Log(vs ...interface{}) { 164 t.mu.Lock() 165 defer t.mu.Unlock() 166 fmt.Fprintln(t.output, vs...) 167 } 168 169 // Logf formats its arguments according to the format, analogous to Printf, and records 170 // the text in the error log. A final newline is added if not provided. 171 func (t *T) Logf(format string, vs ...interface{}) { 172 t.mu.Lock() 173 defer t.mu.Unlock() 174 if len(format) == 0 || format[len(format)-1] != '\n' { 175 format += "\n" 176 } 177 fmt.Fprintf(t.output, format, vs...) 178 } 179 180 // Error is equivalent to Log followed by Fail. 181 func (t *T) Error(vs ...interface{}) { 182 t.Log(vs...) 183 t.Fail() 184 } 185 186 // Errorf is equivalent to Logf followed by Fail. 187 func (t *T) Errorf(format string, vs ...interface{}) { 188 t.Logf(format, vs...) 189 t.Fail() 190 } 191 192 // Fatal is equivalent to Log followed by FailNow. 193 func (t *T) Fatal(vs ...interface{}) { 194 t.Log(vs...) 195 t.FailNow() 196 } 197 198 // Fatalf is equivalent to Logf followed by FailNow. 199 func (t *T) Fatalf(format string, vs ...interface{}) { 200 t.Logf(format, vs...) 201 t.FailNow() 202 }