github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/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  	"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  }