mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
testing: implement Cleanup method
Fixes #32111 Change-Id: I7078947889d1e126d9679fb28f27b3fa6ce133ef Reviewed-on: https://go-review.googlesource.com/c/go/+/201359 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
fb29e2252c
commit
1b2ff10136
3 changed files with 105 additions and 1 deletions
|
|
@ -248,6 +248,16 @@ TODO
|
||||||
|
|
||||||
</dl><!-- runtime -->
|
</dl><!-- runtime -->
|
||||||
|
|
||||||
|
<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 201359 -->
|
||||||
|
The testing package now supports cleanup functions, called after
|
||||||
|
a test or benchmark has finished, by calling
|
||||||
|
<a href="/pkg/testing#T.Cleanup"><code>T.Cleanup</code></a> or
|
||||||
|
<a href="/pkg/testing#B.Cleanup"><code>B.Cleanup</code></a> respectively.
|
||||||
|
</p>
|
||||||
|
</dl><!-- testing -->
|
||||||
|
|
||||||
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ package testing
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -790,3 +791,67 @@ func TestBenchmark(t *T) {
|
||||||
t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
|
t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCleanup(t *T) {
|
||||||
|
var cleanups []int
|
||||||
|
t.Run("test", func(t *T) {
|
||||||
|
t.Cleanup(func() { cleanups = append(cleanups, 1) })
|
||||||
|
t.Cleanup(func() { cleanups = append(cleanups, 2) })
|
||||||
|
})
|
||||||
|
if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("unexpected cleanup record; got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConcurrentCleanup(t *T) {
|
||||||
|
cleanups := 0
|
||||||
|
t.Run("test", func(t *T) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
i := i
|
||||||
|
go func() {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanups |= 1 << i
|
||||||
|
})
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
<-done
|
||||||
|
})
|
||||||
|
if cleanups != 1|2 {
|
||||||
|
t.Errorf("unexpected cleanup; got %d want 3", cleanups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanupCalledEvenAfterGoexit(t *T) {
|
||||||
|
cleanups := 0
|
||||||
|
t.Run("test", func(t *T) {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanups++
|
||||||
|
})
|
||||||
|
t.Cleanup(func() {
|
||||||
|
runtime.Goexit()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if cleanups != 1 {
|
||||||
|
t.Errorf("unexpected cleanup count; got %d want 1", cleanups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunCleanup(t *T) {
|
||||||
|
outerCleanup := 0
|
||||||
|
innerCleanup := 0
|
||||||
|
t.Run("test", func(t *T) {
|
||||||
|
t.Cleanup(func() { outerCleanup++ })
|
||||||
|
t.Run("x", func(t *T) {
|
||||||
|
t.Cleanup(func() { innerCleanup++ })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if innerCleanup != 1 {
|
||||||
|
t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
|
||||||
|
}
|
||||||
|
if outerCleanup != 1 {
|
||||||
|
t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,6 +344,7 @@ type common struct {
|
||||||
skipped bool // Test of benchmark has been skipped.
|
skipped bool // Test of benchmark has been skipped.
|
||||||
done bool // Test is finished and all subtests have completed.
|
done bool // Test is finished and all subtests have completed.
|
||||||
helpers map[string]struct{} // functions to be skipped when writing file/line info
|
helpers map[string]struct{} // functions to be skipped when writing file/line info
|
||||||
|
cleanup func() // optional function to be called at the end of the test
|
||||||
|
|
||||||
chatty bool // A copy of the chatty flag.
|
chatty bool // A copy of the chatty flag.
|
||||||
finished bool // Test function has completed.
|
finished bool // Test function has completed.
|
||||||
|
|
@ -543,6 +544,7 @@ func fmtDuration(d time.Duration) string {
|
||||||
|
|
||||||
// TB is the interface common to T and B.
|
// TB is the interface common to T and B.
|
||||||
type TB interface {
|
type TB interface {
|
||||||
|
Cleanup(func())
|
||||||
Error(args ...interface{})
|
Error(args ...interface{})
|
||||||
Errorf(format string, args ...interface{})
|
Errorf(format string, args ...interface{})
|
||||||
Fail()
|
Fail()
|
||||||
|
|
@ -550,6 +552,7 @@ type TB interface {
|
||||||
Failed() bool
|
Failed() bool
|
||||||
Fatal(args ...interface{})
|
Fatal(args ...interface{})
|
||||||
Fatalf(format string, args ...interface{})
|
Fatalf(format string, args ...interface{})
|
||||||
|
Helper()
|
||||||
Log(args ...interface{})
|
Log(args ...interface{})
|
||||||
Logf(format string, args ...interface{})
|
Logf(format string, args ...interface{})
|
||||||
Name() string
|
Name() string
|
||||||
|
|
@ -557,7 +560,6 @@ type TB interface {
|
||||||
SkipNow()
|
SkipNow()
|
||||||
Skipf(format string, args ...interface{})
|
Skipf(format string, args ...interface{})
|
||||||
Skipped() bool
|
Skipped() bool
|
||||||
Helper()
|
|
||||||
|
|
||||||
// A private method to prevent users implementing the
|
// A private method to prevent users implementing the
|
||||||
// interface and so future additions to it will not
|
// interface and so future additions to it will not
|
||||||
|
|
@ -774,6 +776,32 @@ func (c *common) Helper() {
|
||||||
c.helpers[callerName(1)] = struct{}{}
|
c.helpers[callerName(1)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup registers a function to be called when the test finishes.
|
||||||
|
// Cleanup functions will be called in last added, first called
|
||||||
|
// order.
|
||||||
|
func (c *common) Cleanup(f func()) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
oldCleanup := c.cleanup
|
||||||
|
c.cleanup = func() {
|
||||||
|
if oldCleanup != nil {
|
||||||
|
defer oldCleanup()
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCleanup is called at the end of the test.
|
||||||
|
func (c *common) runCleanup() {
|
||||||
|
c.mu.Lock()
|
||||||
|
cleanup := c.cleanup
|
||||||
|
c.cleanup = nil
|
||||||
|
c.mu.Unlock()
|
||||||
|
if cleanup != nil {
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// callerName gives the function name (qualified with a package path)
|
// callerName gives the function name (qualified with a package path)
|
||||||
// for the caller after skip frames (where 0 means the current function).
|
// for the caller after skip frames (where 0 means the current function).
|
||||||
func callerName(skip int) string {
|
func callerName(skip int) string {
|
||||||
|
|
@ -919,6 +947,7 @@ func tRunner(t *T, fn func(t *T)) {
|
||||||
}
|
}
|
||||||
t.signal <- signal
|
t.signal <- signal
|
||||||
}()
|
}()
|
||||||
|
defer t.runCleanup()
|
||||||
|
|
||||||
t.start = time.Now()
|
t.start = time.Now()
|
||||||
t.raceErrors = -race.Errors()
|
t.raceErrors = -race.Errors()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue