ronantakizawa/codereview-bench huggingface dataset:
Repository: grpc/grpc-go
File: test/gracefulstop_test.go (Go)

Code before:

import (
	"context"
	"fmt"
	"net"
	"sync"
	"testing"
	"time"

	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/internal/stubserver"
	"google.golang.org/grpc/status"

	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
)

type delayListener struct {
	net.Listener
	closeCalled  chan struct{}
	acceptCalled chan struct{}
	allowCloseCh chan struct{}
	mu           sync.Mutex
	dialed       bool
	closed       bool
}

func (d *delayListener) Accept() (net.Conn, error) {
	select {
	case <-d.acceptCalled:
		// On the second call, block until closed, then return an error.
		<-d.closeCalled
		<-d.allowCloseCh
		return nil, fmt.Errorf("listener is closed")
	default:
		close(d.acceptCalled)
		conn, err := d.Listener.Accept()
		if err != nil {
			return nil, err
		}
		// Allow closing of listener only after accept.
		// Note: Dial can return successfully, yet Accept
		// might now have finished.
		d.allowClose()
		return conn, nil
	}
}


Review comment (suggestion):
it looks like we are introducing mutex only to update `closed`? Then it should be put right above it indicating that anything below is guarded by mutex.

Code after:

import (
	"context"
	"fmt"
	"net"
	"testing"
	"time"

	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/internal/stubserver"
	"google.golang.org/grpc/status"

	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
)

type delayListener struct {
	net.Listener
	closeCalled  chan struct{}
	acceptCalled chan struct{}
	allowCloseCh chan struct{}
	dialed       bool
}

func (d *delayListener) Accept() (net.Conn, error) {
	select {
	case <-d.acceptCalled:
		// On the second call, block until closed, then return an error.
		<-d.closeCalled
		<-d.allowCloseCh
		return nil, fmt.Errorf("listener is closed")
	default:
		close(d.acceptCalled)
		conn, err := d.Listener.Accept()
		if err != nil {
			return nil, err
		}
		// Allow closing of listener only after accept.
		// Note: Dial can return successfully, yet Accept
		// might now have finished.
		d.allowClose()
		return conn, nil
	}
}

func (d *delayListener) allowClose() {
	close(d.allowCloseCh)
}
Repository: grpc/grpc-go
File: test/compressor_test.go (Go)

Code before:
// max receive message size restricted to 99 bytes. The test verifies that the
// client receives a ResourceExhausted response from the server.
func (s) TestDecompressionExceedsMaxMessageSize(t *testing.T) {
	oldC := encoding.GetCompressor("gzip")
	defer func() {
		encoding.RegisterCompressor(oldC)
	}()
	messageLen := 100
	encoding.RegisterCompressor(&fakeCompressor{decompressedMessageSize: messageLen})
	ss := &stubserver.StubServer{
		UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
			return &testpb.SimpleResponse{}, nil
		},
	}
	if err := ss.Start([]grpc.ServerOption{grpc.MaxRecvMsgSize(messageLen - 1)}); err != nil {
		t.Fatalf("Error starting endpoint server: %v", err)
	}
	defer ss.Stop()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	p, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(50))
	if err != nil {
		t.Fatalf("Unexpected error from newPayload: %v", err)
	}
	req := &testpb.SimpleRequest{Payload: p}
	_, err = ss.Client.UnaryCall(ctx, req, grpc.UseCompressor("gzip"))
	if err == nil {
		t.Errorf("Client.UnaryCall(%+v) = nil, want %v", req, codes.ResourceExhausted)
	}

	if got, want := status.Code(err), codes.ResourceExhausted; got != want {
		t.Errorf("Client.UnaryCall(%+v) returned stats %v, want %v", req, got, want)
	}
}


Review comment (suggestion):
Is creating this payload required? The fake compressor implementation returns a `bytes.NewReader` with a size of `f.decompressedMessageSize` no matter what.

