...

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