Home | History | Annotate | Download | only in http
      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