Code after:
// max receive message size restricted to 99 bytes. The test verifies that the
// client receives a ResourceExhausted response from the server.
func (s) TestDecompressionExceedsMaxMessageSize(t *testing.T) {
	oldC := encoding.GetCompressor("gzip")
	defer func() {
		encoding.RegisterCompressor(oldC)
	}()
	const messageLen = 100
	encoding.RegisterCompressor(&fakeCompressor{decompressedMessageSize: messageLen})
	ss := &stubserver.StubServer{
		UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
			return &testpb.SimpleResponse{}, nil
		},
	}
	if err := ss.Start([]grpc.ServerOption{grpc.MaxRecvMsgSize(messageLen - 1)}); err != nil {
		t.Fatalf("Error starting endpoint server: %v", err)
	}
	defer ss.Stop()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	req := &testpb.SimpleRequest{Payload: &testpb.Payload{}}
	_, err := ss.Client.UnaryCall(ctx, req, grpc.UseCompressor("gzip"))
	if got, want := status.Code(err), codes.ResourceExhausted; got != want {
		t.Errorf("Client.UnaryCall(%+v) returned status %v, want %v", req, got, want)
	}
}
Repository: grpc/grpc-go
File: resolver/manual/manual_test.go (Go)

Code before:
	t.Run("update_state_panics", func(t *testing.T) {
		defer func() {
			want := "cannot update state as grpc.Dial with resolver has not been called"
			if r := recover(); r != want {
				t.Errorf("expected panic %q, got %q", want, r)
			}
		}()
		r.UpdateState(resolver.State{Addresses: []resolver.Address{
			{Addr: "address"},
			{Addr: "anotheraddress"},
		}})
	})
	t.Run("report_error_panics", func(t *testing.T) {
		defer func() {
			want := "cannot report error as grpc.Dial with resolver has not been called"
			if r := recover(); r != want {
				t.Errorf("expected panic %q, got %q", want, r)
			}
		}()
		r.ReportError(errors.New("example"))
	})

	t.Run("happy_path", func(t *testing.T) {
		r.InitialState(resolver.State{Addresses: []resolver.Address{
			{Addr: "ok"},
		}})
		cc, err := grpc.NewClient("whatever://localhost",
			grpc.WithTransportCredentials(insecure.NewCredentials()),
			grpc.WithResolvers(r))
		if err != nil {
			t.Errorf("grpc.NewClient() failed: %v", err)
		}
		defer cc.Close()
		cc.Connect()
		r.ReportError(errors.New("example"))
	})
}


Review comment (suggestion):
Keep this as `UpdateState` please. This is a test for the manual resolver itself, by changing `UpdateState` to `InitialState` we may be changing the test scenario. `cc.Connect` is called anyways.

Code after:
	t.Run("update_state_panics", func(t *testing.T) {
		defer func() {
			want := "cannot update state as grpc.Dial with resolver has not been called"
			if r := recover(); r != want {
				t.Errorf("expected panic %q, got %q", want, r)
			}
		}()
		r.UpdateState(resolver.State{Addresses: []resolver.Address{
			{Addr: "address"},
			{Addr: "anotheraddress"},
		}})
	})
	t.Run("report_error_panics", func(t *testing.T) {
		defer func() {
			want := "cannot report error as grpc.Dial with resolver has not been called"
			if r := recover(); r != want {
				t.Errorf("expected panic %q, got %q", want, r)
			}
		}()
		r.ReportError(errors.New("example"))
	})

	t.Run("happy_path", func(t *testing.T) {
		cc, err := grpc.NewClient("whatever://localhost",
			grpc.WithTransportCredentials(insecure.NewCredentials()),
			grpc.WithResolvers(r))
		if err != nil {
			t.Errorf("grpc.NewClient() failed: %v", err)
		}
		defer cc.Close()
		cc.Connect()
		r.UpdateState(resolver.State{Addresses: []resolver.Address{
			{Addr: "ok"},
		}})
		r.ReportError(errors.New("example"))
	})
}
Repository: grpc/grpc-go
File: test/clientconn_state_transition_test.go (Go)

Code before:
			t.Error(err)
			return
		}

		go keepReading(conn)

		framer := http2.NewFramer(conn, conn)
		if err := framer.WriteSettings(http2.Setting{}); err != nil {
			t.Errorf("Error while writing settings frame. %v", err)
			return
		}

		// Prevents race between onPrefaceReceipt and onClose.
		<-sawReady

		conn.Close()
	}()

	client, err := grpc.NewClient(lis.Addr().String(),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, stateRecordingBalancerName)))
	if err != nil {
		t.Fatal(err)
	}
	defer client.Close()
	client.Connect()
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	go testutils.StayConnected(ctx, client)

	stateNotifications := testBalancerBuilder.nextStateNotifier()

	want := []connectivity.State{
		connectivity.Connecting,
		connectivity.Ready,
		connectivity.Idle,
		connectivity.Connecting,
	}
	for i := 0; i < len(want); i++ {
		select {
		case <-time.After(defaultTestTimeout):
			t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want)
		case seen := <-stateNotifications:
			if seen == connectivity.Ready {
				sawReady <- struct{}{}
			}
			if seen != want[i] {
				t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen)
			}
		}
	}

Review comment (suggestion):
There is a call to `testutils.StayConnected` below, do we need to call `connect` here? Please consider this every time you replace Dial with NewClient across all PRs. 

Code after:
			t.Error(err)
			return
		}

		go keepReading(conn)

		framer := http2.NewFramer(conn, conn)
		if err := framer.WriteSettings(http2.Setting{}); err != nil {
			t.Errorf("Error while writing settings frame. %v", err)
			return
		}

		// Prevents race between onPrefaceReceipt and onClose.
		<-sawReady

		conn.Close()
	}()

	client, err := grpc.NewClient(lis.Addr().String(),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, stateRecordingBalancerName)))
	if err != nil {
		t.Fatal(err)
	}
	defer client.Close()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	go testutils.StayConnected(ctx, client)
	testutils.AwaitNotState(ctx, t, client, connectivity.Idle)
	stateNotifications := testBalancerBuilder.nextStateNotifier()

	want := []connectivity.State{
		connectivity.Connecting,
		connectivity.Ready,
		connectivity.Idle,
		connectivity.Connecting,
	}
	for i := 0; i < len(want); i++ {
		select {
		case <-time.After(defaultTestTimeout):
			t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want)
		case seen := <-stateNotifications:
			if seen == connectivity.Ready {
				sawReady <- struct{}{}
			}
			if seen != want[i] {
				t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen)
			}
		}
	}
Repository: grpc/grpc-go
File: clientconn_test.go (Go)

Code before:
			defer cc.Close()
			if cc.Target() != test.addr {
				t.Fatalf("Target() = %s, want %s", cc.Target(), test.addr)
			}
			if cc.CanonicalTarget() != test.canonicalTargetWant {
				t.Fatalf("CanonicalTarget() = %s, want %s", cc.CanonicalTarget(), test.canonicalTargetWant)
			}
		})
	}
}

type backoffForever struct{}

func (b backoffForever) Backoff(int) time.Duration { return time.Duration(math.MaxInt64) }

