Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/http/main_test.go

Documentation: net/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  	"io"
    10  	"log"
    11  	"net/http"
    12  	"os"
    13  	"runtime"
    14  	"sort"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  var quietLog = log.New(io.Discard, "", 0)
    21  
    22  func TestMain(m *testing.M) {
    23  	v := m.Run()
    24  	if v == 0 && goroutineLeaked() {
    25  		os.Exit(1)
    26  	}
    27  	os.Exit(v)
    28  }
    29  
    30  func interestingGoroutines() (gs []string) {
    31  	buf := make([]byte, 2<<20)
    32  	buf = buf[:runtime.Stack(buf, true)]
    33  	for _, g := range strings.Split(string(buf), "\n\n") {
    34  		sl := strings.SplitN(g, "\n", 2)
    35  		if len(sl) != 2 {
    36  			continue
    37  		}
    38  		stack := strings.TrimSpace(sl[1])
    39  		if stack == "" ||
    40  			strings.Contains(stack, "testing.(*M).before.func1") ||
    41  			strings.Contains(stack, "os/signal.signal_recv") ||
    42  			strings.Contains(stack, "created by net.startServer") ||
    43  			strings.Contains(stack, "created by testing.RunTests") ||
    44  			strings.Contains(stack, "closeWriteAndWait") ||
    45  			strings.Contains(stack, "testing.Main(") ||
    46  			// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
    47  			strings.Contains(stack, "runtime.goexit") ||
    48  			strings.Contains(stack, "created by runtime.gc") ||
    49  			strings.Contains(stack, "net/http_test.interestingGoroutines") ||
    50  			strings.Contains(stack, "runtime.MHeap_Scavenger") {
    51  			continue
    52  		}
    53  		gs = append(gs, stack)
    54  	}
    55  	sort.Strings(gs)
    56  	return
    57  }
    58  
    59  // Verify the other tests didn't leave any goroutines running.
    60  func goroutineLeaked() bool {
    61  	if testing.Short() || runningBenchmarks() {
    62  		// Don't worry about goroutine leaks in -short mode or in
    63  		// benchmark mode. Too distracting when there are false positives.
    64  		return false
    65  	}
    66  
    67  	var stackCount map[string]int
    68  	for i := 0; i < 5; i++ {
    69  		n := 0
    70  		stackCount = make(map[string]int)
    71  		gs := interestingGoroutines()
    72  		for _, g := range gs {
    73  			stackCount[g]++
    74  			n++
    75  		}
    76  		if n == 0 {
    77  			return false
    78  		}
    79  		// Wait for goroutines to schedule and die off:
    80  		time.Sleep(100 * time.Millisecond)
    81  	}
    82  	fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
    83  	for stack, count := range stackCount {
    84  		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
    85  	}
    86  	return true
    87  }
    88  
    89  // setParallel marks t as a parallel test if we're in short mode
    90  // (all.bash), but as a serial test otherwise. Using t.Parallel isn't
    91  // compatible with the afterTest func in non-short mode.
    92  func setParallel(t *testing.T) {
    93  	if strings.Contains(t.Name(), "HTTP2") {
    94  		http.CondSkipHTTP2(t)
    95  	}
    96  	if testing.Short() {
    97  		t.Parallel()
    98  	}
    99  }
   100  
   101  func runningBenchmarks() bool {
   102  	for i, arg := range os.Args {
   103  		if strings.HasPrefix(arg, "-test.bench=") && !strings.HasSuffix(arg, "=") {
   104  			return true
   105  		}
   106  		if arg == "-test.bench" && i < len(os.Args)-1 && os.Args[i+1] != "" {
   107  			return true
   108  		}
   109  	}
   110  	return false
   111  }
   112  
   113  func afterTest(t testing.TB) {
   114  	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
   115  	if testing.Short() {
   116  		return
   117  	}
   118  	var bad string
   119  	badSubstring := map[string]string{
   120  		").readLoop(":  "a Transport",
   121  		").writeLoop(": "a Transport",
   122  		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
   123  		"timeoutHandler":        "a TimeoutHandler",
   124  		"net.(*netFD).connect(": "a timing out dial",
   125  		").noteClientGone(":     "a closenotifier sender",
   126  	}
   127  	var stacks string
   128  	for i := 0; i < 10; i++ {
   129  		bad = ""
   130  		stacks = strings.Join(interestingGoroutines(), "\n\n")
   131  		for substr, what := range badSubstring {
   132  			if strings.Contains(stacks, substr) {
   133  				bad = what
   134  			}
   135  		}
   136  		if bad == "" {
   137  			return
   138  		}
   139  		// Bad stuff found, but goroutines might just still be
   140  		// shutting down, so give it some time.
   141  		time.Sleep(250 * time.Millisecond)
   142  	}
   143  	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
   144  }
   145  
   146  // waitCondition reports whether fn eventually returned true,
   147  // checking immediately and then every checkEvery amount,
   148  // until waitFor has elapsed, at which point it returns false.
   149  func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
   150  	deadline := time.Now().Add(waitFor)
   151  	for time.Now().Before(deadline) {
   152  		if fn() {
   153  			return true
   154  		}
   155  		time.Sleep(checkEvery)
   156  	}
   157  	return false
   158  }
   159  
   160  // waitErrCondition is like waitCondition but with errors instead of bools.
   161  func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
   162  	deadline := time.Now().Add(waitFor)
   163  	var err error
   164  	for time.Now().Before(deadline) {
   165  		if err = fn(); err == nil {
   166  			return nil
   167  		}
   168  		time.Sleep(checkEvery)
   169  	}
   170  	return err
   171  }
   172  

View as plain text