Merge pull request #529 from bradfitz/leaks
Fix test-only goroutine leaks; add leak checker to end2end tests.
This commit is contained in:
@ -34,12 +34,15 @@
|
|||||||
package grpc_test
|
package grpc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
@ -267,6 +270,7 @@ func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServ
|
|||||||
const tlsDir = "testdata/"
|
const tlsDir = "testdata/"
|
||||||
|
|
||||||
func TestReconnectTimeout(t *testing.T) {
|
func TestReconnectTimeout(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to listen: %v", err)
|
t.Fatalf("Failed to listen: %v", err)
|
||||||
@ -317,19 +321,51 @@ func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type env struct {
|
type env struct {
|
||||||
|
name string
|
||||||
network string // The type of network such as tcp, unix, etc.
|
network string // The type of network such as tcp, unix, etc.
|
||||||
dialer func(addr string, timeout time.Duration) (net.Conn, error)
|
dialer func(addr string, timeout time.Duration) (net.Conn, error)
|
||||||
security string // The security protocol such as TLS, SSH, etc.
|
security string // The security protocol such as TLS, SSH, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
func listTestEnv() []env {
|
func (e env) runnable() bool {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" && strings.HasPrefix(e.name, "unix-") {
|
||||||
return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}}
|
return false
|
||||||
}
|
}
|
||||||
return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}, {"unix", unixDialer, ""}, {"unix", unixDialer, "tls"}}
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
tcpClearEnv = env{name: "tcp-clear", network: "tcp"}
|
||||||
|
tcpTLSEnv = env{name: "tcp-tls", network: "tcp", security: "tls"}
|
||||||
|
unixClearEnv = env{name: "unix-clear", network: "unix", dialer: unixDialer}
|
||||||
|
unixTLSEnv = env{name: "unix-tls", network: "unix", dialer: unixDialer, security: "tls"}
|
||||||
|
allEnv = []env{tcpClearEnv, tcpTLSEnv, unixClearEnv, unixTLSEnv}
|
||||||
|
)
|
||||||
|
|
||||||
|
var onlyEnv = flag.String("only_env", "", "If non-empty, one of 'tcp-clear', 'tcp-tls', 'unix-clear', or 'unix-tls' to only run the tests for that environment. Empty means all.")
|
||||||
|
|
||||||
|
func listTestEnv() (envs []env) {
|
||||||
|
if *onlyEnv != "" {
|
||||||
|
for _, e := range allEnv {
|
||||||
|
if e.name == *onlyEnv {
|
||||||
|
if !e.runnable() {
|
||||||
|
panic(fmt.Sprintf("--only_env environment %q does not run on %s", *onlyEnv, runtime.GOOS))
|
||||||
|
}
|
||||||
|
return []env{e}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid --only_env value %q", *onlyEnv))
|
||||||
|
}
|
||||||
|
for _, e := range allEnv {
|
||||||
|
if e.runnable() {
|
||||||
|
envs = append(envs, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envs
|
||||||
}
|
}
|
||||||
|
|
||||||
func serverSetUp(t *testing.T, servON bool, hs *health.HealthServer, maxStream uint32, cp grpc.Compressor, dc grpc.Decompressor, e env) (s *grpc.Server, addr string) {
|
func serverSetUp(t *testing.T, servON bool, hs *health.HealthServer, maxStream uint32, cp grpc.Compressor, dc grpc.Decompressor, e env) (s *grpc.Server, addr string) {
|
||||||
|
t.Logf("Running test in %s environment...", e.name)
|
||||||
sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream), grpc.RPCCompressor(cp), grpc.RPCDecompressor(dc)}
|
sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream), grpc.RPCCompressor(cp), grpc.RPCDecompressor(dc)}
|
||||||
la := ":0"
|
la := ":0"
|
||||||
switch e.network {
|
switch e.network {
|
||||||
@ -392,6 +428,7 @@ func tearDown(s *grpc.Server, cc *grpc.ClientConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeoutOnDeadServer(t *testing.T) {
|
func TestTimeoutOnDeadServer(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testTimeoutOnDeadServer(t, e)
|
testTimeoutOnDeadServer(t, e)
|
||||||
}
|
}
|
||||||
@ -434,8 +471,8 @@ func testTimeoutOnDeadServer(t *testing.T, e env) {
|
|||||||
cc.Close()
|
cc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func healthCheck(t time.Duration, cc *grpc.ClientConn, serviceName string) (*healthpb.HealthCheckResponse, error) {
|
func healthCheck(d time.Duration, cc *grpc.ClientConn, serviceName string) (*healthpb.HealthCheckResponse, error) {
|
||||||
ctx, _ := context.WithTimeout(context.Background(), t)
|
ctx, _ := context.WithTimeout(context.Background(), d)
|
||||||
hc := healthpb.NewHealthClient(cc)
|
hc := healthpb.NewHealthClient(cc)
|
||||||
req := &healthpb.HealthCheckRequest{
|
req := &healthpb.HealthCheckRequest{
|
||||||
Service: serviceName,
|
Service: serviceName,
|
||||||
@ -444,6 +481,7 @@ func healthCheck(t time.Duration, cc *grpc.ClientConn, serviceName string) (*hea
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthCheckOnSuccess(t *testing.T) {
|
func TestHealthCheckOnSuccess(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testHealthCheckOnSuccess(t, e)
|
testHealthCheckOnSuccess(t, e)
|
||||||
}
|
}
|
||||||
@ -461,6 +499,7 @@ func testHealthCheckOnSuccess(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthCheckOnFailure(t *testing.T) {
|
func TestHealthCheckOnFailure(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testHealthCheckOnFailure(t, e)
|
testHealthCheckOnFailure(t, e)
|
||||||
}
|
}
|
||||||
@ -478,6 +517,7 @@ func testHealthCheckOnFailure(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthCheckOff(t *testing.T) {
|
func TestHealthCheckOff(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testHealthCheckOff(t, e)
|
testHealthCheckOff(t, e)
|
||||||
}
|
}
|
||||||
@ -493,6 +533,7 @@ func testHealthCheckOff(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthCheckServingStatus(t *testing.T) {
|
func TestHealthCheckServingStatus(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testHealthCheckServingStatus(t, e)
|
testHealthCheckServingStatus(t, e)
|
||||||
}
|
}
|
||||||
@ -533,6 +574,7 @@ func testHealthCheckServingStatus(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyUnaryWithUserAgent(t *testing.T) {
|
func TestEmptyUnaryWithUserAgent(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testEmptyUnaryWithUserAgent(t, e)
|
testEmptyUnaryWithUserAgent(t, e)
|
||||||
}
|
}
|
||||||
@ -577,6 +619,7 @@ func testEmptyUnaryWithUserAgent(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailedEmptyUnary(t *testing.T) {
|
func TestFailedEmptyUnary(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testFailedEmptyUnary(t, e)
|
testFailedEmptyUnary(t, e)
|
||||||
}
|
}
|
||||||
@ -594,6 +637,7 @@ func testFailedEmptyUnary(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLargeUnary(t *testing.T) {
|
func TestLargeUnary(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testLargeUnary(t, e)
|
testLargeUnary(t, e)
|
||||||
}
|
}
|
||||||
@ -629,6 +673,7 @@ func testLargeUnary(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMetadataUnaryRPC(t *testing.T) {
|
func TestMetadataUnaryRPC(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testMetadataUnaryRPC(t, e)
|
testMetadataUnaryRPC(t, e)
|
||||||
}
|
}
|
||||||
@ -657,10 +702,10 @@ func testMetadataUnaryRPC(t *testing.T, e env) {
|
|||||||
if _, err := tc.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer)); err != nil {
|
if _, err := tc.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer)); err != nil {
|
||||||
t.Fatalf("TestService.UnaryCall(%v, _, _, _) = _, %v; want _, <nil>", ctx, err)
|
t.Fatalf("TestService.UnaryCall(%v, _, _, _) = _, %v; want _, <nil>", ctx, err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(testMetadata, header) {
|
if !reflect.DeepEqual(header, testMetadata) {
|
||||||
t.Fatalf("Received header metadata %v, want %v", header, testMetadata)
|
t.Fatalf("Received header metadata %v, want %v", header, testMetadata)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(testMetadata, trailer) {
|
if !reflect.DeepEqual(trailer, testMetadata) {
|
||||||
t.Fatalf("Received trailer metadata %v, want %v", trailer, testMetadata)
|
t.Fatalf("Received trailer metadata %v, want %v", trailer, testMetadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,6 +740,7 @@ func performOneRPC(t *testing.T, tc testpb.TestServiceClient, wg *sync.WaitGroup
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRetry(t *testing.T) {
|
func TestRetry(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testRetry(t, e)
|
testRetry(t, e)
|
||||||
}
|
}
|
||||||
@ -709,9 +755,15 @@ func testRetry(t *testing.T, e env) {
|
|||||||
tc := testpb.NewTestServiceClient(cc)
|
tc := testpb.NewTestServiceClient(cc)
|
||||||
defer tearDown(s, cc)
|
defer tearDown(s, cc)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
numRPC := 1000
|
||||||
|
rpcSpacing := 2 * time.Millisecond
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(1 * time.Second)
|
// Halfway through starting RPCs, kill all connections:
|
||||||
|
time.Sleep(time.Duration(numRPC/2) * rpcSpacing)
|
||||||
|
|
||||||
// The server shuts down the network connection to make a
|
// The server shuts down the network connection to make a
|
||||||
// transport error which will be detected by the client side
|
// transport error which will be detected by the client side
|
||||||
// code.
|
// code.
|
||||||
@ -719,8 +771,8 @@ func testRetry(t *testing.T, e env) {
|
|||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
// All these RPCs should succeed eventually.
|
// All these RPCs should succeed eventually.
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < numRPC; i++ {
|
||||||
time.Sleep(2 * time.Millisecond)
|
time.Sleep(rpcSpacing)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go performOneRPC(t, tc, &wg)
|
go performOneRPC(t, tc, &wg)
|
||||||
}
|
}
|
||||||
@ -728,6 +780,7 @@ func testRetry(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRPCTimeout(t *testing.T) {
|
func TestRPCTimeout(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testRPCTimeout(t, e)
|
testRPCTimeout(t, e)
|
||||||
}
|
}
|
||||||
@ -762,6 +815,7 @@ func testRPCTimeout(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCancel(t *testing.T) {
|
func TestCancel(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testCancel(t, e)
|
testCancel(t, e)
|
||||||
}
|
}
|
||||||
@ -794,6 +848,7 @@ func testCancel(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCancelNoIO(t *testing.T) {
|
func TestCancelNoIO(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testCancelNoIO(t, e)
|
testCancelNoIO(t, e)
|
||||||
}
|
}
|
||||||
@ -847,6 +902,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNoService(t *testing.T) {
|
func TestNoService(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testNoService(t, e)
|
testNoService(t, e)
|
||||||
}
|
}
|
||||||
@ -858,8 +914,10 @@ func testNoService(t *testing.T, e env) {
|
|||||||
tc := testpb.NewTestServiceClient(cc)
|
tc := testpb.NewTestServiceClient(cc)
|
||||||
defer tearDown(s, cc)
|
defer tearDown(s, cc)
|
||||||
// Make sure setting ack has been sent.
|
// Make sure setting ack has been sent.
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(20 * time.Millisecond)
|
||||||
stream, err := tc.FullDuplexCall(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
stream, err := tc.FullDuplexCall(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
||||||
}
|
}
|
||||||
@ -869,6 +927,7 @@ func testNoService(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPingPong(t *testing.T) {
|
func TestPingPong(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testPingPong(t, e)
|
testPingPong(t, e)
|
||||||
}
|
}
|
||||||
@ -927,6 +986,7 @@ func testPingPong(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMetadataStreamingRPC(t *testing.T) {
|
func TestMetadataStreamingRPC(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testMetadataStreamingRPC(t, e)
|
testMetadataStreamingRPC(t, e)
|
||||||
}
|
}
|
||||||
@ -994,6 +1054,7 @@ func testMetadataStreamingRPC(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerStreaming(t *testing.T) {
|
func TestServerStreaming(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testServerStreaming(t, e)
|
testServerStreaming(t, e)
|
||||||
}
|
}
|
||||||
@ -1047,6 +1108,7 @@ func testServerStreaming(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailedServerStreaming(t *testing.T) {
|
func TestFailedServerStreaming(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testFailedServerStreaming(t, e)
|
testFailedServerStreaming(t, e)
|
||||||
}
|
}
|
||||||
@ -1078,6 +1140,7 @@ func testFailedServerStreaming(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClientStreaming(t *testing.T) {
|
func TestClientStreaming(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testClientStreaming(t, e)
|
testClientStreaming(t, e)
|
||||||
}
|
}
|
||||||
@ -1118,6 +1181,7 @@ func testClientStreaming(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExceedMaxStreamsLimit(t *testing.T) {
|
func TestExceedMaxStreamsLimit(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testExceedMaxStreamsLimit(t, e)
|
testExceedMaxStreamsLimit(t, e)
|
||||||
}
|
}
|
||||||
@ -1129,13 +1193,16 @@ func testExceedMaxStreamsLimit(t *testing.T, e env) {
|
|||||||
cc := clientSetUp(t, addr, nil, nil, "", e)
|
cc := clientSetUp(t, addr, nil, nil, "", e)
|
||||||
tc := testpb.NewTestServiceClient(cc)
|
tc := testpb.NewTestServiceClient(cc)
|
||||||
defer tearDown(s, cc)
|
defer tearDown(s, cc)
|
||||||
_, err := tc.StreamingInputCall(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
_, err := tc.StreamingInputCall(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, <nil>", tc, err)
|
t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, <nil>", tc, err)
|
||||||
}
|
}
|
||||||
// Loop until receiving the new max stream setting from the server.
|
// Loop until receiving the new max stream setting from the server.
|
||||||
for {
|
for {
|
||||||
ctx, _ := context.WithTimeout(context.Background(), time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
_, err := tc.StreamingInputCall(ctx)
|
_, err := tc.StreamingInputCall(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
@ -1149,6 +1216,7 @@ func testExceedMaxStreamsLimit(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCompressServerHasNoSupport(t *testing.T) {
|
func TestCompressServerHasNoSupport(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testCompressServerHasNoSupport(t, e)
|
testCompressServerHasNoSupport(t, e)
|
||||||
}
|
}
|
||||||
@ -1202,6 +1270,7 @@ func testCompressServerHasNoSupport(t *testing.T, e env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCompressOK(t *testing.T) {
|
func TestCompressOK(t *testing.T) {
|
||||||
|
defer leakCheck(t)()
|
||||||
for _, e := range listTestEnv() {
|
for _, e := range listTestEnv() {
|
||||||
testCompressOK(t, e)
|
testCompressOK(t, e)
|
||||||
}
|
}
|
||||||
@ -1228,7 +1297,9 @@ func testCompressOK(t *testing.T, e env) {
|
|||||||
t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, <nil>", err)
|
t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, <nil>", err)
|
||||||
}
|
}
|
||||||
// Streaming RPC
|
// Streaming RPC
|
||||||
stream, err := tc.FullDuplexCall(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
stream, err := tc.FullDuplexCall(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
||||||
}
|
}
|
||||||
@ -1253,3 +1324,72 @@ func testCompressOK(t *testing.T, e env) {
|
|||||||
t.Fatalf("%v.Recv() = %v, want <nil>", stream, err)
|
t.Fatalf("%v.Recv() = %v, want <nil>", stream, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interestingGoroutines returns all goroutines we care about for the purpose
|
||||||
|
// of leak checking. It excludes testing or runtime ones.
|
||||||
|
func interestingGoroutines() (gs []string) {
|
||||||
|
buf := make([]byte, 2<<20)
|
||||||
|
buf = buf[:runtime.Stack(buf, true)]
|
||||||
|
for _, g := range strings.Split(string(buf), "\n\n") {
|
||||||
|
sl := strings.SplitN(g, "\n", 2)
|
||||||
|
if len(sl) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stack := strings.TrimSpace(sl[1])
|
||||||
|
if strings.HasPrefix(stack, "testing.RunTests") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack == "" ||
|
||||||
|
strings.Contains(stack, "testing.Main(") ||
|
||||||
|
strings.Contains(stack, "runtime.goexit") ||
|
||||||
|
strings.Contains(stack, "created by runtime.gc") ||
|
||||||
|
strings.Contains(stack, "interestingGoroutines") ||
|
||||||
|
strings.Contains(stack, "runtime.MHeap_Scavenger") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gs = append(gs, g)
|
||||||
|
}
|
||||||
|
sort.Strings(gs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var failOnLeaks = flag.Bool("fail_on_leaks", false, "Fail tests if goroutines leak.")
|
||||||
|
|
||||||
|
// leakCheck snapshots the currently-running goroutines and returns a
|
||||||
|
// function to be run at the end of tests to see whether any
|
||||||
|
// goroutines leaked.
|
||||||
|
func leakCheck(t testing.TB) func() {
|
||||||
|
orig := map[string]bool{}
|
||||||
|
for _, g := range interestingGoroutines() {
|
||||||
|
orig[g] = true
|
||||||
|
}
|
||||||
|
leakf := t.Logf
|
||||||
|
if *failOnLeaks {
|
||||||
|
leakf = t.Errorf
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
// Loop, waiting for goroutines to shut down.
|
||||||
|
// Wait up to 5 seconds, but finish as quickly as possible.
|
||||||
|
deadline := time.Now().Add(5 * time.Second)
|
||||||
|
for {
|
||||||
|
var leaked []string
|
||||||
|
for _, g := range interestingGoroutines() {
|
||||||
|
if !orig[g] {
|
||||||
|
leaked = append(leaked, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(leaked) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if time.Now().Before(deadline) {
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, g := range leaked {
|
||||||
|
leakf("Leaked goroutine: %v", g)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user