func (s) TestResetConnectBackoff(t *testing.T) {
	clients := make(chan struct{})
	defer func() { // If we fail, let the http2client break out of newclient.
		select {
		case <-clients:
		default:
		}
	}()
	newclient := func(string, time.Duration) (net.Conn, error) {
		clients <- struct{}{}
		return nil, errors.New("Failed to create a fake NewClient")
	}
	cc, err := NewClient("passthrough:///", WithTransportCredentials(insecure.NewCredentials()), WithDialer(newclient), withBackoff(backoffForever{}))
	if err != nil {
		t.Fatalf("NewClient() = _, %v; want _, nil", err)
	}
	defer cc.Close()
	go stayConnected(cc)
	select {
	case <-clients:
	case <-time.NewTimer(10 * time.Second).C:
		t.Fatal("Failed to call NewClient within 10s")
	}

	select {
	case <-clients:
		t.Fatal("NewClient called unexpectedly before resetting backoff")
	case <-time.NewTimer(100 * time.Millisecond).C:
	}

	cc.ResetConnectBackoff()

	select {
	case <-clients:
	case <-time.NewTimer(10 * time.Second).C:
		t.Fatal("Failed to call NewClient within 10s after resetting backoff")

Review comment (suggestion):
`newClient` should still be named `dialer` because it is a custom dialer. Similarly, the channel `clients` should still be named `dials`.

Code after:
			defer cc.Close()
			if cc.Target() != test.addr {
				t.Fatalf("Target() = %s, want %s", cc.Target(), test.addr)
			}
			if cc.CanonicalTarget() != test.canonicalTargetWant {
				t.Fatalf("CanonicalTarget() = %s, want %s", cc.CanonicalTarget(), test.canonicalTargetWant)
			}
		})
	}
}

type backoffForever struct{}

func (b backoffForever) Backoff(int) time.Duration { return time.Duration(math.MaxInt64) }

func (s) TestResetConnectBackoff(t *testing.T) {
	dials := make(chan struct{})
	defer func() { // If we fail, let the http2client break out of dialing.
		select {
		case <-dials:
		default:
		}
	}()
	dialer := func(string, time.Duration) (net.Conn, error) {
		dials <- struct{}{}
		return nil, errors.New("failed to fake dial")
	}
	cc, err := NewClient("passthrough:///", WithTransportCredentials(insecure.NewCredentials()), WithDialer(dialer), withBackoff(backoffForever{}))
	if err != nil {
		t.Fatalf("grpc.NewClient() failed with error: %v, want: nil", err)
	}
	defer cc.Close()
	go stayConnected(cc)
	select {
	case <-dials:
	case <-time.NewTimer(10 * time.Second).C:
		t.Fatal("Failed to call dial within 10s")
	}

	select {
	case <-dials:
		t.Fatal("Dial called unexpectedly before resetting backoff")
	case <-time.NewTimer(100 * time.Millisecond).C:
	}

	cc.ResetConnectBackoff()

	select {
	case <-dials:
	case <-time.NewTimer(10 * time.Second).C:
		t.Fatal("Failed to call dial within 10s after resetting backoff")
Repository: grpc/grpc-go
File: balancer_wrapper_test.go (Go)

Code before:
				<-started.Done()
				// In a goroutine, create the subconn.
				go func() {
					_, err := bd.ClientConn.NewSubConn(ccs.ResolverState.Addresses, balancer.NewSubConnOptions{
						StateListener: func(scs balancer.SubConnState) {
							t.Error("Unexpected call to StateListener with:", scs)
						},
					})
					if err != nil && !strings.Contains(err.Error(), "connection is closing") && !strings.Contains(err.Error(), "is deleted") && !strings.Contains(err.Error(), "is closed or idle") && !strings.Contains(err.Error(), "balancer is being closed") {
						t.Error("Unexpected error creating subconn:", err)
					}
					wg.Done()
				}()
				// At approximately the same time, close the channel.
				cc.Close()
				wg.Done()
			}()
			return nil
		},
	}
	stub.Register(t.Name(), bf)
	svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name())

	cc, err := NewClient("passthrough:///test.server", WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(svcCfg))
	if err != nil {
		t.Fatal("Failed to create a client for server:", err)
	}
	cc.Connect()
	started.Fire()

	// Wait for the LB policy to call NewSubConn and cc.Close.
	wg.Wait()
}


