1
0
mirror of https://github.com/golang/go synced 2024-11-11 22:20:22 -07: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:
Roger Peppe 2019-10-16 20:50:17 +01:00 committed by roger peppe
parent fb29e2252c
commit 1b2ff10136
3 changed files with 105 additions and 1 deletions

View File

@ -248,6 +248,16 @@ TODO
</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>
<p>

View File

@ -7,6 +7,7 @@ package testing
import (
"bytes"
"fmt"
"reflect"
"regexp"
"runtime"
"strings"
@ -790,3 +791,67 @@ func TestBenchmark(t *T) {
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)
}
}

View File

@ -344,6 +344,7 @@ type common struct {
skipped bool // Test of benchmark has been skipped.
done bool // Test is finished and all subtests have completed.
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.
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.
type TB interface {
Cleanup(func())
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fail()
@ -550,6 +552,7 @@ type TB interface {
Failed() bool
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Helper()
Log(args ...interface{})
Logf(format string, args ...interface{})
Name() string
@ -557,7 +560,6 @@ type TB interface {
SkipNow()
Skipf(format string, args ...interface{})
Skipped() bool
Helper()
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -774,6 +776,32 @@ func (c *common) Helper() {
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)
// for the caller after skip frames (where 0 means the current function).
func callerName(skip int) string {
@ -919,6 +947,7 @@ func tRunner(t *T, fn func(t *T)) {
}
t.signal <- signal
}()
defer t.runCleanup()
t.start = time.Now()
t.raceErrors = -race.Errors()