@@ -2,6 +2,7 @@ package leaktest
2
2
3
3
import (
4
4
"fmt"
5
+ "sync"
5
6
"testing"
6
7
"time"
7
8
)
@@ -16,18 +17,62 @@ func (tr *testReporter) Errorf(format string, args ...interface{}) {
16
17
tr .msg = fmt .Sprintf (format , args )
17
18
}
18
19
19
- func TestCheck (t * testing.T ) {
20
- checker := & testReporter {}
21
-
22
- snapshot := Check (checker )
23
- go func () {
20
+ var leakyFuncs = []func (){
21
+ // Infinite for loop
22
+ func () {
24
23
for {
25
24
time .Sleep (time .Second )
26
25
}
27
- }()
26
+ },
27
+ // Select on a channel not referenced by other goroutines.
28
+ func () {
29
+ c := make (chan struct {}, 0 )
30
+ select {
31
+ case <- c :
32
+ }
33
+ },
34
+ // Blocked select on channels not referenced by other goroutines.
35
+ func () {
36
+ c := make (chan struct {}, 0 )
37
+ c2 := make (chan struct {}, 0 )
38
+ select {
39
+ case <- c :
40
+ case c2 <- struct {}{}:
41
+ }
42
+ },
43
+ // Blocking wait on sync.Mutex that isn't referenced by other goroutines.
44
+ func () {
45
+ var mu sync.Mutex
46
+ mu .Lock ()
47
+ mu .Lock ()
48
+ },
49
+ // Blocking wait on sync.RWMutex that isn't referenced by other goroutines.
50
+ func () {
51
+ var mu sync.RWMutex
52
+ mu .RLock ()
53
+ mu .Lock ()
54
+ },
55
+ func () {
56
+ var mu sync.Mutex
57
+ mu .Lock ()
58
+ c := sync .NewCond (& mu )
59
+ c .Wait ()
60
+ },
61
+ }
28
62
29
- snapshot ()
30
- if ! checker .failed {
31
- t .Errorf ("didn't catch sleeping goroutine" )
63
+ func TestCheck (t * testing.T ) {
64
+
65
+ // this works because the running goroutine is left running at the
66
+ // start of the next test case - so the previous leaks don't affect the
67
+ // check for the next one
68
+ for i , fn := range leakyFuncs {
69
+ checker := & testReporter {}
70
+ snapshot := Check (checker )
71
+ go fn ()
72
+
73
+ snapshot ()
74
+ if ! checker .failed {
75
+ t .Errorf ("didn't catch sleeping goroutine, test #%d" , i )
76
+ }
32
77
}
33
78
}
0 commit comments