Review comment (bug):
This doesn't seem to be a very useful test failure message. How about just saying `t.Fatal("NewClient() failed: %v", err)`. Here and elsewhere in this PR. Thanks.

Code after:
				<-started.Done()
				// In a goroutine, create the subconn.
				go func() {
					_, err := bd.ClientConn.NewSubConn(ccs.ResolverState.Addresses, balancer.NewSubConnOptions{
						StateListener: func(scs balancer.SubConnState) {
							t.Error("Unexpected call to StateListener with:", scs)
						},
					})
					if err != nil && !strings.Contains(err.Error(), "connection is closing") && !strings.Contains(err.Error(), "is deleted") && !strings.Contains(err.Error(), "is closed or idle") && !strings.Contains(err.Error(), "balancer is being closed") {
						t.Error("Unexpected error creating subconn:", err)
					}
					wg.Done()
				}()
				// At approximately the same time, close the channel.
				cc.Close()
				wg.Done()
			}()
			return nil
		},
	}
	stub.Register(t.Name(), bf)
	svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name())

	cc, err := NewClient("passthrough:///test.server", WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(svcCfg))
	if err != nil {
		t.Fatalf("grpc.NewClient() failed: %v", err)
	}
	cc.Connect()
	started.Fire()

	// Wait for the LB policy to call NewSubConn and cc.Close.
	wg.Wait()
}
Repository: grpc/grpc-go
File: interop/alts/client/client.go (Go)

Code before:
	"google.golang.org/grpc/credentials/alts"
	"google.golang.org/grpc/grpclog"

	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
)

var (
	hsAddr     = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
	serverAddr = flag.String("server_address", ":8080", "The port on which the server is listening")

	logger = grpclog.Component("interop")
)

func main() {
	flag.Parse()

	opts := alts.DefaultClientOptions()
	if *hsAddr != "" {
		opts.HandshakerServiceAddress = *hsAddr
	}
	altsTC := alts.NewClientCreds(opts)
	// Block until the server is ready.
	conn, err := grpc.NewClient(*serverAddr, grpc.WithTransportCredentials(altsTC), grpc.WithBlock())
	if err != nil {
		logger.Fatalf("gRPC Client: NewClient() failed %v: %v", *serverAddr, err)
	}
	defer conn.Close()
	grpcClient := testgrpc.NewTestServiceClient(conn)

	// Call the EmptyCall API.
	ctx := context.Background()
	request := &testpb.Empty{}
	if _, err := grpcClient.EmptyCall(ctx, request); err != nil {
		logger.Fatalf("grpc Client: EmptyCall(_, %v) failed: %v", request, err)
	}
	logger.Info("grpc Client: empty call succeeded")

	// This sleep prevents the connection from being abruptly disconnected
	// when running this binary (along with grpc_server) on GCP dev cluster.
	time.Sleep(1 * time.Second)
}


Review comment (bug):
```suggestion
logger.Fatalf("gRPC Client: failed to create a client for server at %q: %v", *serverAddr, err)
```

Code after:
	"google.golang.org/grpc/credentials/alts"
	"google.golang.org/grpc/grpclog"

	testgrpc "google.golang.org/grpc/interop/grpc_testing"
	testpb "google.golang.org/grpc/interop/grpc_testing"
)

var (
	hsAddr     = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
	serverAddr = flag.String("server_address", ":8080", "The port on which the server is listening")

	logger = grpclog.Component("interop")
)

