github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 "flag" 9 "fmt" 10 "net/http" 11 "os" 12 "runtime" 13 "sort" 14 "strings" 15 "testing" 16 "time" 17 ) 18 19 var flaky = flag.Bool("flaky", false, "run known-flaky tests too") 20 21 func TestMain(m *testing.M) { 22 v := m.Run() 23 if v == 0 && goroutineLeaked() { 24 os.Exit(1) 25 } 26 os.Exit(v) 27 } 28 29 func 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. 57 func 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. 88 func setParallel(t *testing.T) { 89 if testing.Short() { 90 t.Parallel() 91 } 92 } 93 94 func 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 100 func 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 }