diff --git a/integration/z_last_test.go b/integration/main_test.go similarity index 66% rename from integration/z_last_test.go rename to integration/main_test.go index 54d9549302d2..8c8cbfa6d164 100644 --- a/integration/z_last_test.go +++ b/integration/main_test.go @@ -5,7 +5,9 @@ package integration import ( + "fmt" "net/http" + "os" "runtime" "sort" "strings" @@ -26,6 +28,9 @@ func interestingGoroutines() (gs []string) { strings.Contains(stack, "created by testing.RunTests") || strings.Contains(stack, "testing.Main(") || strings.Contains(stack, "runtime.goexit") || + strings.Contains(stack, "github.com/coreos/etcd/integration.interestingGoroutines") || + strings.Contains(stack, "github.com/coreos/etcd/pkg/logutil.(*MergeLogger).outputLoop") || + strings.Contains(stack, "github.com/golang/glog.(*loggingT).flushDaemon") || strings.Contains(stack, "created by runtime.gc") || strings.Contains(stack, "runtime.MHeap_Scavenger") { continue @@ -37,11 +42,18 @@ func interestingGoroutines() (gs []string) { } // Verify the other tests didn't leave any goroutines running. -// This is in a file named z_last_test.go so it sorts at the end. -func TestGoroutinesRunning(t *testing.T) { - t.Skip("TODO: etcdserver.Sender may still dial closed remote endpoint and need some time to timeout.") +func TestMain(m *testing.M) { + v := m.Run() + if v == 0 && goroutineLeaked() { + os.Exit(1) + } + os.Exit(v) +} + +func goroutineLeaked() bool { if testing.Short() { - t.Skip("not counting goroutines for leakage in -short mode") + // not counting goroutines for leakage in -short mode + return false } gs := interestingGoroutines() @@ -52,13 +64,14 @@ func TestGoroutinesRunning(t *testing.T) { n++ } - t.Logf("num goroutines = %d", n) - if n > 0 { - t.Error("Too many goroutines.") - for stack, count := range stackCount { - t.Logf("%d instances of:\n%s", count, stack) - } + if n == 0 { + return false + } + fmt.Fprintf(os.Stderr, "Too many goroutines running after integration test(s).\n") + for stack, count := range stackCount { + fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) } + return true } func afterTest(t *testing.T) { @@ -68,18 +81,12 @@ func afterTest(t *testing.T) { } var bad string badSubstring := map[string]string{ - // TODO: there might exist a bug in http package, which will leave - // readLoop without writeLoop after close all idle connections. - // comment this line until we have time to dig into it. - // ").readLoop(": "a Transport", + ").readLoop(": "a Transport", ").writeLoop(": "a Transport", "created by net/http/httptest.(*Server).Start": "an httptest.Server", "timeoutHandler": "a TimeoutHandler", - // TODO: dial goroutines leaks even if the request is cancelled. - // It needs to wait dial timeout to recycle the goroutine. - // comment this line until we have time to dig into it. - "net.(*netFD).connect(": "a timing out dial", - ").noteClientGone(": "a closenotifier sender", + "net.(*netFD).connect(": "a timing out dial", + ").noteClientGone(": "a closenotifier sender", } var stacks string for i := 0; i < 6; i++ {