44 "context"
55 "errors"
66 "fmt"
7+ "net/http"
8+ "net/http/httptest"
79 "sync"
810 "testing"
911 "time"
@@ -19,60 +21,116 @@ func (tr *testReporter) Errorf(format string, args ...interface{}) {
1921 tr .msg = fmt .Sprintf (format , args ... )
2022}
2123
22- var leakyFuncs = []func (){
23- // Infinite for loop
24- func () {
25- for {
26- time .Sleep (time .Second )
27- }
28- },
29- // Select on a channel not referenced by other goroutines.
30- func () {
31- c := make (chan struct {})
32- <- c
33- },
34- // Blocked select on channels not referenced by other goroutines.
35- func () {
36- c := make (chan struct {})
37- c2 := make (chan struct {})
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- }
24+ // Client for the TestServer
25+ var testServer * httptest.Server
6226
6327func TestCheck (t * testing.T ) {
28+ leakyFuncs := []struct {
29+ f func ()
30+ name string
31+ expectLeak bool
32+ }{
33+ {
34+ name : "Infinite for loop" ,
35+ expectLeak : true ,
36+ f : func () {
37+ for {
38+ time .Sleep (time .Second )
39+ }
40+ },
41+ },
42+ {
43+ name : "Select on a channel not referenced by other goroutines." ,
44+ expectLeak : true ,
45+ f : func () {
46+ c := make (chan struct {})
47+ <- c
48+ },
49+ },
50+ {
51+ name : "Blocked select on channels not referenced by other goroutines." ,
52+ expectLeak : true ,
53+ f : func () {
54+ c := make (chan struct {})
55+ c2 := make (chan struct {})
56+ select {
57+ case <- c :
58+ case c2 <- struct {}{}:
59+ }
60+ },
61+ },
62+ {
63+ name : "Blocking wait on sync.Mutex that isn't referenced by other goroutines." ,
64+ expectLeak : true ,
65+ f : func () {
66+ var mu sync.Mutex
67+ mu .Lock ()
68+ mu .Lock ()
69+ },
70+ },
71+ {
72+ name : "Blocking wait on sync.RWMutex that isn't referenced by other goroutines." ,
73+ expectLeak : true ,
74+ f : func () {
75+ var mu sync.RWMutex
76+ mu .RLock ()
77+ mu .Lock ()
78+ },
79+ },
80+ {
81+ name : "HTTP Client with KeepAlive Disabled." ,
82+ expectLeak : false ,
83+ f : func () {
84+ tr := & http.Transport {
85+ DisableKeepAlives : true ,
86+ }
87+ client := & http.Client {Transport : tr }
88+ _ , err := client .Get (testServer .URL )
89+ if err != nil {
90+ t .Error (err )
91+ }
92+ },
93+ },
94+ {
95+ name : "HTTP Client with KeepAlive Enabled." ,
96+ expectLeak : true ,
97+ f : func () {
98+ tr := & http.Transport {
99+ DisableKeepAlives : false ,
100+ }
101+ client := & http.Client {Transport : tr }
102+ _ , err := client .Get (testServer .URL )
103+ if err != nil {
104+ t .Error (err )
105+ }
106+ },
107+ },
108+ }
109+
110+ // Start our keep alive server for keep alive tests
111+ ctx , cancel := context .WithCancel (context .Background ())
112+ defer cancel ()
113+ testServer = startKeepAliveEnabledServer (ctx )
114+
64115 // this works because the running goroutine is left running at the
65116 // start of the next test case - so the previous leaks don't affect the
66117 // check for the next one
67- for i , fn := range leakyFuncs {
68- checker := & testReporter {}
69- snapshot := CheckTimeout (checker , time .Second )
70- go fn ()
71-
72- snapshot ()
73- if ! checker .failed {
74- t .Errorf ("didn't catch sleeping goroutine, test #%d" , i )
75- }
118+ for _ , leakyTestcase := range leakyFuncs {
119+
120+ t .Run (leakyTestcase .name , func (t * testing.T ) {
121+ checker := & testReporter {}
122+ snapshot := CheckTimeout (checker , time .Second )
123+ go leakyTestcase .f ()
124+
125+ snapshot ()
126+
127+ if ! checker .failed && leakyTestcase .expectLeak {
128+ t .Error ("didn't catch sleeping goroutine" )
129+ }
130+ if checker .failed && ! leakyTestcase .expectLeak {
131+ t .Error ("got leak but didn't expect it" )
132+ }
133+ })
76134 }
77135}
78136
@@ -133,6 +191,10 @@ func TestInterestingGoroutine(t *testing.T) {
133191 stack : "goroutine 123 [running]:" ,
134192 err : errors .New (`error parsing stack: "goroutine 123 [running]:"` ),
135193 },
194+ {
195+ stack : "goroutine 299 [IO wait]:\n net/http.(*persistConn).readLoop(0xc420556240)" ,
196+ err : nil ,
197+ },
136198 {
137199 stack : "goroutine 123 [running]:\n testing.RunTests" ,
138200 err : nil ,
0 commit comments