Skip to content

Commit 569b539

Browse files
committed
testing: implement testing.Cleanup
1 parent cb36c36 commit 569b539

File tree

2 files changed

+133
-4
lines changed

2 files changed

+133
-4
lines changed

src/testing/sub_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package testing
6+
7+
import (
8+
"reflect"
9+
)
10+
11+
func TestCleanup(t *T) {
12+
var cleanups []int
13+
t.Run("test", func(t *T) {
14+
t.Cleanup(func() { cleanups = append(cleanups, 1) })
15+
t.Cleanup(func() { cleanups = append(cleanups, 2) })
16+
})
17+
if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
18+
t.Errorf("unexpected cleanup record; got %v want %v", got, want)
19+
}
20+
}
21+
22+
func TestConcurrentCleanup(t *T) {
23+
cleanups := 0
24+
t.Run("test", func(t *T) {
25+
done := make(chan struct{})
26+
for i := 0; i < 2; i++ {
27+
i := i
28+
go func() {
29+
t.Cleanup(func() {
30+
cleanups |= 1 << i
31+
})
32+
done <- struct{}{}
33+
}()
34+
}
35+
<-done
36+
<-done
37+
})
38+
if cleanups != 1|2 {
39+
t.Errorf("unexpected cleanup; got %d want 3", cleanups)
40+
}
41+
}
42+
43+
func TestRunCleanup(t *T) {
44+
outerCleanup := 0
45+
innerCleanup := 0
46+
t.Run("test", func(t *T) {
47+
t.Cleanup(func() { outerCleanup++ })
48+
t.Run("x", func(t *T) {
49+
t.Cleanup(func() { innerCleanup++ })
50+
})
51+
})
52+
if innerCleanup != 1 {
53+
t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
54+
}
55+
if outerCleanup != 1 {
56+
t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
57+
}
58+
}
59+
60+
func TestCleanupParallelSubtests(t *T) {
61+
ranCleanup := 0
62+
t.Run("test", func(t *T) {
63+
t.Cleanup(func() { ranCleanup++ })
64+
t.Run("x", func(t *T) {
65+
t.Parallel()
66+
if ranCleanup > 0 {
67+
t.Error("outer cleanup ran before parallel subtest")
68+
}
69+
})
70+
})
71+
if ranCleanup != 1 {
72+
t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
73+
}
74+
}
75+
76+
func TestNestedCleanup(t *T) {
77+
ranCleanup := 0
78+
t.Run("test", func(t *T) {
79+
t.Cleanup(func() {
80+
if ranCleanup != 2 {
81+
t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
82+
}
83+
ranCleanup++
84+
})
85+
t.Cleanup(func() {
86+
if ranCleanup != 0 {
87+
t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
88+
}
89+
ranCleanup++
90+
t.Cleanup(func() {
91+
if ranCleanup != 1 {
92+
t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
93+
}
94+
ranCleanup++
95+
})
96+
})
97+
})
98+
if ranCleanup != 3 {
99+
t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
100+
}
101+
}

src/testing/testing.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ type common struct {
4747
w io.Writer // either &output, or at top level, os.Stdout
4848
indent string
4949

50-
failed bool // Test or benchmark has failed.
51-
skipped bool // Test of benchmark has been skipped.
52-
finished bool // Test function has completed.
53-
name string // Name of test or benchmark.
50+
failed bool // Test or benchmark has failed.
51+
skipped bool // Test of benchmark has been skipped.
52+
finished bool // Test function has completed.
53+
cleanups []func() // optional functions to be called at the end of the test
5454
parent *common
55+
name string // Name of test or benchmark.
5556
}
5657

5758
// TB is the interface common to T and B.
@@ -207,7 +208,34 @@ func (c *common) Parallel() {
207208
// Unimplemented.
208209
}
209210

211+
// Cleanup registers a function to be called when the test (or subtest) and all its
212+
// subtests complete. Cleanup functions will be called in last added,
213+
// first called order.
214+
func (c *common) Cleanup(f func()) {
215+
c.cleanups = append(c.cleanups, f)
216+
}
217+
218+
// runCleanup is called at the end of the test.
219+
func (c *common) runCleanup() {
220+
for {
221+
var cleanup func()
222+
if len(c.cleanups) > 0 {
223+
last := len(c.cleanups) - 1
224+
cleanup = c.cleanups[last]
225+
c.cleanups = c.cleanups[:last]
226+
}
227+
if cleanup == nil {
228+
return
229+
}
230+
cleanup()
231+
}
232+
}
233+
210234
func tRunner(t *T, fn func(t *T)) {
235+
defer func() {
236+
t.runCleanup()
237+
}()
238+
211239
// Run the test.
212240
if flagVerbose {
213241
fmt.Fprintf(t.w, "=== RUN %s\n", t.name)

0 commit comments

Comments
 (0)