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 "net/http" 10 "os" 11 "runtime" 12 "sort" 13 "strings" 14 "testing" 15 "time" 16 ) 17 18 func TestMain(m *testing.M) { 19 v := m.Run() 20 if v == 0 && goroutineLeaked() { 21 os.Exit(1) 22 } 23 os.Exit(v) 24 } 25 26 func interestingGoroutines() (gs []string) { 27 buf := make([]byte, 2<<20) 28 buf = buf[:runtime.Stack(buf, true)] 29 for _, g := range strings.Split(string(buf), "\n\n") { 30 sl := strings.SplitN(g, "\n", 2) 31 if len(sl) != 2 { 32 continue 33 } 34 stack := strings.TrimSpace(sl[1]) 35 if stack == "" || 36 strings.Contains(stack, "created by net.startServer") || 37 strings.Contains(stack, "created by testing.RunTests") || 38 strings.Contains(stack, "closeWriteAndWait") || 39 strings.Contains(stack, "testing.Main(") || 40 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 41 strings.Contains(stack, "runtime.goexit") || 42 strings.Contains(stack, "created by runtime.gc") || 43 strings.Contains(stack, "net/http_test.interestingGoroutines") || 44 strings.Contains(stack, "runtime.MHeap_Scavenger") { 45 continue 46 } 47 gs = append(gs, stack) 48 } 49 sort.Strings(gs) 50 return 51 } 52 53 // Verify the other tests didn't leave any goroutines running. 54 func goroutineLeaked() bool { 55 if testing.Short() { 56 // not counting goroutines for leakage in -short mode 57 return false 58 } 59 60 var stackCount map[string]int 61 for i := 0; i < 5; i++ { 62 n := 0 63 stackCount = make(map[string]int) 64 gs := interestingGoroutines() 65 for _, g := range gs { 66 stackCount[g]++ 67 n++ 68 } 69 if n == 0 { 70 return false 71 } 72 // Wait for goroutines to schedule and die off: 73 time.Sleep(100 * time.Millisecond) 74 } 75 fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") 76 for stack, count := range stackCount { 77 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 78 } 79 return true 80 } 81 82 func afterTest(t testing.TB) { 83 http.DefaultTransport.(*http.Transport).CloseIdleConnections() 84 if testing.Short() { 85 return 86 } 87 var bad string 88 badSubstring := map[string]string{ 89 ").readLoop(": "a Transport", 90 ").writeLoop(": "a Transport", 91 "created by net/http/httptest.(*Server).Start": "an httptest.Server", 92 "timeoutHandler": "a TimeoutHandler", 93 "net.(*netFD).connect(": "a timing out dial", 94 ").noteClientGone(": "a closenotifier sender", 95 } 96 var stacks string 97 for i := 0; i < 4; i++ { 98 bad = "" 99 stacks = strings.Join(interestingGoroutines(), "\n\n") 100 for substr, what := range badSubstring { 101 if strings.Contains(stacks, substr) { 102 bad = what 103 } 104 } 105 if bad == "" { 106 return 107 } 108 // Bad stuff found, but goroutines might just still be 109 // shutting down, so give it some time. 110 time.Sleep(250 * time.Millisecond) 111 } 112 t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) 113 } 114