|  | // Copyright 2013 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package http_test | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "log" | 
|  | "net/http" | 
|  | "os" | 
|  | "runtime" | 
|  | "sort" | 
|  | "strings" | 
|  | "testing" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | var quietLog = log.New(ioutil.Discard, "", 0) | 
|  |  | 
|  | func TestMain(m *testing.M) { | 
|  | v := m.Run() | 
|  | if v == 0 && goroutineLeaked() { | 
|  | os.Exit(1) | 
|  | } | 
|  | os.Exit(v) | 
|  | } | 
|  |  | 
|  | func interestingGoroutines() (gs []string) { | 
|  | buf := make([]byte, 2<<20) | 
|  | buf = buf[:runtime.Stack(buf, true)] | 
|  | for _, g := range strings.Split(string(buf), "\n\n") { | 
|  | sl := strings.SplitN(g, "\n", 2) | 
|  | if len(sl) != 2 { | 
|  | continue | 
|  | } | 
|  | stack := strings.TrimSpace(sl[1]) | 
|  | if stack == "" || | 
|  | strings.Contains(stack, "testing.(*M).before.func1") || | 
|  | strings.Contains(stack, "os/signal.signal_recv") || | 
|  | strings.Contains(stack, "created by net.startServer") || | 
|  | strings.Contains(stack, "created by testing.RunTests") || | 
|  | strings.Contains(stack, "closeWriteAndWait") || | 
|  | strings.Contains(stack, "testing.Main(") || | 
|  | // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) | 
|  | strings.Contains(stack, "runtime.goexit") || | 
|  | strings.Contains(stack, "created by runtime.gc") || | 
|  | strings.Contains(stack, "net/http_test.interestingGoroutines") || | 
|  | strings.Contains(stack, "runtime.MHeap_Scavenger") { | 
|  | continue | 
|  | } | 
|  | gs = append(gs, stack) | 
|  | } | 
|  | sort.Strings(gs) | 
|  | return | 
|  | } | 
|  |  | 
|  | // Verify the other tests didn't leave any goroutines running. | 
|  | func goroutineLeaked() bool { | 
|  | if testing.Short() || runningBenchmarks() { | 
|  | // Don't worry about goroutine leaks in -short mode or in | 
|  | // benchmark mode. Too distracting when there are false positives. | 
|  | return false | 
|  | } | 
|  |  | 
|  | var stackCount map[string]int | 
|  | for i := 0; i < 5; i++ { | 
|  | n := 0 | 
|  | stackCount = make(map[string]int) | 
|  | gs := interestingGoroutines() | 
|  | for _, g := range gs { | 
|  | stackCount[g]++ | 
|  | n++ | 
|  | } | 
|  | if n == 0 { | 
|  | return false | 
|  | } | 
|  | // Wait for goroutines to schedule and die off: | 
|  | time.Sleep(100 * time.Millisecond) | 
|  | } | 
|  | fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") | 
|  | for stack, count := range stackCount { | 
|  | fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | // setParallel marks t as a parallel test if we're in short mode | 
|  | // (all.bash), but as a serial test otherwise. Using t.Parallel isn't | 
|  | // compatible with the afterTest func in non-short mode. | 
|  | func setParallel(t *testing.T) { | 
|  | if testing.Short() { | 
|  | t.Parallel() | 
|  | } | 
|  | } | 
|  |  | 
|  | func runningBenchmarks() bool { | 
|  | for i, arg := range os.Args { | 
|  | if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") { | 
|  | return true | 
|  | } | 
|  | if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" { | 
|  | return true | 
|  | } | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | func afterTest(t testing.TB) { | 
|  | http.DefaultTransport.(*http.Transport).CloseIdleConnections() | 
|  | if testing.Short() { | 
|  | return | 
|  | } | 
|  | var bad string | 
|  | badSubstring := map[string]string{ | 
|  | ").readLoop(":  "a Transport", | 
|  | ").writeLoop(": "a Transport", | 
|  | "created by net/http/httptest.(*Server).Start": "an httptest.Server", | 
|  | "timeoutHandler":        "a TimeoutHandler", | 
|  | "net.(*netFD).connect(": "a timing out dial", | 
|  | ").noteClientGone(":     "a closenotifier sender", | 
|  | } | 
|  | var stacks string | 
|  | for i := 0; i < 4; i++ { | 
|  | bad = "" | 
|  | stacks = strings.Join(interestingGoroutines(), "\n\n") | 
|  | for substr, what := range badSubstring { | 
|  | if strings.Contains(stacks, substr) { | 
|  | bad = what | 
|  | } | 
|  | } | 
|  | if bad == "" { | 
|  | return | 
|  | } | 
|  | // Bad stuff found, but goroutines might just still be | 
|  | // shutting down, so give it some time. | 
|  | time.Sleep(250 * time.Millisecond) | 
|  | } | 
|  | t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) | 
|  | } | 
|  |  | 
|  | // waitCondition reports whether fn eventually returned true, | 
|  | // checking immediately and then every checkEvery amount, | 
|  | // until waitFor has elapsed, at which point it returns false. | 
|  | func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { | 
|  | deadline := time.Now().Add(waitFor) | 
|  | for time.Now().Before(deadline) { | 
|  | if fn() { | 
|  | return true | 
|  | } | 
|  | time.Sleep(checkEvery) | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | // waitErrCondition is like waitCondition but with errors instead of bools. | 
|  | func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { | 
|  | deadline := time.Now().Add(waitFor) | 
|  | var err error | 
|  | for time.Now().Before(deadline) { | 
|  | if err = fn(); err == nil { | 
|  | return nil | 
|  | } | 
|  | time.Sleep(checkEvery) | 
|  | } | 
|  | return err | 
|  | } |