TestClientConnClose_WithPendingRPC tests the scenario where the channel has not yet received any update from the name resolver and hence RPCs are blocking. The test verifies that closing the ClientConn unblocks the RPC with the expected error code.
(t *testing.T)
| 47 | // blocking. The test verifies that closing the ClientConn unblocks the RPC with |
| 48 | // the expected error code. |
| 49 | func (s) TestClientConnClose_WithPendingRPC(t *testing.T) { |
| 50 | r := manual.NewBuilderWithScheme("whatever") |
| 51 | cc, err := grpc.NewClient(r.Scheme()+":///test.server", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) |
| 52 | if err != nil { |
| 53 | t.Fatalf("grpc.NewClient() failed: %v", err) |
| 54 | } |
| 55 | client := testgrpc.NewTestServiceClient(cc) |
| 56 | |
| 57 | ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) |
| 58 | defer cancel() |
| 59 | doneErrCh := make(chan error, 1) |
| 60 | go func() { |
| 61 | // This RPC would block until the ClientConn is closed, because the |
| 62 | // resolver has not provided its first update yet. |
| 63 | _, err := client.EmptyCall(ctx, &testpb.Empty{}) |
| 64 | if status.Code(err) != codes.Canceled || !strings.Contains(err.Error(), "client connection is closing") { |
| 65 | doneErrCh <- fmt.Errorf("EmptyCall() = %v, want %s", err, codes.Canceled) |
| 66 | } |
| 67 | doneErrCh <- nil |
| 68 | }() |
| 69 | |
| 70 | // Make sure that there is one pending RPC on the ClientConn before attempting |
| 71 | // to close it. If we don't do this, cc.Close() can happen before the above |
| 72 | // goroutine gets to make the RPC. |
| 73 | for { |
| 74 | if err := ctx.Err(); err != nil { |
| 75 | t.Fatal(err) |
| 76 | } |
| 77 | tcs, _ := channelz.GetTopChannels(0, 0) |
| 78 | if len(tcs) != 1 { |
| 79 | t.Fatalf("there should only be one top channel, not %d", len(tcs)) |
| 80 | } |
| 81 | started := tcs[0].ChannelMetrics.CallsStarted.Load() |
| 82 | completed := tcs[0].ChannelMetrics.CallsSucceeded.Load() + tcs[0].ChannelMetrics.CallsFailed.Load() |
| 83 | if (started - completed) == 1 { |
| 84 | break |
| 85 | } |
| 86 | time.Sleep(defaultTestShortTimeout) |
| 87 | } |
| 88 | cc.Close() |
| 89 | if err := <-doneErrCh; err != nil { |
| 90 | t.Fatal(err) |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | type testStatsHandler struct { |
| 95 | nameResolutionDelayed bool |
nothing calls this directly
no test coverage detected