github.com/euank/go@v0.0.0-20160829210321-495514729181/src/net/http/main_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package http_test 6 7 import ( 8 "fmt" 9 "net/http" 10 "os" 11 "runtime" 12 "sort" 13 "strings" 14 "testing" 15 "time" 16 ) 17 18 func TestMain(m *testing.M) { 19 v := m.Run() 20 if v == 0 && goroutineLeaked() { 21 os.Exit(1) 22 } 23 os.Exit(v) 24 } 25 26 func interestingGoroutines() (gs []string) { 27 buf := make([]byte, 2<<20) 28 buf = buf[:runtime.Stack(buf, true)] 29 for _, g := range strings.Split(string(buf), "\n\n") { 30 sl := strings.SplitN(g, "\n", 2) 31 if len(sl) != 2 { 32 continue 33 } 34 stack := strings.TrimSpace(sl[1]) 35 if stack == "" || 36 strings.Contains(stack, "created by net.startServer") || 37 strings.Contains(stack, "created by testing.RunTests") || 38 strings.Contains(stack, "closeWriteAndWait") || 39 strings.Contains(stack, "testing.Main(") || 40 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 41 strings.Contains(stack, "runtime.goexit") || 42 strings.Contains(stack, "created by runtime.gc") || 43 strings.Contains(stack, "net/http_test.interestingGoroutines") || 44 strings.Contains(stack, "runtime.MHeap_Scavenger") { 45 continue 46 } 47 gs = append(gs, stack) 48 } 49 sort.Strings(gs) 50 return 51 } 52 53 // Verify the other tests didn't leave any goroutines running. 54 func goroutineLeaked() bool { 55 if testing.Short() { 56 // not counting goroutines for leakage in -short mode 57 return false 58 } 59 60 var stackCount map[string]int 61 for i := 0; i < 5; i++ { 62 n := 0 63 stackCount = make(map[string]int) 64 gs := interestingGoroutines() 65 for _, g := range gs { 66 stackCount[g]++ 67 n++ 68 } 69 if n == 0 { 70 return false 71 } 72 // Wait for goroutines to schedule and die off: 73 time.Sleep(100 * time.Millisecond) 74 } 75 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") 76 for stack, count := range stackCount { 77 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 78 } 79 return true 80 } 81 82 // setParallel marks t as a parallel test if we're in short mode 83 // (all.bash), but as a serial test otherwise. Using t.Parallel isn't 84 // compatible with the afterTest func in non-short mode. 85 func setParallel(t *testing.T) { 86 if testing.Short() { 87 t.Parallel() 88 } 89 } 90 91 func afterTest(t testing.TB) { 92 http.DefaultTransport.(*http.Transport).CloseIdleConnections() 93 if testing.Short() { 94 return 95 } 96 var bad string 97 badSubstring := map[string]string{ 98 ").readLoop(": "a Transport", 99 ").writeLoop(": "a Transport", 100 "created by net/http/httptest.(*Server).Start": "an httptest.Server", 101 "timeoutHandler": "a TimeoutHandler", 102 "net.(*netFD).connect(": "a timing out dial", 103 ").noteClientGone(": "a closenotifier sender", 104 } 105 var stacks string 106 for i := 0; i < 4; i++ { 107 bad = "" 108 stacks = strings.Join(interestingGoroutines(), "\n\n") 109 for substr, what := range badSubstring { 110 if strings.Contains(stacks, substr) { 111 bad = what 112 } 113 } 114 if bad == "" { 115 return 116 } 117 // Bad stuff found, but goroutines might just still be 118 // shutting down, so give it some time. 119 time.Sleep(250 * time.Millisecond) 120 } 121 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) 122 } 123 124 // waitCondition reports whether fn eventually returned true, 125 // checking immediately and then every checkEvery amount, 126 // until waitFor has elapsed, at which point it returns false. 127 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 128 deadline := time.Now().Add(waitFor) 129 for time.Now().Before(deadline) { 130 if fn() { 131 return true 132 } 133 time.Sleep(checkEvery) 134 } 135 return false 136 }