github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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 "io/ioutil" 10 "log" 11 "net/http" 12 "os" 13 "runtime" 14 "sort" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 var quietLog = log.New(ioutil.Discard, "", 0) 21 22 func TestMain(m *testing.M) { 23 v := m.Run() 24 if v == 0 && goroutineLeaked() { 25 os.Exit(1) 26 } 27 os.Exit(v) 28 } 29 30 func interestingGoroutines() (gs []string) { 31 buf := make([]byte, 2<<20) 32 buf = buf[:runtime.Stack(buf, true)] 33 for _, g := range strings.Split(string(buf), "\n\n") { 34 sl := strings.SplitN(g, "\n", 2) 35 if len(sl) != 2 { 36 continue 37 } 38 stack := strings.TrimSpace(sl[1]) 39 if stack == "" || 40 strings.Contains(stack, "testing.(*M).before.func1") || 41 strings.Contains(stack, "os/signal.signal_recv") || 42 strings.Contains(stack, "created by net.startServer") || 43 strings.Contains(stack, "created by testing.RunTests") || 44 strings.Contains(stack, "closeWriteAndWait") || 45 strings.Contains(stack, "testing.Main(") || 46 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 47 strings.Contains(stack, "runtime.goexit") || 48 strings.Contains(stack, "created by runtime.gc") || 49 strings.Contains(stack, "net/http_test.interestingGoroutines") || 50 strings.Contains(stack, "runtime.MHeap_Scavenger") { 51 continue 52 } 53 gs = append(gs, stack) 54 } 55 sort.Strings(gs) 56 return 57 } 58 59 // Verify the other tests didn't leave any goroutines running. 60 func goroutineLeaked() bool { 61 if testing.Short() || runningBenchmarks() { 62 // Don't worry about goroutine leaks in -short mode or in 63 // benchmark mode. Too distracting when there are false positives. 64 return false 65 } 66 67 var stackCount map[string]int 68 for i := 0; i < 5; i++ { 69 n := 0 70 stackCount = make(map[string]int) 71 gs := interestingGoroutines() 72 for _, g := range gs { 73 stackCount[g]++ 74 n++ 75 } 76 if n == 0 { 77 return false 78 } 79 // Wait for goroutines to schedule and die off: 80 time.Sleep(100 * time.Millisecond) 81 } 82 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") 83 for stack, count := range stackCount { 84 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 85 } 86 return true 87 } 88 89 // setParallel marks t as a parallel test if we're in short mode 90 // (all.bash), but as a serial test otherwise. Using t.Parallel isn't 91 // compatible with the afterTest func in non-short mode. 92 func setParallel(t *testing.T) { 93 if testing.Short() { 94 t.Parallel() 95 } 96 } 97 98 func runningBenchmarks() bool { 99 for i, arg := range os.Args { 100 if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") { 101 return true 102 } 103 if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" { 104 return true 105 } 106 } 107 return false 108 } 109 110 func afterTest(t testing.TB) { 111 http.DefaultTransport.(*http.Transport).CloseIdleConnections() 112 if testing.Short() { 113 return 114 } 115 var bad string 116 badSubstring := map[string]string{ 117 ").readLoop(": "a Transport", 118 ").writeLoop(": "a Transport", 119 "created by net/http/httptest.(*Server).Start": "an httptest.Server", 120 "timeoutHandler": "a TimeoutHandler", 121 "net.(*netFD).connect(": "a timing out dial", 122 ").noteClientGone(": "a closenotifier sender", 123 } 124 var stacks string 125 for i := 0; i < 4; i++ { 126 bad = "" 127 stacks = strings.Join(interestingGoroutines(), "\n\n") 128 for substr, what := range badSubstring { 129 if strings.Contains(stacks, substr) { 130 bad = what 131 } 132 } 133 if bad == "" { 134 return 135 } 136 // Bad stuff found, but goroutines might just still be 137 // shutting down, so give it some time. 138 time.Sleep(250 * time.Millisecond) 139 } 140 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) 141 } 142 143 // waitCondition reports whether fn eventually returned true, 144 // checking immediately and then every checkEvery amount, 145 // until waitFor has elapsed, at which point it returns false. 146 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 147 deadline := time.Now().Add(waitFor) 148 for time.Now().Before(deadline) { 149 if fn() { 150 return true 151 } 152 time.Sleep(checkEvery) 153 } 154 return false 155 } 156 157 // waitErrCondition is like waitCondition but with errors instead of bools. 158 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 159 deadline := time.Now().Add(waitFor) 160 var err error 161 for time.Now().Before(deadline) { 162 if err = fn(); err == nil { 163 return nil 164 } 165 time.Sleep(checkEvery) 166 } 167 return err 168 }