() {
@@ -202,12 +222,17 @@ public class RouteGuideClient {
public void onNext(RouteNote note) {
info("Got message \"{0}\" at {1}, {2}", note.getMessage(), note.getLocation()
.getLatitude(), note.getLocation().getLongitude());
+ if (testHelper != null) {
+ testHelper.onMessage(note);
+ }
}
@Override
public void onError(Throwable t) {
- Status status = Status.fromThrowable(t);
- logger.log(Level.WARNING, "RouteChat Failed: {0}", status);
+ warning("RouteChat Failed: {0}", Status.fromThrowable(t));
+ if (testHelper != null) {
+ testHelper.onRpcError(t);
+ }
finishLatch.countDown();
}
@@ -236,8 +261,8 @@ public class RouteGuideClient {
// Mark the end of requests
requestObserver.onCompleted();
- // Receiving happens asynchronously
- finishLatch.await(1, TimeUnit.MINUTES);
+ // return the latch while receiving happens asynchronously
+ return finishLatch;
}
/** Issues several different requests and then exits. */
@@ -265,18 +290,55 @@ public class RouteGuideClient {
client.recordRoute(features, 10);
// Send and receive some notes.
- client.routeChat();
+ CountDownLatch finishLatch = client.routeChat();
+
+ if (!finishLatch.await(1, TimeUnit.MINUTES)) {
+ client.warning("routeChat can not finish within 1 minutes");
+ }
} finally {
client.shutdown();
}
}
- private static void info(String msg, Object... params) {
+ private void info(String msg, Object... params) {
logger.log(Level.INFO, msg, params);
}
+ private void warning(String msg, Object... params) {
+ logger.log(Level.WARNING, msg, params);
+ }
+
private RouteNote newNote(String message, int lat, int lon) {
return RouteNote.newBuilder().setMessage(message)
.setLocation(Point.newBuilder().setLatitude(lat).setLongitude(lon).build()).build();
}
+
+ /**
+ * Only used for unit test, as we do not want to introduce randomness in unit test.
+ */
+ @VisibleForTesting
+ void setRandom(Random random) {
+ this.random = random;
+ }
+
+ /**
+ * Only used for helping unit test.
+ */
+ @VisibleForTesting
+ interface TestHelper {
+ /**
+ * Used for verify/inspect message received from server.
+ */
+ void onMessage(Message message);
+
+ /**
+ * Used for verify/inspect error received from server.
+ */
+ void onRpcError(Throwable exception);
+ }
+
+ @VisibleForTesting
+ void setTestHelper(TestHelper testHelper) {
+ this.testHelper = testHelper;
+ }
}
diff --git a/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideServer.java b/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideServer.java
index 3b7c9971f8..aac4f706a9 100644
--- a/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideServer.java
+++ b/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideServer.java
@@ -188,7 +188,7 @@ public class RouteGuideServer {
int featureCount;
int distance;
Point previous;
- long startTime = System.nanoTime();
+ final long startTime = System.nanoTime();
@Override
public void onNext(Point point) {
@@ -295,7 +295,7 @@ public class RouteGuideServer {
double lat2 = RouteGuideUtil.getLatitude(end);
double lon1 = RouteGuideUtil.getLongitude(start);
double lon2 = RouteGuideUtil.getLongitude(end);
- int r = 6371000; // metres
+ int r = 6371000; // meters
double phi1 = toRadians(lat1);
double phi2 = toRadians(lat2);
double deltaPhi = toRadians(lat2 - lat1);
diff --git a/examples/src/test/java/io/grpc/examples/header/HeaderClientInterceptorTest.java b/examples/src/test/java/io/grpc/examples/header/HeaderClientInterceptorTest.java
new file mode 100644
index 0000000000..b01c08f167
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/header/HeaderClientInterceptorTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.examples.header;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.Server;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.ServerInterceptors;
+import io.grpc.StatusRuntimeException;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.GreeterGrpc.GreeterBlockingStub;
+import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
+
+/**
+ * Unit tests for {@link HeaderClientInterceptor}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ * For basic unit test examples see {@link io.grpc.examples.helloworld.HelloWorldClientTest} and
+ * {@link io.grpc.examples.helloworld.HelloWorldServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class HeaderClientInterceptorTest {
+
+ private final ServerInterceptor mockServerInterceptor = spy(
+ new ServerInterceptor() {
+ @Override
+ public Listener interceptCall(
+ ServerCall call, Metadata headers, ServerCallHandler next) {
+ return next.startCall(call, headers);
+ }
+ });
+
+ private Server fakeServer;
+ private ManagedChannel inProcessChannel;
+
+ @Before
+ public void setUp() throws Exception {
+ String uniqueServerName = "fake server for " + getClass();
+ fakeServer = InProcessServerBuilder.forName(uniqueServerName)
+ .addService(ServerInterceptors.intercept(new GreeterImplBase() {}, mockServerInterceptor))
+ .directExecutor()
+ .build()
+ .start();
+ inProcessChannel = InProcessChannelBuilder.forName(uniqueServerName)
+ .intercept(new HeaderClientInterceptor())
+ .directExecutor()
+ .build();
+ }
+
+ @After
+ public void tearDown() {
+ inProcessChannel.shutdownNow();
+ fakeServer.shutdownNow();
+ }
+
+ @Test
+ public void clientHeaderDeliveredToServer() {
+ GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(inProcessChannel);
+ ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
+
+ try {
+ blockingStub.sayHello(HelloRequest.getDefaultInstance());
+ fail();
+ } catch (StatusRuntimeException expected) {
+ // expected because the method is not implemented at server side
+ }
+
+ verify(mockServerInterceptor).interceptCall(
+ Matchers.>any(),
+ metadataCaptor.capture(),
+ Matchers.>any());
+ assertEquals(
+ "customRequestValue",
+ metadataCaptor.getValue().get(HeaderClientInterceptor.CUSTOM_HEADER_KEY));
+ }
+}
diff --git a/examples/src/test/java/io/grpc/examples/header/HeaderServerInterceptorTest.java b/examples/src/test/java/io/grpc/examples/header/HeaderServerInterceptorTest.java
new file mode 100644
index 0000000000..e1c8a1e8a4
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/header/HeaderServerInterceptorTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.examples.header;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import io.grpc.Server;
+import io.grpc.ServerInterceptors;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.GreeterGrpc.GreeterBlockingStub;
+import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Unit tests for {@link HeaderClientInterceptor}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ * For basic unit test examples see {@link io.grpc.examples.helloworld.HelloWorldClientTest} and
+ * {@link io.grpc.examples.helloworld.HelloWorldServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class HeaderServerInterceptorTest {
+ private Server fakeServer;
+ private ManagedChannel inProcessChannel;
+
+ @Before
+ public void setUp() throws Exception {
+ String uniqueServerName = "fake server for " + getClass();
+ GreeterImplBase greeterImplBase =
+ new GreeterImplBase() {
+ @Override
+ public void sayHello(HelloRequest request, StreamObserver responseObserver) {
+ responseObserver.onNext(HelloReply.getDefaultInstance());
+ responseObserver.onCompleted();
+ }
+ };
+ fakeServer = InProcessServerBuilder.forName(uniqueServerName)
+ .addService(ServerInterceptors.intercept(greeterImplBase, new HeaderServerInterceptor()))
+ .directExecutor()
+ .build()
+ .start();
+ inProcessChannel = InProcessChannelBuilder.forName(uniqueServerName).directExecutor().build();
+ }
+
+ @After
+ public void tearDown() {
+ inProcessChannel.shutdownNow();
+ fakeServer.shutdownNow();
+ }
+
+ @Test
+ public void serverHeaderDeliveredToClient() {
+ class SpyingClientInterceptor implements ClientInterceptor {
+ ClientCall.Listener> spyListener;
+
+ @Override
+ public ClientCall interceptCall(
+ MethodDescriptor method, CallOptions callOptions, Channel next) {
+ return new SimpleForwardingClientCall(next.newCall(method, callOptions)) {
+ @Override
+ public void start(Listener responseListener, Metadata headers) {
+ spyListener = responseListener = spy(responseListener);
+ super.start(responseListener, headers);
+ }
+ };
+ }
+ }
+
+ SpyingClientInterceptor clientInterceptor = new SpyingClientInterceptor();
+ GreeterBlockingStub blockingStub =
+ GreeterGrpc.newBlockingStub(inProcessChannel).withInterceptors(clientInterceptor);
+ ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
+
+ blockingStub.sayHello(HelloRequest.getDefaultInstance());
+
+ assertNotNull(clientInterceptor.spyListener);
+ verify(clientInterceptor.spyListener).onHeaders(metadataCaptor.capture());
+ assertEquals(
+ "customRespondValue",
+ metadataCaptor.getValue().get(HeaderServerInterceptor.CUSTOM_HEADER_KEY));
+ }
+}
diff --git a/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldClientTest.java b/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldClientTest.java
new file mode 100644
index 0000000000..d0614f28fa
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldClientTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015, 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.examples.helloworld;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.Server;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
+
+/**
+ * Unit tests for {@link HelloWorldClient}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ * For more unit test examples see {@link io.grpc.examples.routeguide.RouteGuideClientTest} and
+ * {@link io.grpc.examples.routeguide.RouteGuideServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class HelloWorldClientTest {
+ private final GreeterGrpc.GreeterImplBase serviceImpl = spy(new GreeterGrpc.GreeterImplBase() {});
+
+ private Server fakeServer;
+ private HelloWorldClient client;
+
+ /**
+ * Creates and starts a fake in-process server, and creates a client with an in-process channel.
+ */
+ @Before
+ public void setUp() throws Exception {
+ String uniqueServerName = "fake server for " + getClass();
+ fakeServer = InProcessServerBuilder
+ .forName(uniqueServerName).directExecutor().addService(serviceImpl).build().start();
+ ManagedChannelBuilder channelBuilder =
+ InProcessChannelBuilder.forName(uniqueServerName).directExecutor();
+ client = new HelloWorldClient(channelBuilder);
+ }
+
+ /**
+ * Shuts down the client and server.
+ */
+ @After
+ public void tearDown() throws Exception {
+ client.shutdown();
+ fakeServer.shutdownNow();
+ }
+
+ /**
+ * To test the client, call from the client against the fake server, and verify behaviors or state
+ * changes from the server side.
+ */
+ @Test
+ public void greet_messageDeliveredToServer() {
+ ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HelloRequest.class);
+ String testName = "test name";
+
+ client.greet(testName);
+
+ verify(serviceImpl)
+ .sayHello(requestCaptor.capture(), Matchers.>any());
+ assertEquals(testName, requestCaptor.getValue().getName());
+ }
+}
diff --git a/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldServerTest.java b/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldServerTest.java
new file mode 100644
index 0000000000..9f51258fea
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldServerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.examples.helloworld;
+
+import static org.junit.Assert.assertEquals;
+
+import io.grpc.ManagedChannel;
+import io.grpc.Server;
+import io.grpc.examples.helloworld.HelloWorldServer.GreeterImpl;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link HelloWorldServer}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ * For more unit test examples see {@link io.grpc.examples.routeguide.RouteGuideClientTest} and
+ * {@link io.grpc.examples.routeguide.RouteGuideServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class HelloWorldServerTest {
+ private static final String UNIQUE_SERVER_NAME =
+ "in-process server for " + HelloWorldServerTest.class;
+ private final Server inProcessServer = InProcessServerBuilder
+ .forName(UNIQUE_SERVER_NAME).addService(new GreeterImpl()).directExecutor().build();
+ private final ManagedChannel inProcessChannel =
+ InProcessChannelBuilder.forName(UNIQUE_SERVER_NAME).directExecutor().build();
+
+ /**
+ * Creates and starts the server with the {@link InProcessServerBuilder},
+ * and creates an in-process channel with the {@link InProcessChannelBuilder}.
+ */
+ @Before
+ public void setUp() throws Exception {
+ inProcessServer.start();
+ }
+
+ /**
+ * Shuts down the in-process channel and server.
+ */
+ @After
+ public void tearDown() {
+ inProcessChannel.shutdownNow();
+ inProcessServer.shutdownNow();
+ }
+
+ /**
+ * To test the server, make calls with a real stub using the in-process channel, and verify
+ * behaviors or state changes from the client side.
+ */
+ @Test
+ public void greeterImpl_replyMessage() throws Exception {
+ GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(inProcessChannel);
+ String testName = "test name";
+
+ HelloReply reply = blockingStub.sayHello(HelloRequest.newBuilder().setName(testName).build());
+
+ assertEquals("Hello " + testName, reply.getMessage());
+ }
+}
diff --git a/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java
new file mode 100644
index 0000000000..c5a61ea778
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java
@@ -0,0 +1,577 @@
+/*
+ * 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.examples.routeguide;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import com.google.protobuf.Message;
+
+import io.grpc.Server;
+import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+import io.grpc.examples.routeguide.RouteGuideClient.TestHelper;
+import io.grpc.examples.routeguide.RouteGuideGrpc.RouteGuideImplBase;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+import io.grpc.util.MutableHandlerRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Unit tests for {@link RouteGuideClient}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ *
For basic unit test examples see {@link io.grpc.examples.helloworld.HelloWorldClientTest} and
+ * {@link io.grpc.examples.helloworld.HelloWorldServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class RouteGuideClientTest {
+ private final MutableHandlerRegistry serviceRegistry = new MutableHandlerRegistry();
+ private final TestHelper testHelper = mock(TestHelper.class);
+ private final Random noRandomness =
+ new Random() {
+ int index;
+ boolean isForSleep;
+
+ /**
+ * Returns a number deterministically. If the random number is for sleep time, then return
+ * -500 so that {@code Thread.sleep(random.nextInt(1000) + 500)} sleeps 0 ms. Otherwise, it
+ * is for list index, then return incrementally (and cyclically).
+ */
+ @Override
+ public int nextInt(int bound) {
+ int retVal = isForSleep ? -500 : (index++ % bound);
+ isForSleep = ! isForSleep;
+ return retVal;
+ }
+ };
+ private Server fakeServer;
+ private RouteGuideClient client;
+
+ @Before
+ public void setUp() throws Exception {
+ String uniqueServerName = "fake server for " + getClass();
+
+ // use a mutable service registry for later registering the service impl for each test case.
+ fakeServer = InProcessServerBuilder.forName(uniqueServerName)
+ .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start();
+ client =
+ new RouteGuideClient(InProcessChannelBuilder.forName(uniqueServerName).directExecutor());
+ client.setTestHelper(testHelper);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ client.shutdown();
+ fakeServer.shutdownNow();
+ }
+
+ /**
+ * Example for testing blocking unary call.
+ */
+ @Test
+ public void getFeature() {
+ Point requestPoint = Point.newBuilder().setLatitude(-1).setLongitude(-1).build();
+ Point responsePoint = Point.newBuilder().setLatitude(-123).setLongitude(-123).build();
+ final AtomicReference pointDelivered = new AtomicReference();
+ final Feature responseFeature =
+ Feature.newBuilder().setName("dummyFeature").setLocation(responsePoint).build();
+
+ // implement the fake service
+ RouteGuideImplBase getFeatureImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public void getFeature(Point point, StreamObserver responseObserver) {
+ pointDelivered.set(point);
+ responseObserver.onNext(responseFeature);
+ responseObserver.onCompleted();
+ }
+ };
+ serviceRegistry.addService(getFeatureImpl);
+
+ client.getFeature(-1, -1);
+
+ assertEquals(requestPoint, pointDelivered.get());
+ verify(testHelper).onMessage(responseFeature);
+ verify(testHelper, never()).onRpcError(any(Throwable.class));
+ }
+
+ /**
+ * Example for testing blocking unary call.
+ */
+ @Test
+ public void getFeature_error() {
+ Point requestPoint = Point.newBuilder().setLatitude(-1).setLongitude(-1).build();
+ final AtomicReference pointDelivered = new AtomicReference();
+ final StatusRuntimeException fakeError = new StatusRuntimeException(Status.DATA_LOSS);
+
+ // implement the fake service
+ RouteGuideImplBase getFeatureImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public void getFeature(Point point, StreamObserver responseObserver) {
+ pointDelivered.set(point);
+ responseObserver.onError(fakeError);
+ }
+ };
+ serviceRegistry.addService(getFeatureImpl);
+
+ client.getFeature(-1, -1);
+
+ assertEquals(requestPoint, pointDelivered.get());
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class);
+ verify(testHelper).onRpcError(errorCaptor.capture());
+ assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
+ }
+
+ /**
+ * Example for testing blocking server-streaming.
+ */
+ @Test
+ public void listFeatures() {
+ final Feature responseFeature1 = Feature.newBuilder().setName("feature 1").build();
+ final Feature responseFeature2 = Feature.newBuilder().setName("feature 2").build();
+ final AtomicReference rectangleDelivered = new AtomicReference();
+
+ // implement the fake service
+ RouteGuideImplBase listFeaturesImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public void listFeatures(Rectangle rectangle, StreamObserver responseObserver) {
+ rectangleDelivered.set(rectangle);
+
+ // send two response messages
+ responseObserver.onNext(responseFeature1);
+ responseObserver.onNext(responseFeature2);
+
+ // complete the response
+ responseObserver.onCompleted();
+ }
+ };
+ serviceRegistry.addService(listFeaturesImpl);
+
+ client.listFeatures(1, 2, 3, 4);
+
+ assertEquals(Rectangle.newBuilder()
+ .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
+ .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
+ .build(),
+ rectangleDelivered.get());
+ verify(testHelper).onMessage(responseFeature1);
+ verify(testHelper).onMessage(responseFeature2);
+ verify(testHelper, never()).onRpcError(any(Throwable.class));
+ }
+
+ /**
+ * Example for testing blocking server-streaming.
+ */
+ @Test
+ public void listFeatures_error() {
+ final Feature responseFeature1 =
+ Feature.newBuilder().setName("feature 1").build();
+ final AtomicReference rectangleDelivered = new AtomicReference();
+ final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
+
+ // implement the fake service
+ RouteGuideImplBase listFeaturesImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public void listFeatures(Rectangle rectangle, StreamObserver responseObserver) {
+ rectangleDelivered.set(rectangle);
+
+ // send one response message
+ responseObserver.onNext(responseFeature1);
+
+ // let the rpc fail
+ responseObserver.onError(fakeError);
+ }
+ };
+ serviceRegistry.addService(listFeaturesImpl);
+
+ client.listFeatures(1, 2, 3, 4);
+
+ assertEquals(Rectangle.newBuilder()
+ .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
+ .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
+ .build(),
+ rectangleDelivered.get());
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class);
+ verify(testHelper).onMessage(responseFeature1);
+ verify(testHelper).onRpcError(errorCaptor.capture());
+ assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
+ }
+
+ /**
+ * Example for testing async client-streaming.
+ */
+ @Test
+ public void recordRoute() throws Exception {
+ client.setRandom(noRandomness);
+ Point point1 = Point.newBuilder().setLatitude(1).setLongitude(1).build();
+ Point point2 = Point.newBuilder().setLatitude(2).setLongitude(2).build();
+ Point point3 = Point.newBuilder().setLatitude(3).setLongitude(3).build();
+ Feature requestFeature1 =
+ Feature.newBuilder().setLocation(point1).build();
+ Feature requestFeature2 =
+ Feature.newBuilder().setLocation(point2).build();
+ Feature requestFeature3 =
+ Feature.newBuilder().setLocation(point3).build();
+ final List features = Arrays.asList(
+ requestFeature1, requestFeature2, requestFeature3);
+ final List pointsDelivered = new ArrayList();
+ final RouteSummary fakeResponse = RouteSummary
+ .newBuilder()
+ .setPointCount(7)
+ .setFeatureCount(8)
+ .setDistance(9)
+ .setElapsedTime(10)
+ .build();
+
+ // implement the fake service
+ RouteGuideImplBase recordRouteImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver recordRoute(
+ final StreamObserver responseObserver) {
+ StreamObserver requestObserver = new StreamObserver() {
+ @Override
+ public void onNext(Point value) {
+ pointsDelivered.add(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onNext(fakeResponse);
+ responseObserver.onCompleted();
+ }
+ };
+
+ return requestObserver;
+ }
+ };
+ serviceRegistry.addService(recordRouteImpl);
+
+ // send requestFeature1, requestFeature2, requestFeature3, and then requestFeature1 again
+ client.recordRoute(features, 4);
+
+ assertEquals(
+ Arrays.asList(
+ requestFeature1.getLocation(),
+ requestFeature2.getLocation(),
+ requestFeature3.getLocation(),
+ requestFeature1.getLocation()),
+ pointsDelivered);
+ verify(testHelper).onMessage(fakeResponse);
+ verify(testHelper, never()).onRpcError(any(Throwable.class));
+ }
+
+ /**
+ * Example for testing async client-streaming.
+ */
+ @Test
+ public void recordRoute_wrongResponse() throws Exception {
+ client.setRandom(noRandomness);
+ Point point1 = Point.newBuilder().setLatitude(1).setLongitude(1).build();
+ final Feature requestFeature1 =
+ Feature.newBuilder().setLocation(point1).build();
+ final List features = Arrays.asList(requestFeature1);
+
+ // implement the fake service
+ RouteGuideImplBase recordRouteImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver recordRoute(StreamObserver responseObserver) {
+ RouteSummary response = RouteSummary.getDefaultInstance();
+ // sending more than one responses is not right for client-streaming call.
+ responseObserver.onNext(response);
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+
+ return new StreamObserver() {
+ @Override
+ public void onNext(Point value) {
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+
+ @Override
+ public void onCompleted() {
+ }
+ };
+ }
+ };
+ serviceRegistry.addService(recordRouteImpl);
+
+ client.recordRoute(features, 4);
+
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class);
+ verify(testHelper).onRpcError(errorCaptor.capture());
+ assertEquals(Status.Code.CANCELLED, Status.fromThrowable(errorCaptor.getValue()).getCode());
+ }
+
+ /**
+ * Example for testing async client-streaming.
+ */
+ @Test
+ public void recordRoute_serverError() throws Exception {
+ client.setRandom(noRandomness);
+ Point point1 = Point.newBuilder().setLatitude(1).setLongitude(1).build();
+ final Feature requestFeature1 =
+ Feature.newBuilder().setLocation(point1).build();
+ final List features = Arrays.asList(requestFeature1);
+ final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
+
+ // implement the fake service
+ RouteGuideImplBase recordRouteImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver recordRoute(StreamObserver responseObserver) {
+ // send an error immediately
+ responseObserver.onError(fakeError);
+
+ StreamObserver requestObserver = new StreamObserver() {
+ @Override
+ public void onNext(Point value) {
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+
+ @Override
+ public void onCompleted() {
+ }
+ };
+ return requestObserver;
+ }
+ };
+ serviceRegistry.addService(recordRouteImpl);
+
+ client.recordRoute(features, 4);
+
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class);
+ verify(testHelper).onRpcError(errorCaptor.capture());
+ assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
+ }
+
+ /**
+ * Example for testing bi-directional call.
+ */
+ @Test
+ public void routeChat_simpleResponse() throws Exception {
+ RouteNote fakeResponse1 = RouteNote.newBuilder().setMessage("dummy msg1").build();
+ RouteNote fakeResponse2 = RouteNote.newBuilder().setMessage("dummy msg2").build();
+ final List messagesDelivered = new ArrayList();
+ final List locationsDelivered = new ArrayList();
+ final AtomicReference> responseObserverRef =
+ new AtomicReference>();
+ final CountDownLatch allRequestsDelivered = new CountDownLatch(1);
+ // implement the fake service
+ RouteGuideImplBase routeChatImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver routeChat(StreamObserver responseObserver) {
+ responseObserverRef.set(responseObserver);
+
+ StreamObserver requestObserver = new StreamObserver() {
+ @Override
+ public void onNext(RouteNote value) {
+ messagesDelivered.add(value.getMessage());
+ locationsDelivered.add(value.getLocation());
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+
+ @Override
+ public void onCompleted() {
+ allRequestsDelivered.countDown();
+ }
+ };
+
+ return requestObserver;
+ }
+ };
+ serviceRegistry.addService(routeChatImpl);
+
+ // start routeChat
+ CountDownLatch latch = client.routeChat();
+
+ // request message sent and delivered for four times
+ assertTrue(allRequestsDelivered.await(1, TimeUnit.SECONDS));
+ assertEquals(
+ Arrays.asList("First message", "Second message", "Third message", "Fourth message"),
+ messagesDelivered);
+ assertEquals(
+ Arrays.asList(
+ Point.newBuilder().setLatitude(0).setLongitude(0).build(),
+ Point.newBuilder().setLatitude(0).setLongitude(1).build(),
+ Point.newBuilder().setLatitude(1).setLongitude(0).build(),
+ Point.newBuilder().setLatitude(1).setLongitude(1).build()
+ ),
+ locationsDelivered);
+
+ // Let the server send out two simple response messages
+ // and verify that the client receives them.
+ // Allow some timeout for verify() if not using directExecutor
+ responseObserverRef.get().onNext(fakeResponse1);
+ verify(testHelper).onMessage(fakeResponse1);
+ responseObserverRef.get().onNext(fakeResponse2);
+ verify(testHelper).onMessage(fakeResponse2);
+
+ // let server complete.
+ responseObserverRef.get().onCompleted();
+
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+ verify(testHelper, never()).onRpcError(any(Throwable.class));
+ }
+
+ /**
+ * Example for testing bi-directional call.
+ */
+ @Test
+ public void routeChat_echoResponse() throws Exception {
+ final List notesDelivered = new ArrayList();
+
+ // implement the fake service
+ RouteGuideImplBase routeChatImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver routeChat(
+ final StreamObserver responseObserver) {
+ StreamObserver requestObserver = new StreamObserver() {
+ @Override
+ public void onNext(RouteNote value) {
+ notesDelivered.add(value);
+ responseObserver.onNext(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ responseObserver.onError(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onCompleted();
+ }
+ };
+
+ return requestObserver;
+ }
+ };
+ serviceRegistry.addService(routeChatImpl);
+
+ client.routeChat().await(1, TimeUnit.SECONDS);
+
+ String[] messages =
+ {"First message", "Second message", "Third message", "Fourth message"};
+ for (int i = 0; i < 4; i++) {
+ verify(testHelper).onMessage(notesDelivered.get(i));
+ assertEquals(messages[i], notesDelivered.get(i).getMessage());
+ }
+
+ verify(testHelper, never()).onRpcError(any(Throwable.class));
+ }
+
+ /**
+ * Example for testing bi-directional call.
+ */
+ @Test
+ public void routeChat_errorResponse() throws Exception {
+ final List notesDelivered = new ArrayList();
+ final StatusRuntimeException fakeError = new StatusRuntimeException(Status.PERMISSION_DENIED);
+
+ // implement the fake service
+ RouteGuideImplBase routeChatImpl =
+ new RouteGuideImplBase() {
+ @Override
+ public StreamObserver routeChat(
+ final StreamObserver responseObserver) {
+ StreamObserver requestObserver = new StreamObserver() {
+ @Override
+ public void onNext(RouteNote value) {
+ notesDelivered.add(value);
+ responseObserver.onError(fakeError);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onCompleted();
+ }
+ };
+
+ return requestObserver;
+ }
+ };
+ serviceRegistry.addService(routeChatImpl);
+
+ client.routeChat().await(1, TimeUnit.SECONDS);
+
+ assertEquals("First message", notesDelivered.get(0).getMessage());
+ verify(testHelper, never()).onMessage(any(Message.class));
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class);
+ verify(testHelper).onRpcError(errorCaptor.capture());
+ assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
+ }
+}
diff --git a/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideServerTest.java b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideServerTest.java
new file mode 100644
index 0000000000..3197776505
--- /dev/null
+++ b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideServerTest.java
@@ -0,0 +1,282 @@
+/*
+ * 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.examples.routeguide;
+
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import io.grpc.ManagedChannel;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for {@link RouteGuideServer}.
+ * For demonstrating how to write gRPC unit test only.
+ * Not intended to provide a high code coverage or to test every major usecase.
+ *
+ * For basic unit test examples see {@link io.grpc.examples.helloworld.HelloWorldClientTest} and
+ * {@link io.grpc.examples.helloworld.HelloWorldServerTest}.
+ */
+@RunWith(JUnit4.class)
+public class RouteGuideServerTest {
+ private RouteGuideServer server;
+ private ManagedChannel inProcessChannel;
+ private Collection features;
+
+ @Before
+ public void setUp() throws Exception {
+ String uniqueServerName = "in-process server for " + getClass();
+ features = new ArrayList();
+ // use directExecutor for both InProcessServerBuilder and InProcessChannelBuilder can reduce the
+ // usage timeouts and latches in test. But we still add timeout and latches where they would be
+ // needed if no directExecutor were used, just for demo purpose.
+ server = new RouteGuideServer(
+ InProcessServerBuilder.forName(uniqueServerName).directExecutor(), 0, features);
+ server.start();
+ inProcessChannel = InProcessChannelBuilder.forName(uniqueServerName).directExecutor().build();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ inProcessChannel.shutdownNow();
+ server.stop();
+ }
+
+ @Test
+ public void getFeature() {
+ Point point = Point.newBuilder().setLongitude(1).setLatitude(1).build();
+ Feature unnamedFeature = Feature.newBuilder()
+ .setName("").setLocation(point).build();
+ RouteGuideGrpc.RouteGuideBlockingStub stub = RouteGuideGrpc.newBlockingStub(inProcessChannel);
+
+ // feature not found in the server
+ Feature feature = stub.getFeature(point);
+
+ assertEquals(unnamedFeature, feature);
+
+ // feature found in the server
+ Feature namedFeature = Feature.newBuilder()
+ .setName("name").setLocation(point).build();
+ features.add(namedFeature);
+
+ feature = stub.getFeature(point);
+
+ assertEquals(namedFeature, feature);
+ }
+
+ @Test
+ public void listFeatures() throws Exception {
+ // setup
+ Rectangle rect = Rectangle.newBuilder()
+ .setLo(Point.newBuilder().setLongitude(0).setLatitude(0).build())
+ .setHi(Point.newBuilder().setLongitude(10).setLatitude(10).build())
+ .build();
+ Feature f1 = Feature.newBuilder()
+ .setLocation(Point.newBuilder().setLongitude(-1).setLatitude(-1).build())
+ .setName("f1")
+ .build(); // not inside rect
+ Feature f2 = Feature.newBuilder()
+ .setLocation(Point.newBuilder().setLongitude(2).setLatitude(2).build())
+ .setName("f2")
+ .build();
+ Feature f3 = Feature.newBuilder()
+ .setLocation(Point.newBuilder().setLongitude(3).setLatitude(3).build())
+ .setName("f3")
+ .build();
+ Feature f4 = Feature.newBuilder()
+ .setLocation(Point.newBuilder().setLongitude(4).setLatitude(4).build())
+ .build(); // unamed
+ features.add(f1);
+ features.add(f2);
+ features.add(f3);
+ features.add(f4);
+ final Collection result = new HashSet();
+ final CountDownLatch latch = new CountDownLatch(1);
+ StreamObserver responseObserver =
+ new StreamObserver() {
+ @Override
+ public void onNext(Feature value) {
+ result.add(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ fail();
+ }
+
+ @Override
+ public void onCompleted() {
+ latch.countDown();
+ }
+ };
+ RouteGuideGrpc.RouteGuideStub stub = RouteGuideGrpc.newStub(inProcessChannel);
+
+ // run
+ stub.listFeatures(rect, responseObserver);
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+
+ // verify
+ assertEquals(new HashSet(Arrays.asList(f2, f3)), result);
+ }
+
+ @Test
+ public void recordRoute() {
+ Point p1 = Point.newBuilder().setLongitude(1000).setLatitude(1000).build();
+ Point p2 = Point.newBuilder().setLongitude(2000).setLatitude(2000).build();
+ Point p3 = Point.newBuilder().setLongitude(3000).setLatitude(3000).build();
+ Point p4 = Point.newBuilder().setLongitude(4000).setLatitude(4000).build();
+ Feature f1 = Feature.newBuilder().setLocation(p1).build(); // unamed
+ Feature f2 = Feature.newBuilder().setLocation(p2).setName("f2").build();
+ Feature f3 = Feature.newBuilder().setLocation(p3).setName("f3").build();
+ Feature f4 = Feature.newBuilder().setLocation(p4).build(); // unamed
+ features.add(f1);
+ features.add(f2);
+ features.add(f3);
+ features.add(f4);
+
+ @SuppressWarnings("unchecked")
+ StreamObserver responseObserver =
+ (StreamObserver) mock(StreamObserver.class);
+ RouteGuideGrpc.RouteGuideStub stub = RouteGuideGrpc.newStub(inProcessChannel);
+ ArgumentCaptor routeSummaryCaptor = ArgumentCaptor.forClass(RouteSummary.class);
+
+ StreamObserver requestObserver = stub.recordRoute(responseObserver);
+
+ requestObserver.onNext(p1);
+ requestObserver.onNext(p2);
+ requestObserver.onNext(p3);
+ requestObserver.onNext(p4);
+
+ verify(responseObserver, never()).onNext(any(RouteSummary.class));
+
+ requestObserver.onCompleted();
+
+ // allow some ms to let client receive the response. Similar usage later on.
+ verify(responseObserver, timeout(100)).onNext(routeSummaryCaptor.capture());
+ RouteSummary summary = routeSummaryCaptor.getValue();
+ assertEquals(45, summary.getDistance()); // 45 is the hard coded distance from p1 to p4.
+ assertEquals(2, summary.getFeatureCount());
+ verify(responseObserver, timeout(100)).onCompleted();
+ verify(responseObserver, never()).onError(any(Throwable.class));
+ }
+
+ @Test
+ public void routeChat() {
+ Point p1 = Point.newBuilder().setLongitude(1).setLatitude(1).build();
+ Point p2 = Point.newBuilder().setLongitude(2).setLatitude(2).build();
+ RouteNote n1 = RouteNote.newBuilder().setLocation(p1).setMessage("m1").build();
+ RouteNote n2 = RouteNote.newBuilder().setLocation(p2).setMessage("m2").build();
+ RouteNote n3 = RouteNote.newBuilder().setLocation(p1).setMessage("m3").build();
+ RouteNote n4 = RouteNote.newBuilder().setLocation(p2).setMessage("m4").build();
+ RouteNote n5 = RouteNote.newBuilder().setLocation(p1).setMessage("m5").build();
+ RouteNote n6 = RouteNote.newBuilder().setLocation(p1).setMessage("m6").build();
+ int timesOnNext = 0;
+
+ @SuppressWarnings("unchecked")
+ StreamObserver responseObserver =
+ (StreamObserver) mock(StreamObserver.class);
+ RouteGuideGrpc.RouteGuideStub stub = RouteGuideGrpc.newStub(inProcessChannel);
+
+ StreamObserver requestObserver = stub.routeChat(responseObserver);
+ verify(responseObserver, never()).onNext(any(RouteNote.class));
+
+ requestObserver.onNext(n1);
+ verify(responseObserver, never()).onNext(any(RouteNote.class));
+
+ requestObserver.onNext(n2);
+ verify(responseObserver, never()).onNext(any(RouteNote.class));
+
+ requestObserver.onNext(n3);
+ ArgumentCaptor routeNoteCaptor = ArgumentCaptor.forClass(RouteNote.class);
+ verify(responseObserver, timeout(100).times(++timesOnNext)).onNext(routeNoteCaptor.capture());
+ RouteNote result = routeNoteCaptor.getValue();
+ assertEquals(p1, result.getLocation());
+ assertEquals("m1", result.getMessage());
+
+ requestObserver.onNext(n4);
+ routeNoteCaptor = ArgumentCaptor.forClass(RouteNote.class);
+ verify(responseObserver, timeout(100).times(++timesOnNext)).onNext(routeNoteCaptor.capture());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 1);
+ assertEquals(p2, result.getLocation());
+ assertEquals("m2", result.getMessage());
+
+ requestObserver.onNext(n5);
+ routeNoteCaptor = ArgumentCaptor.forClass(RouteNote.class);
+ timesOnNext += 2;
+ verify(responseObserver, timeout(100).times(timesOnNext)).onNext(routeNoteCaptor.capture());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 2);
+ assertEquals(p1, result.getLocation());
+ assertEquals("m1", result.getMessage());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 1);
+ assertEquals(p1, result.getLocation());
+ assertEquals("m3", result.getMessage());
+
+ requestObserver.onNext(n6);
+ routeNoteCaptor = ArgumentCaptor.forClass(RouteNote.class);
+ timesOnNext += 3;
+ verify(responseObserver, timeout(100).times(timesOnNext)).onNext(routeNoteCaptor.capture());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 3);
+ assertEquals(p1, result.getLocation());
+ assertEquals("m1", result.getMessage());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 2);
+ assertEquals(p1, result.getLocation());
+ assertEquals("m3", result.getMessage());
+ result = routeNoteCaptor.getAllValues().get(timesOnNext - 1);
+ assertEquals(p1, result.getLocation());
+ assertEquals("m5", result.getMessage());
+
+ requestObserver.onCompleted();
+ verify(responseObserver, timeout(100)).onCompleted();
+ verify(responseObserver, never()).onError(any(Throwable.class));
+ }
+}