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
5package http_test
6
7import (
8	"fmt"
9	"io/ioutil"
10	"log"
11	"net/http"
12	"os"
13	"runtime"
14	"sort"
15	"strings"
16	"testing"
17	"time"
18)
19
20var quietLog = log.New(ioutil.Discard, "", 0)
21
22func TestMain(m *testing.M) {
23	v := m.Run()
24	if v == 0 && goroutineLeaked() {
25		os.Exit(1)
26	}
27	os.Exit(v)
28}
29
30func 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.
60func 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.
92func setParallel(t *testing.T) {
93	if testing.Short() {
94		t.Parallel()
95	}
96}
97
98func 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
110func 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.
146func 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.
158func 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