Source file
src/net/http/main_test.go
1
2
3
4
5 package http_test
6
7 import (
8 "fmt"
9 "io"
10 "log"
11 "net/http"
12 "os"
13 "runtime"
14 "sort"
15 "strings"
16 "testing"
17 "time"
18 )
19
20 var quietLog = log.New(io.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 _, stack, _ := strings.Cut(g, "\n")
35 stack = strings.TrimSpace(stack)
36 if stack == "" ||
37 strings.Contains(stack, "testing.(*M).before.func1") ||
38 strings.Contains(stack, "os/signal.signal_recv") ||
39 strings.Contains(stack, "created by net.startServer") ||
40 strings.Contains(stack, "created by testing.RunTests") ||
41 strings.Contains(stack, "closeWriteAndWait") ||
42 strings.Contains(stack, "testing.Main(") ||
43
44 strings.Contains(stack, "runtime.goexit") ||
45 strings.Contains(stack, "created by runtime.gc") ||
46 strings.Contains(stack, "interestingGoroutines") ||
47 strings.Contains(stack, "runtime.MHeap_Scavenger") {
48 continue
49 }
50 gs = append(gs, stack)
51 }
52 sort.Strings(gs)
53 return
54 }
55
56
57 func goroutineLeaked() bool {
58 if testing.Short() || runningBenchmarks() {
59
60
61 return false
62 }
63
64 var stackCount map[string]int
65 for i := 0; i < 5; i++ {
66 n := 0
67 stackCount = make(map[string]int)
68 gs := interestingGoroutines()
69 for _, g := range gs {
70 stackCount[g]++
71 n++
72 }
73 if n == 0 {
74 return false
75 }
76
77 time.Sleep(100 * time.Millisecond)
78 }
79 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
80 for stack, count := range stackCount {
81 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
82 }
83 return true
84 }
85
86
87
88
89 func setParallel(t *testing.T) {
90 if strings.Contains(t.Name(), "HTTP2") {
91 http.CondSkipHTTP2(t)
92 }
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 < 10; 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
137
138 time.Sleep(250 * time.Millisecond)
139 }
140 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
141 }
142
143
144
145
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
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 }
169
View as plain text