-
Notifications
You must be signed in to change notification settings - Fork 159
Open
Labels
Description
If you have a module with a leak in func leak():
package foo
import (
"sync"
"time"
)
type O struct {
once *sync.Once
quit chan struct{}
}
func NewO() *O {
o := &O{
once: &sync.Once{},
quit: make(chan struct{}),
}
return o
}
func (o *O) leak() {
go func() {
d := 100 * time.Millisecond
for {
select {
case <-o.quit:
return
case <-time.After(2 * d):
time.Sleep(d)
}
}
}()
}
func (o *O) close() {
o.once.Do(func() {
close(o.quit)
})
}
func (*O) run(d time.Duration) {
time.Sleep(d)
}
and tests checking for leaks by running defer goleak.VerifyNone(t)
, that don't run leak(), it will show a leak in TestC, even if the leak is in TestB without defer goleak.VerifyNone(t)
:
package foo
import (
"testing"
"time"
"go.uber.org/goleak"
)
func TestA(t *testing.T) {
defer goleak.VerifyNone(t)
o := NewO()
defer o.close()
o.run(time.Second)
}
func TestB(t *testing.T) {
o := NewO()
o.leak()
o.run(time.Second)
}
func TestC(t *testing.T) {
defer goleak.VerifyNone(t)
o := NewO()
defer o.close()
o.run(time.Second)
}
It depends on order of execution of tests.
Here a test run:
% go test .
--- FAIL: TestC (1.45s)
foo_test.go:30: found unexpected goroutines:
[Goroutine 6 in state select, with foo.(*O).leak.func1 on top of the stack:
goroutine 6 [select]:
foo.(*O).leak.func1()
/tmp/go/goleak/foo.go:25 +0x85
created by foo.(*O).leak
/tmp/go/goleak/foo.go:22 +0x56
]
FAIL
FAIL foo 3.466s
FAIL