Browse Source

Merge pull request #7481 from heyitsanthony/testafter-clientv3

clientv3: use CheckAfterTest after terminating cluster
Xiang Li 8 years ago
parent
commit
5193965005
2 changed files with 22 additions and 5 deletions
  1. 6 0
      clientv3/main_test.go
  2. 16 5
      pkg/testutil/leak.go

+ 6 - 0
clientv3/main_test.go

@@ -15,10 +15,12 @@
 package clientv3_test
 package clientv3_test
 
 
 import (
 import (
+	"fmt"
 	"os"
 	"os"
 	"regexp"
 	"regexp"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+	"time"
 
 
 	"github.com/coreos/etcd/auth"
 	"github.com/coreos/etcd/auth"
 	"github.com/coreos/etcd/integration"
 	"github.com/coreos/etcd/integration"
@@ -50,6 +52,10 @@ func TestMain(m *testing.M) {
 		}
 		}
 		v = m.Run()
 		v = m.Run()
 		clus.Terminate(nil)
 		clus.Terminate(nil)
+		if err := testutil.CheckAfterTest(time.Second); err != nil {
+			fmt.Fprintf(os.Stderr, "%v", err)
+			os.Exit(1)
+		}
 	} else {
 	} else {
 		v = m.Run()
 		v = m.Run()
 	}
 	}

+ 16 - 5
pkg/testutil/leak.go

@@ -62,10 +62,11 @@ func CheckLeakedGoroutine() bool {
 	return true
 	return true
 }
 }
 
 
-func AfterTest(t *testing.T) {
+// CheckAfterTest returns an error if AfterTest would fail with an error.
+func CheckAfterTest(d time.Duration) error {
 	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
 	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
 	if testing.Short() {
 	if testing.Short() {
-		return
+		return nil
 	}
 	}
 	var bad string
 	var bad string
 	badSubstring := map[string]string{
 	badSubstring := map[string]string{
@@ -78,7 +79,8 @@ func AfterTest(t *testing.T) {
 	}
 	}
 
 
 	var stacks string
 	var stacks string
-	for i := 0; i < 6; i++ {
+	begin := time.Now()
+	for time.Since(begin) < d {
 		bad = ""
 		bad = ""
 		stacks = strings.Join(interestingGoroutines(), "\n\n")
 		stacks = strings.Join(interestingGoroutines(), "\n\n")
 		for substr, what := range badSubstring {
 		for substr, what := range badSubstring {
@@ -87,13 +89,22 @@ func AfterTest(t *testing.T) {
 			}
 			}
 		}
 		}
 		if bad == "" {
 		if bad == "" {
-			return
+			return nil
 		}
 		}
 		// Bad stuff found, but goroutines might just still be
 		// Bad stuff found, but goroutines might just still be
 		// shutting down, so give it some time.
 		// shutting down, so give it some time.
 		time.Sleep(50 * time.Millisecond)
 		time.Sleep(50 * time.Millisecond)
 	}
 	}
-	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
+	return fmt.Errorf("appears to have leaked %s:\n%s", bad, stacks)
+}
+
+// AfterTest is meant to run in a defer that executes after a test completes.
+// It will detect common goroutine leaks, retrying in case there are goroutines
+// not synchronously torn down, and fail the test if any goroutines are stuck.
+func AfterTest(t *testing.T) {
+	if err := CheckAfterTest(300 * time.Millisecond); err != nil {
+		t.Errorf("Test %v", err)
+	}
 }
 }
 
 
 func interestingGoroutines() (gs []string) {
 func interestingGoroutines() (gs []string) {