func main() {
	flag.Parse()

	opts := alts.DefaultClientOptions()
	if *hsAddr != "" {
		opts.HandshakerServiceAddress = *hsAddr
	}
	altsTC := alts.NewClientCreds(opts)
	// Block until the server is ready.
	conn, err := grpc.NewClient(*serverAddr, grpc.WithTransportCredentials(altsTC))
	if err != nil {
		logger.Fatalf("grpc.NewClient(%q) = %v", *serverAddr, err)
	}
	defer conn.Close()
	grpcClient := testgrpc.NewTestServiceClient(conn)

	// Call the EmptyCall API.
	ctx := context.Background()
	request := &testpb.Empty{}
	if _, err := grpcClient.EmptyCall(ctx, request); err != nil {
		logger.Fatalf("grpc Client: EmptyCall(_, %v) failed: %v", request, err)
	}
	logger.Info("grpc Client: empty call succeeded")

	// This sleep prevents the connection from being abruptly disconnected
	// when running this binary (along with grpc_server) on GCP dev cluster.
	time.Sleep(1 * time.Second)
}
Repository: grpc/grpc-go
File: interop/http2/negative_http2_client.go (Go)

Code before:
func ping(ctx context.Context, tc testgrpc.TestServiceClient) {
	// The server will assert that every ping it sends was ACK-ed by the client.
	interop.DoLargeUnaryCall(ctx, tc)
}

func maxStreams(ctx context.Context, tc testgrpc.TestServiceClient) {
	interop.DoLargeUnaryCall(ctx, tc)
	var wg sync.WaitGroup
	for i := 0; i < 15; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			interop.DoLargeUnaryCall(ctx, tc)
		}()
	}
	wg.Wait()
}

func main() {
	flag.Parse()
	serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	conn, err := grpc.NewClient(serverAddr, opts...)
	if err != nil {
		logger.Fatalf("NewClient() failed: %v", err)
	}
	defer conn.Close()
	tc := testgrpc.NewTestServiceClient(conn)
	ctx := context.Background()
	switch *testCase {
	case "goaway":
		goaway(ctx, tc)
		logger.Infoln("goaway done")
	case "rst_after_header":
		rstAfterHeader(tc)
		logger.Infoln("rst_after_header done")
	case "rst_during_data":
		rstDuringData(tc)
		logger.Infoln("rst_during_data done")
	case "rst_after_data":
		rstAfterData(tc)
		logger.Infoln("rst_after_data done")
	case "ping":
		ping(ctx, tc)
		logger.Infoln("ping done")
	case "max_streams":
		maxStreams(ctx, tc)
		logger.Infoln("max_streams done")
	default:
		logger.Fatal("Unsupported test case: ", *testCase)

Review comment (bug):
```suggestion
logger.Fatalf("Failed to create a client: %v", err)
```

Code after:
func ping(ctx context.Context, tc testgrpc.TestServiceClient) {
	// The server will assert that every ping it sends was ACK-ed by the client.
	interop.DoLargeUnaryCall(ctx, tc)
}

func maxStreams(ctx context.Context, tc testgrpc.TestServiceClient) {
	interop.DoLargeUnaryCall(ctx, tc)
	var wg sync.WaitGroup
	for i := 0; i < 15; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			interop.DoLargeUnaryCall(ctx, tc)
		}()
	}
	wg.Wait()
}

func main() {
	flag.Parse()
	serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	conn, err := grpc.NewClient(serverAddr, opts...)
	if err != nil {
		logger.Fatalf("grpc.NewClient(%q) = %v", serverAddr, err)
	}
	defer conn.Close()
	tc := testgrpc.NewTestServiceClient(conn)
	ctx := context.Background()
	switch *testCase {
	case "goaway":
		goaway(ctx, tc)
		logger.Infoln("goaway done")
	case "rst_after_header":
		rstAfterHeader(tc)
		logger.Infoln("rst_after_header done")
	case "rst_during_data":
		rstDuringData(tc)
		logger.Infoln("rst_during_data done")
	case "rst_after_data":
		rstAfterData(tc)
		logger.Infoln("rst_after_data done")
	case "ping":
		ping(ctx, tc)
		logger.Infoln("ping done")
	case "max_streams":
		maxStreams(ctx, tc)
		logger.Infoln("max_streams done")
	default:
		logger.Fatal("Unsupported test case: ", *testCase)