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  }