| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | // Copyright 2020 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Package utesting provides a standalone replacement for package testing. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This package exists because package testing cannot easily be embedded into a | 
					
						
							|  |  |  | // standalone go program. It provides an API that mirrors the standard library | 
					
						
							|  |  |  | // testing API. | 
					
						
							|  |  |  | package utesting | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test represents a single test. | 
					
						
							|  |  |  | type Test struct { | 
					
						
							|  |  |  | 	Name string | 
					
						
							|  |  |  | 	Fn   func(*T) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Result is the result of a test execution. | 
					
						
							|  |  |  | type Result struct { | 
					
						
							|  |  |  | 	Name     string | 
					
						
							|  |  |  | 	Failed   bool | 
					
						
							|  |  |  | 	Output   string | 
					
						
							|  |  |  | 	Duration time.Duration | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MatchTests returns the tests whose name matches a regular expression. | 
					
						
							|  |  |  | func MatchTests(tests []Test, expr string) []Test { | 
					
						
							|  |  |  | 	var results []Test | 
					
						
							|  |  |  | 	re, err := regexp.Compile(expr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		if re.MatchString(test.Name) { | 
					
						
							|  |  |  | 			results = append(results, test) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return results | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RunTests executes all given tests in order and returns their results. | 
					
						
							|  |  |  | // If the report writer is non-nil, a test report is written to it in real time. | 
					
						
							|  |  |  | func RunTests(tests []Test, report io.Writer) []Result { | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 	if report == nil { | 
					
						
							|  |  |  | 		report = ioutil.Discard | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	results := run(tests, newConsoleOutput(report)) | 
					
						
							|  |  |  | 	fails := CountFailures(results) | 
					
						
							|  |  |  | 	fmt.Fprintf(report, "%v/%v tests passed.\n", len(tests)-fails, len(tests)) | 
					
						
							|  |  |  | 	return results | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RunTAP runs the given tests and writes Test Anything Protocol output | 
					
						
							|  |  |  | // to the report writer. | 
					
						
							|  |  |  | func RunTAP(tests []Test, report io.Writer) []Result { | 
					
						
							|  |  |  | 	return run(tests, newTAP(report, len(tests))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func run(tests []Test, output testOutput) []Result { | 
					
						
							|  |  |  | 	var results = make([]Result, len(tests)) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	for i, test := range tests { | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 		buffer := new(bytes.Buffer) | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 		logOutput := io.MultiWriter(buffer, output) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		output.testStart(test.Name) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 		start := time.Now() | 
					
						
							|  |  |  | 		results[i].Name = test.Name | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 		results[i].Failed = runTest(test, logOutput) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 		results[i].Duration = time.Since(start) | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 		results[i].Output = buffer.String() | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 		output.testResult(results[i]) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return results | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | // testOutput is implemented by output formats. | 
					
						
							|  |  |  | type testOutput interface { | 
					
						
							|  |  |  | 	testStart(name string) | 
					
						
							|  |  |  | 	Write([]byte) (int, error) | 
					
						
							|  |  |  | 	testResult(Result) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // consoleOutput prints test results similarly to go test. | 
					
						
							|  |  |  | type consoleOutput struct { | 
					
						
							|  |  |  | 	out         io.Writer | 
					
						
							|  |  |  | 	indented    *indentWriter | 
					
						
							|  |  |  | 	curTest     string | 
					
						
							|  |  |  | 	wroteHeader bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newConsoleOutput(w io.Writer) *consoleOutput { | 
					
						
							|  |  |  | 	return &consoleOutput{ | 
					
						
							|  |  |  | 		out:      w, | 
					
						
							|  |  |  | 		indented: newIndentWriter(" ", w), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testStart signals the start of a new test. | 
					
						
							|  |  |  | func (c *consoleOutput) testStart(name string) { | 
					
						
							|  |  |  | 	c.curTest = name | 
					
						
							|  |  |  | 	c.wroteHeader = false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write handles test log output. | 
					
						
							|  |  |  | func (c *consoleOutput) Write(b []byte) (int, error) { | 
					
						
							|  |  |  | 	if !c.wroteHeader { | 
					
						
							|  |  |  | 		// This is the first output line from the test. Print a "-- RUN" header. | 
					
						
							|  |  |  | 		fmt.Fprintln(c.out, "-- RUN", c.curTest) | 
					
						
							|  |  |  | 		c.wroteHeader = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return c.indented.Write(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testResult prints the final test result line. | 
					
						
							|  |  |  | func (c *consoleOutput) testResult(r Result) { | 
					
						
							|  |  |  | 	c.indented.flush() | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	pd := r.Duration.Truncate(100 * time.Microsecond) | 
					
						
							|  |  |  | 	if r.Failed { | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 		fmt.Fprintf(c.out, "-- FAIL %s (%v)\n", r.Name, pd) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 		fmt.Fprintf(c.out, "-- OK %s (%v)\n", r.Name, pd) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tapOutput produces Test Anything Protocol v13 output. | 
					
						
							|  |  |  | type tapOutput struct { | 
					
						
							|  |  |  | 	out      io.Writer | 
					
						
							|  |  |  | 	indented *indentWriter | 
					
						
							|  |  |  | 	counter  int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newTAP(out io.Writer, numTests int) *tapOutput { | 
					
						
							|  |  |  | 	fmt.Fprintf(out, "1..%d\n", numTests) | 
					
						
							|  |  |  | 	return &tapOutput{ | 
					
						
							|  |  |  | 		out:      out, | 
					
						
							|  |  |  | 		indented: newIndentWriter("# ", out), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *tapOutput) testStart(name string) { | 
					
						
							|  |  |  | 	t.counter++ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write does nothing for TAP because there is no real-time output of test logs. | 
					
						
							|  |  |  | func (t *tapOutput) Write(b []byte) (int, error) { | 
					
						
							|  |  |  | 	return len(b), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *tapOutput) testResult(r Result) { | 
					
						
							|  |  |  | 	status := "ok" | 
					
						
							|  |  |  | 	if r.Failed { | 
					
						
							|  |  |  | 		status = "not ok" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fmt.Fprintln(t.out, status, t.counter, r.Name) | 
					
						
							|  |  |  | 	t.indented.Write([]byte(r.Output)) | 
					
						
							|  |  |  | 	t.indented.flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // indentWriter indents all written text. | 
					
						
							|  |  |  | type indentWriter struct { | 
					
						
							|  |  |  | 	out    io.Writer | 
					
						
							|  |  |  | 	indent string | 
					
						
							|  |  |  | 	inLine bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newIndentWriter(indent string, out io.Writer) *indentWriter { | 
					
						
							|  |  |  | 	return &indentWriter{out: out, indent: indent} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *indentWriter) Write(b []byte) (n int, err error) { | 
					
						
							|  |  |  | 	for len(b) > 0 { | 
					
						
							|  |  |  | 		if !w.inLine { | 
					
						
							|  |  |  | 			if _, err = io.WriteString(w.out, w.indent); err != nil { | 
					
						
							|  |  |  | 				return n, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.inLine = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		end := bytes.IndexByte(b, '\n') | 
					
						
							|  |  |  | 		if end == -1 { | 
					
						
							|  |  |  | 			nn, err := w.out.Write(b) | 
					
						
							|  |  |  | 			n += nn | 
					
						
							|  |  |  | 			return n, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		line := b[:end+1] | 
					
						
							|  |  |  | 		nn, err := w.out.Write(line) | 
					
						
							|  |  |  | 		n += nn | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return n, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		b = b[end+1:] | 
					
						
							|  |  |  | 		w.inLine = false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // flush ensures the current line is terminated. | 
					
						
							|  |  |  | func (w *indentWriter) flush() { | 
					
						
							|  |  |  | 	if w.inLine { | 
					
						
							|  |  |  | 		fmt.Println(w.out) | 
					
						
							|  |  |  | 		w.inLine = false | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CountFailures returns the number of failed tests in the result slice. | 
					
						
							|  |  |  | func CountFailures(rr []Result) int { | 
					
						
							|  |  |  | 	count := 0 | 
					
						
							|  |  |  | 	for _, r := range rr { | 
					
						
							|  |  |  | 		if r.Failed { | 
					
						
							|  |  |  | 			count++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return count | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run executes a single test. | 
					
						
							|  |  |  | func Run(test Test) (bool, string) { | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	output := new(bytes.Buffer) | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | 	failed := runTest(test, output) | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	return failed, output.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | func runTest(test Test, output io.Writer) bool { | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	t := &T{output: output} | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | 	done := make(chan struct{}) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(done) | 
					
						
							|  |  |  | 		defer func() { | 
					
						
							|  |  |  | 			if err := recover(); err != nil { | 
					
						
							|  |  |  | 				buf := make([]byte, 4096) | 
					
						
							|  |  |  | 				i := runtime.Stack(buf, false) | 
					
						
							|  |  |  | 				t.Logf("panic: %v\n\n%s", err, buf[:i]) | 
					
						
							|  |  |  | 				t.Fail() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 		test.Fn(t) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	<-done | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	return t.failed | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // T is the value given to the test function. The test can signal failures | 
					
						
							|  |  |  | // and log output by calling methods on this object. | 
					
						
							|  |  |  | type T struct { | 
					
						
							|  |  |  | 	mu     sync.Mutex | 
					
						
							|  |  |  | 	failed bool | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	output io.Writer | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 15:02:58 +01:00
										 |  |  | // Helper exists for compatibility with testing.T. | 
					
						
							|  |  |  | func (t *T) Helper() {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | // FailNow marks the test as having failed and stops its execution by calling | 
					
						
							|  |  |  | // runtime.Goexit (which then runs all deferred calls in the current goroutine). | 
					
						
							|  |  |  | func (t *T) FailNow() { | 
					
						
							|  |  |  | 	t.Fail() | 
					
						
							|  |  |  | 	runtime.Goexit() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Fail marks the test as having failed but continues execution. | 
					
						
							|  |  |  | func (t *T) Fail() { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	defer t.mu.Unlock() | 
					
						
							|  |  |  | 	t.failed = true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Failed reports whether the test has failed. | 
					
						
							|  |  |  | func (t *T) Failed() bool { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	defer t.mu.Unlock() | 
					
						
							|  |  |  | 	return t.failed | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Log formats its arguments using default formatting, analogous to Println, and records | 
					
						
							|  |  |  | // the text in the error log. | 
					
						
							|  |  |  | func (t *T) Log(vs ...interface{}) { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	defer t.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	fmt.Fprintln(t.output, vs...) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Logf formats its arguments according to the format, analogous to Printf, and records | 
					
						
							|  |  |  | // the text in the error log. A final newline is added if not provided. | 
					
						
							|  |  |  | func (t *T) Logf(format string, vs ...interface{}) { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	defer t.mu.Unlock() | 
					
						
							|  |  |  | 	if len(format) == 0 || format[len(format)-1] != '\n' { | 
					
						
							|  |  |  | 		format += "\n" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-14 12:28:17 +02:00
										 |  |  | 	fmt.Fprintf(t.output, format, vs...) | 
					
						
							| 
									
										
										
										
											2020-07-07 14:37:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Error is equivalent to Log followed by Fail. | 
					
						
							|  |  |  | func (t *T) Error(vs ...interface{}) { | 
					
						
							|  |  |  | 	t.Log(vs...) | 
					
						
							|  |  |  | 	t.Fail() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Errorf is equivalent to Logf followed by Fail. | 
					
						
							|  |  |  | func (t *T) Errorf(format string, vs ...interface{}) { | 
					
						
							|  |  |  | 	t.Logf(format, vs...) | 
					
						
							|  |  |  | 	t.Fail() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Fatal is equivalent to Log followed by FailNow. | 
					
						
							|  |  |  | func (t *T) Fatal(vs ...interface{}) { | 
					
						
							|  |  |  | 	t.Log(vs...) | 
					
						
							|  |  |  | 	t.FailNow() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Fatalf is equivalent to Logf followed by FailNow. | 
					
						
							|  |  |  | func (t *T) Fatalf(format string, vs ...interface{}) { | 
					
						
							|  |  |  | 	t.Logf(format, vs...) | 
					
						
							|  |  |  | 	t.FailNow() | 
					
						
							|  |  |  | } |