diff --git a/interop-testing/build.gradle b/interop-testing/build.gradle index 0b867c4e28..1d6c1f42d4 100644 --- a/interop-testing/build.gradle +++ b/interop-testing/build.gradle @@ -3,16 +3,6 @@ apply plugin: 'application' description = "gRPC: Integration Testing" startScripts.enabled = false -// Add dependency on the protobuf plugin -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath libraries.protobuf_plugin - } -} - dependencies { compile project(':grpc-auth'), project(':grpc-core'), @@ -21,6 +11,7 @@ dependencies { project(':grpc-protobuf'), project(':grpc-stub'), project(':grpc-testing'), + project(':grpc-testing-proto'), libraries.junit, libraries.mockito, libraries.netty_tcnative, @@ -84,13 +75,3 @@ applicationDistribution.into("bin") { from(stresstest_client) fileMode = 0755 } - -configureProtoCompilation() - -// Let intellij projects refer to generated code -idea { - module { - sourceDirs += file("${projectDir}/src/generated/main/java"); - sourceDirs += file("${projectDir}/src/generated/main/grpc"); - } -} diff --git a/settings.gradle b/settings.gradle index d9ea440620..ba0e64090d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include ":grpc-protobuf-nano" include ":grpc-netty" include ":grpc-grpclb" include ":grpc-testing" +include ":grpc-testing-proto" include ":grpc-interop-testing" include ":grpc-all" include ":grpc-benchmarks" @@ -27,6 +28,7 @@ project(':grpc-protobuf-nano').projectDir = "$rootDir/protobuf-nano" as File project(':grpc-netty').projectDir = "$rootDir/netty" as File project(':grpc-grpclb').projectDir = "$rootDir/grpclb" as File project(':grpc-testing').projectDir = "$rootDir/testing" as File +project(':grpc-testing-proto').projectDir = "$rootDir/testing-proto" as File project(':grpc-interop-testing').projectDir = "$rootDir/interop-testing" as File project(':grpc-all').projectDir = "$rootDir/all" as File project(':grpc-benchmarks').projectDir = "$rootDir/benchmarks" as File diff --git a/testing-proto/build.gradle b/testing-proto/build.gradle new file mode 100644 index 0000000000..f5fc71ecec --- /dev/null +++ b/testing-proto/build.gradle @@ -0,0 +1,26 @@ +description = "gRPC: Testing Protos" + +// Add dependency on the protobuf plugin +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath libraries.protobuf_plugin + } +} + +dependencies { + compile project(':grpc-protobuf'), + project(':grpc-stub') +} + +configureProtoCompilation() + +// Let intellij projects refer to generated code +idea { + module { + sourceDirs += file("${projectDir}/src/generated/main/java") + sourceDirs += file("${projectDir}/src/generated/main/grpc") + } +} diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java diff --git a/interop-testing/src/generated/main/java/com/google/protobuf/EmptyProtos.java b/testing-proto/src/generated/main/java/com/google/protobuf/EmptyProtos.java similarity index 100% rename from interop-testing/src/generated/main/java/com/google/protobuf/EmptyProtos.java rename to testing-proto/src/generated/main/java/com/google/protobuf/EmptyProtos.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Messages.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Messages.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Messages.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Messages.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Metrics.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Metrics.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Metrics.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Metrics.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Test.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Test.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Test.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Test.java diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto index 8f71229b7f..af5591b665 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -43,4 +42,4 @@ option java_outer_classname = "EmptyProtos"; // rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; // }; // -message Empty {} \ No newline at end of file +message Empty {} diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto index f18a632944..5110719e82 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -176,4 +175,4 @@ message ReconnectParams { message ReconnectInfo { bool passed = 1; repeated int32 backoff_ms = 2; -} \ No newline at end of file +} diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/metrics.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/metrics.proto similarity index 100% rename from interop-testing/src/main/proto/io/grpc/testing/integration/metrics.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/metrics.proto diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/test.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/test.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/test.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/test.proto index 23f8e8483a..e4a41b4c9e 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/test.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/test.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -80,7 +79,7 @@ service TestService { // that case. service UnimplementedService { // A call that no server should implement - rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); + rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); } // A service used to control reconnect server. diff --git a/testing/build.gradle b/testing/build.gradle index 80707895e0..2ebc78fa26 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -1,8 +1,11 @@ description = "gRPC: Testing" + dependencies { compile project(':grpc-core'), project(':grpc-stub'), libraries.junit, libraries.mockito, libraries.truth + + testCompile project(':grpc-testing-proto') } diff --git a/testing/src/main/java/io/grpc/testing/GrpcServerRule.java b/testing/src/main/java/io/grpc/testing/GrpcServerRule.java new file mode 100644 index 0000000000..8f8ccfc37a --- /dev/null +++ b/testing/src/main/java/io/grpc/testing/GrpcServerRule.java @@ -0,0 +1,157 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.testing; + +import io.grpc.BindableService; +import io.grpc.ExperimentalApi; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.ServerServiceDefinition; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.AbstractStub; +import io.grpc.util.MutableHandlerRegistry; + +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * {@code GrpcServerRule} is a JUnit {@link TestRule} that starts an in-process gRPC service with + * a {@link MutableHandlerRegistry} for adding services. It is particularly useful for mocking out + * external gRPC-based services and asserting that the expected requests were made. + * + *
An {@link AbstractStub} can be created against this service by using the
+ * {@link ManagedChannel} provided by {@link GrpcServerRule#getChannel()}.
+ */
+@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2488")
+public class GrpcServerRule extends ExternalResource {
+
+ private ManagedChannel channel;
+ private Server server;
+ private String serverName;
+ private MutableHandlerRegistry serviceRegistry;
+ private boolean useDirectExecutor;
+
+ /**
+ * Returns {@code this} configured to use a direct executor for the {@link ManagedChannel} and
+ * {@link Server}.
+ */
+ public final GrpcServerRule directExecutor() {
+ useDirectExecutor = true;
+ return this;
+ }
+
+ /**
+ * Returns a {@link ManagedChannel} connected to this service.
+ */
+ public final ManagedChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Returns the underlying gRPC {@link Server} for this service.
+ */
+ public final Server getServer() {
+ return server;
+ }
+
+ /**
+ * Returns the randomly generated server name for this service.
+ */
+ public final String getServerName() {
+ return serverName;
+ }
+
+ /**
+ * Returns the service registry for this service. The registry is used to add service instances
+ * (e.g. {@link BindableService} or {@link ServerServiceDefinition} to the server.
+ */
+ public final MutableHandlerRegistry getServiceRegistry() {
+ return serviceRegistry;
+ }
+
+ /**
+ * After the test has completed, clean up the channel and server.
+ */
+ @Override
+ protected void after() {
+ serverName = null;
+ serviceRegistry = null;
+
+ channel.shutdown();
+ server.shutdown();
+
+ try {
+ channel.awaitTermination(1, TimeUnit.MINUTES);
+ server.awaitTermination(1, TimeUnit.MINUTES);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ } finally {
+ channel.shutdownNow();
+ channel = null;
+
+ server.shutdownNow();
+ server = null;
+ }
+ }
+
+ /**
+ * Before the test has started, create the server and channel.
+ */
+ @Override
+ protected void before() throws Throwable {
+ serverName = UUID.randomUUID().toString();
+
+ serviceRegistry = new MutableHandlerRegistry();
+
+ InProcessServerBuilder serverBuilder = InProcessServerBuilder.forName(serverName)
+ .fallbackHandlerRegistry(serviceRegistry);
+
+ if (useDirectExecutor) {
+ serverBuilder.directExecutor();
+ }
+
+ server = serverBuilder.build().start();
+
+ InProcessChannelBuilder channelBuilder = InProcessChannelBuilder.forName(serverName);
+
+ if (useDirectExecutor) {
+ channelBuilder.directExecutor();
+ }
+
+ channel = channelBuilder.build();
+ }
+}
diff --git a/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java b/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java
new file mode 100644
index 0000000000..89171bd07d
--- /dev/null
+++ b/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2016, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.grpc.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.EmptyProtos;
+
+import io.grpc.ManagedChannel;
+import io.grpc.Server;
+import io.grpc.stub.StreamObserver;
+import io.grpc.testing.integration.Messages;
+import io.grpc.testing.integration.TestServiceGrpc;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runners.model.Statement;
+
+import java.util.Collection;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class GrpcServerRuleTest {
+
+ public static class WithoutDirectExecutor {
+
+ @Rule
+ public final GrpcServerRule grpcServerRule = new GrpcServerRule();
+
+ @Test
+ public void serverAndChannelAreStarted() {
+ assertThat(grpcServerRule.getServer().isShutdown()).isFalse();
+ assertThat(grpcServerRule.getServer().isTerminated()).isFalse();
+
+ assertThat(grpcServerRule.getChannel().isShutdown()).isFalse();
+ assertThat(grpcServerRule.getChannel().isTerminated()).isFalse();
+
+ assertThat(grpcServerRule.getServerName()).isNotNull();
+ assertThat(grpcServerRule.getServiceRegistry()).isNotNull();
+ }
+
+ @Test
+ public void serverAllowsServicesToBeAddedViaServiceRegistry() {
+ TestServiceImpl testService = new TestServiceImpl();
+
+ grpcServerRule.getServiceRegistry().addService(testService);
+
+ TestServiceGrpc.TestServiceBlockingStub stub =
+ TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel());
+
+ Messages.SimpleRequest request1 = Messages.SimpleRequest.newBuilder()
+ .setPayload(Messages.Payload.newBuilder()
+ .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString())))
+ .build();
+
+ Messages.SimpleRequest request2 = Messages.SimpleRequest.newBuilder()
+ .setPayload(Messages.Payload.newBuilder()
+ .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString())))
+ .build();
+
+ stub.unaryCall(request1);
+ stub.unaryCall(request2);
+
+ assertThat(testService.unaryCallRequests)
+ .containsExactly(request1, request2);
+ }
+
+ @Test
+ public void serviceIsNotRunOnSameThreadAsTest() {
+ TestServiceImpl testService = new TestServiceImpl();
+
+ grpcServerRule.getServiceRegistry().addService(testService);
+
+ TestServiceGrpc.TestServiceBlockingStub stub =
+ TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel());
+
+ // Make a garbage request first due to https://github.com/grpc/grpc-java/issues/2444.
+ stub.emptyCall(EmptyProtos.Empty.newBuilder().build());
+ stub.emptyCall(EmptyProtos.Empty.newBuilder().build());
+
+ assertThat(testService.lastEmptyCallRequestThread).isNotEqualTo(Thread.currentThread());
+ }
+ }
+
+ public static class WithDirectExecutor {
+
+ @Rule
+ public final GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();
+
+ @Test
+ public void serverAndChannelAreStarted() {
+ assertThat(grpcServerRule.getServer().isShutdown()).isFalse();
+ assertThat(grpcServerRule.getServer().isTerminated()).isFalse();
+
+ assertThat(grpcServerRule.getChannel().isShutdown()).isFalse();
+ assertThat(grpcServerRule.getChannel().isTerminated()).isFalse();
+
+ assertThat(grpcServerRule.getServerName()).isNotNull();
+ assertThat(grpcServerRule.getServiceRegistry()).isNotNull();
+ }
+
+ @Test
+ public void serverAllowsServicesToBeAddedViaServiceRegistry() {
+ TestServiceImpl testService = new TestServiceImpl();
+
+ grpcServerRule.getServiceRegistry().addService(testService);
+
+ TestServiceGrpc.TestServiceBlockingStub stub =
+ TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel());
+
+ Messages.SimpleRequest request1 = Messages.SimpleRequest.newBuilder()
+ .setPayload(Messages.Payload.newBuilder()
+ .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString())))
+ .build();
+
+ Messages.SimpleRequest request2 = Messages.SimpleRequest.newBuilder()
+ .setPayload(Messages.Payload.newBuilder()
+ .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString())))
+ .build();
+
+ stub.unaryCall(request1);
+ stub.unaryCall(request2);
+
+ assertThat(testService.unaryCallRequests)
+ .containsExactly(request1, request2);
+ }
+
+ @Test
+ public void serviceIsRunOnSameThreadAsTest() {
+ TestServiceImpl testService = new TestServiceImpl();
+
+ grpcServerRule.getServiceRegistry().addService(testService);
+
+ TestServiceGrpc.TestServiceBlockingStub stub =
+ TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel());
+
+ // Make a garbage request first due to https://github.com/grpc/grpc-java/issues/2444.
+ stub.emptyCall(EmptyProtos.Empty.newBuilder().build());
+ stub.emptyCall(EmptyProtos.Empty.newBuilder().build());
+
+ assertThat(testService.lastEmptyCallRequestThread).isEqualTo(Thread.currentThread());
+ }
+ }
+
+ public static class ResourceCleanup {
+
+ @Test
+ public void serverAndChannelAreShutdownAfterRule() throws Throwable {
+ GrpcServerRule grpcServerRule = new GrpcServerRule();
+
+ // Before the rule has been executed, all of its resources should be null.
+ assertThat(grpcServerRule.getChannel()).isNull();
+ assertThat(grpcServerRule.getServer()).isNull();
+ assertThat(grpcServerRule.getServerName()).isNull();
+ assertThat(grpcServerRule.getServiceRegistry()).isNull();
+
+ // The TestStatement stores the channel and server instances so that we can inspect them after
+ // the rule cleans up.
+ TestStatement statement = new TestStatement(grpcServerRule);
+
+ grpcServerRule.apply(statement, null).evaluate();
+
+ // Ensure that the stored channel and server instances were shut down.
+ assertThat(statement.channel.isShutdown()).isTrue();
+ assertThat(statement.server.isShutdown()).isTrue();
+
+ // All references to the resources that we created should be set to null.
+ assertThat(grpcServerRule.getChannel()).isNull();
+ assertThat(grpcServerRule.getServer()).isNull();
+ assertThat(grpcServerRule.getServerName()).isNull();
+ assertThat(grpcServerRule.getServiceRegistry()).isNull();
+ }
+
+ private static class TestStatement extends Statement {
+
+ private final GrpcServerRule grpcServerRule;
+
+ private ManagedChannel channel;
+ private Server server;
+
+ private TestStatement(GrpcServerRule grpcServerRule) {
+ this.grpcServerRule = grpcServerRule;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ channel = grpcServerRule.getChannel();
+ server = grpcServerRule.getServer();
+ }
+ }
+ }
+
+ private static class TestServiceImpl extends TestServiceGrpc.TestServiceImplBase {
+
+ private final Collection