mirror of https://github.com/perkeep/perkeep.git
leak: fix racy test
Based largely on Mathieu Lonjaret's Gerrit CL 2267. Change-Id: I14634b6fc892e84cbdc718a9bb5a1aafca1ec4e9
This commit is contained in:
parent
bf35e5f011
commit
a3d915e5a9
|
@ -43,10 +43,12 @@ func (c *Checker) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checker) finalize() {
|
func (c *Checker) finalize() {
|
||||||
|
if testHookFinalize != nil {
|
||||||
|
defer testHookFinalize()
|
||||||
|
}
|
||||||
if c == nil || c.pc == nil {
|
if c == nil || c.pc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nTestLeaks++ // for testing
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("Leak at:\n")
|
buf.WriteString("Leak at:\n")
|
||||||
for _, pc := range c.pc {
|
for _, pc := range c.pc {
|
||||||
|
@ -57,7 +59,16 @@ func (c *Checker) finalize() {
|
||||||
file, line := f.FileLine(f.Entry())
|
file, line := f.FileLine(f.Entry())
|
||||||
fmt.Fprintf(&buf, " %s:%d\n", file, line)
|
fmt.Fprintf(&buf, " %s:%d\n", file, line)
|
||||||
}
|
}
|
||||||
log.Println(buf.String())
|
onLeak(c, buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
var nTestLeaks int
|
// testHookFinalize optionally specifies a function to run after
|
||||||
|
// finalization. For tests.
|
||||||
|
var testHookFinalize func()
|
||||||
|
|
||||||
|
// onLeak is changed by tests.
|
||||||
|
var onLeak = logLeak
|
||||||
|
|
||||||
|
func logLeak(c *Checker, stack string) {
|
||||||
|
log.Println(stack)
|
||||||
|
}
|
||||||
|
|
|
@ -18,37 +18,57 @@ package leak
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLeak(t *testing.T) {
|
func TestLeak(t *testing.T) {
|
||||||
testLeak(t, false, 1)
|
testLeak(t, true, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoLeak(t *testing.T) {
|
func TestNoLeak(t *testing.T) {
|
||||||
testLeak(t, true, 0)
|
testLeak(t, false, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeak(t *testing.T, close bool, want int) {
|
func testLeak(t *testing.T, leak bool, want int) {
|
||||||
if testing.Short() {
|
defer func() {
|
||||||
// Skipping not because this test is slow, but because finalizers are broken at Go tip during the 1.3 dev cycle:
|
testHookFinalize = nil
|
||||||
// https://code.google.com/p/go/issues/detail?id=7358
|
onLeak = logLeak
|
||||||
// https://code.google.com/p/go/issues/detail?id=7375
|
}()
|
||||||
t.Skip("skipping during short tests")
|
var mu sync.Mutex // guards leaks
|
||||||
|
var leaks []string
|
||||||
|
onLeak = func(_ *Checker, stack string) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
leaks = append(leaks, stack)
|
||||||
}
|
}
|
||||||
|
finalizec := make(chan bool)
|
||||||
|
testHookFinalize = func() {
|
||||||
|
finalizec <- true
|
||||||
|
}
|
||||||
|
|
||||||
c := make(chan bool)
|
c := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
ch := NewChecker()
|
ch := NewChecker()
|
||||||
if close {
|
if !leak {
|
||||||
ch.Close()
|
ch.Close()
|
||||||
}
|
}
|
||||||
c <- true
|
c <- true
|
||||||
}()
|
}()
|
||||||
<-c
|
<-c
|
||||||
leak0 := nTestLeaks
|
go runtime.GC()
|
||||||
runtime.GC()
|
select {
|
||||||
leaks := nTestLeaks - leak0
|
case <-time.After(5 * time.Second):
|
||||||
if leaks != want {
|
t.Error("timeout waiting for finalization")
|
||||||
t.Errorf("got %d leaks; want %d", leaks, want)
|
case <-finalizec:
|
||||||
|
}
|
||||||
|
mu.Lock() // no need to unlock
|
||||||
|
if len(leaks) != want {
|
||||||
|
t.Errorf("got %d leaks; want %d", len(leaks), want)
|
||||||
|
}
|
||||||
|
if len(leaks) == 1 && !strings.Contains(leaks[0], "leak_test.go") {
|
||||||
|
t.Errorf("Leak stack doesn't contain leak_test.go: %s", leaks[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue