github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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" 10 "log" 11 "net/http" 12 "os" 13 "runtime" 14 "slices" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 var quietLog = log.New(io.Discard, "", 0) 21 22 func TestMain(m *testing.M) { 23 *http.MaxWriteWaitBeforeConnReuse = 60 * time.Minute 24 v := m.Run() 25 if v == 0 && goroutineLeaked() { 26 os.Exit(1) 27 } 28 os.Exit(v) 29 } 30 31 func interestingGoroutines() (gs []string) { 32 buf := make([]byte, 2<<20) 33 buf = buf[:runtime.Stack(buf, true)] 34 for _, g := range strings.Split(string(buf), "\n\n") { 35 _, stack, _ := strings.Cut(g, "\n") 36 stack = strings.TrimSpace(stack) 37 if stack == "" || 38 strings.Contains(stack, "testing.(*M).before.func1") || 39 strings.Contains(stack, "os/signal.signal_recv") || 40 strings.Contains(stack, "created by net.startServer") || 41 strings.Contains(stack, "created by testing.RunTests") || 42 strings.Contains(stack, "closeWriteAndWait") || 43 strings.Contains(stack, "testing.Main(") || 44 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 45 strings.Contains(stack, "runtime.goexit") || 46 strings.Contains(stack, "created by runtime.gc") || 47 strings.Contains(stack, "interestingGoroutines") || 48 strings.Contains(stack, "runtime.MHeap_Scavenger") { 49 continue 50 } 51 gs = append(gs, stack) 52 } 53 slices.Sort(gs) 54 return 55 } 56 57 // Verify the other tests didn't leave any goroutines running. 58 func goroutineLeaked() bool { 59 if testing.Short() || runningBenchmarks() { 60 // Don't worry about goroutine leaks in -short mode or in 61 // benchmark mode. Too distracting when there are false positives. 62 return false 63 } 64 65 var stackCount map[string]int 66 for i := 0; i < 5; i++ { 67 n := 0 68 stackCount = make(map[string]int) 69 gs := interestingGoroutines() 70 for _, g := range gs { 71 stackCount[g]++ 72 n++ 73 } 74 if n == 0 { 75 return false 76 } 77 // Wait for goroutines to schedule and die off: 78 time.Sleep(100 * time.Millisecond) 79 } 80 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") 81 for stack, count := range stackCount { 82 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 83 } 84 return true 85 } 86 87 // setParallel marks t as a parallel test if we're in short mode 88 // (all.bash), but as a serial test otherwise. Using t.Parallel isn't 89 // compatible with the afterTest func in non-short mode. 90 func setParallel(t *testing.T) { 91 if strings.Contains(t.Name(), "HTTP2") { 92 http.CondSkipHTTP2(t) 93 } 94 if testing.Short() { 95 t.Parallel() 96 } 97 } 98 99 func runningBenchmarks() bool { 100 for i, arg := range os.Args { 101 if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") { 102 return true 103 } 104 if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" { 105 return true 106 } 107 } 108 return false 109 } 110 111 var leakReported bool 112 113 func afterTest(t testing.TB) { 114 http.DefaultTransport.(*http.Transport).CloseIdleConnections() 115 if testing.Short() { 116 return 117 } 118 if leakReported { 119 // To avoid confusion, only report the first leak of each test run. 120 // After the first leak has been reported, we can't tell whether the leaked 121 // goroutines are a new leak from a subsequent test or just the same 122 // goroutines from the first leak still hanging around, and we may add a lot 123 // of latency waiting for them to exit at the end of each test. 124 return 125 } 126 127 // We shouldn't be running the leak check for parallel tests, because we might 128 // report the goroutines from a test that is still running as a leak from a 129 // completely separate test that has just finished. So we use non-atomic loads 130 // and stores for the leakReported variable, and store every time we start a 131 // leak check so that the race detector will flag concurrent leak checks as a 132 // race even if we don't detect any leaks. 133 leakReported = true 134 135 var bad string 136 badSubstring := map[string]string{ 137 ").readLoop(": "a Transport", 138 ").writeLoop(": "a Transport", 139 "created by net/http/httptest.(*Server).Start": "an httptest.Server", 140 "timeoutHandler": "a TimeoutHandler", 141 "net.(*netFD).connect(": "a timing out dial", 142 ").noteClientGone(": "a closenotifier sender", 143 } 144 var stacks string 145 for i := 0; i < 10; i++ { 146 bad = "" 147 stacks = strings.Join(interestingGoroutines(), "\n\n") 148 for substr, what := range badSubstring { 149 if strings.Contains(stacks, substr) { 150 bad = what 151 } 152 } 153 if bad == "" { 154 leakReported = false 155 return 156 } 157 // Bad stuff found, but goroutines might just still be 158 // shutting down, so give it some time. 159 time.Sleep(250 * time.Millisecond) 160 } 161 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) 162 } 163 164 // waitCondition waits for fn to return true, 165 // checking immediately and then at exponentially increasing intervals. 166 func waitCondition(t testing.TB, delay time.Duration, fn func(time.Duration) bool) { 167 t.Helper() 168 start := time.Now() 169 var since time.Duration 170 for !fn(since) { 171 time.Sleep(delay) 172 delay = 2*delay - (delay / 2) // 1.5x, rounded up 173 since = time.Since(start) 174 } 175 }