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	"flag"
9	"fmt"
10	"net/http"
11	"os"
12	"runtime"
13	"sort"
14	"strings"
15	"testing"
16	"time"
17)
18
19var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
20
21func TestMain(m *testing.M) {
22	v := m.Run()
23	if v == 0 && goroutineLeaked() {
24		os.Exit(1)
25	}
26	os.Exit(v)
27}
28
29func interestingGoroutines() (gs []string) {
30	buf := make([]byte, 2<<20)
31	buf = buf[:runtime.Stack(buf, true)]
32	for _, g := range strings.Split(string(buf), "\n\n") {
33		sl := strings.SplitN(g, "\n", 2)
34		if len(sl) != 2 {
35			continue
36		}
37		stack := strings.TrimSpace(sl[1])
38		if stack == "" ||
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			// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
44			strings.Contains(stack, "runtime.goexit") ||
45			strings.Contains(stack, "created by runtime.gc") ||
46			strings.Contains(stack, "net/http_test.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// Verify the other tests didn't leave any goroutines running.
57func goroutineLeaked() bool {
58	if testing.Short() {
59		// not counting goroutines for leakage in -short mode
60		return false
61	}
62
63	var stackCount map[string]int
64	for i := 0; i < 5; i++ {
65		n := 0
66		stackCount = make(map[string]int)
67		gs := interestingGoroutines()
68		for _, g := range gs {
69			stackCount[g]++
70			n++
71		}
72		if n == 0 {
73			return false
74		}
75		// Wait for goroutines to schedule and die off:
76		time.Sleep(100 * time.Millisecond)
77	}
78	fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
79	for stack, count := range stackCount {
80		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
81	}
82	return true
83}
84
85// setParallel marks t as a parallel test if we're in short mode
86// (all.bash), but as a serial test otherwise. Using t.Parallel isn't
87// compatible with the afterTest func in non-short mode.
88func setParallel(t *testing.T) {
89	if testing.Short() {
90		t.Parallel()
91	}
92}
93
94func setFlaky(t *testing.T, issue int) {
95	if !*flaky {
96		t.Skipf("skipping known flaky test; see golang.org/issue/%d", issue)
97	}
98}
99
100func afterTest(t testing.TB) {
101	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
102	if testing.Short() {
103		return
104	}
105	var bad string
106	badSubstring := map[string]string{
107		").readLoop(":                                  "a Transport",
108		").writeLoop(":                                 "a Transport",
109		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
110		"timeoutHandler":                               "a TimeoutHandler",
111		"net.(*netFD).connect(":                        "a timing out dial",
112		").noteClientGone(":                            "a closenotifier sender",
113	}
114	var stacks string
115	for i := 0; i < 4; i++ {
116		bad = ""
117		stacks = strings.Join(interestingGoroutines(), "\n\n")
118		for substr, what := range badSubstring {
119			if strings.Contains(stacks, substr) {
120				bad = what
121			}
122		}
123		if bad == "" {
124			return
125		}
126		// Bad stuff found, but goroutines might just still be
127		// shutting down, so give it some time.
128		time.Sleep(250 * time.Millisecond)
129	}
130	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
131}
132