mirror of https://github.com/grpc/grpc-java.git
examples: unit test examples for users
Demonstrate unit testing gRPC clients and servers with `InProcessTransport`.
This commit is contained in:
parent
7311572236
commit
7306df4266
|
@ -44,3 +44,23 @@ $ mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldServer
|
|||
$ # In another terminal run the client
|
||||
$ mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldClient
|
||||
```
|
||||
|
||||
Unit test examples
|
||||
==============================================
|
||||
|
||||
Examples for unit testing gRPC clients and servers are located in [examples/src/test](src/test).
|
||||
|
||||
In general, we DO NOT allow overriding the client stub.
|
||||
We encourage users to leverage `InProcessTransport` as demonstrated in the examples to
|
||||
write unit tests. `InProcessTransport` is light-weight and runs the server
|
||||
and client in the same process without any socket/TCP connection.
|
||||
|
||||
For testing a gRPC client, create the client with a real stub
|
||||
using an
|
||||
[InProcessChannel](../core/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java),
|
||||
and test it against an
|
||||
[InProcessServer](../core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java)
|
||||
with a mock/fake service implementation.
|
||||
|
||||
For testing a gRPC server, create the server as an InProcessServer,
|
||||
and test it against a real client stub with an InProcessChannel.
|
||||
|
|
|
@ -28,6 +28,9 @@ dependencies {
|
|||
compile "io.grpc:grpc-netty:${grpcVersion}"
|
||||
compile "io.grpc:grpc-protobuf:${grpcVersion}"
|
||||
compile "io.grpc:grpc-stub:${grpcVersion}"
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
}
|
||||
|
||||
protobuf {
|
||||
|
|
|
@ -28,6 +28,18 @@
|
|||
<artifactId>grpc-stub</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<extensions>
|
||||
|
|
|
@ -61,11 +61,11 @@ import java.util.logging.Logger;
|
|||
public class HelloJsonServer {
|
||||
private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());
|
||||
|
||||
/* The port on which the server should run */
|
||||
private int port = 50051;
|
||||
private Server server;
|
||||
|
||||
private void start() throws IOException {
|
||||
/* The port on which the server should run */
|
||||
int port = 50051;
|
||||
server = ServerBuilder.forPort(port)
|
||||
.addService(new GreeterImpl())
|
||||
.build()
|
||||
|
|
|
@ -86,11 +86,10 @@ public class DetailErrorSample {
|
|||
new DetailErrorSample().run();
|
||||
}
|
||||
|
||||
private Server server;
|
||||
private ManagedChannel channel;
|
||||
|
||||
void run() throws Exception {
|
||||
server = ServerBuilder.forPort(0).addService(new GreeterGrpc.GreeterImplBase() {
|
||||
Server server = ServerBuilder.forPort(0).addService(new GreeterGrpc.GreeterImplBase() {
|
||||
@Override
|
||||
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
|
||||
Metadata trailers = new Metadata();
|
||||
|
|
|
@ -67,11 +67,11 @@ public class ErrorHandlingClient {
|
|||
new ErrorHandlingClient().run();
|
||||
}
|
||||
|
||||
private Server server;
|
||||
private ManagedChannel channel;
|
||||
|
||||
void run() throws Exception {
|
||||
server = ServerBuilder.forPort(0).addService(new GreeterGrpc.GreeterImplBase() {
|
||||
// Port 0 means that the operating system will pick an available port to use.
|
||||
Server server = ServerBuilder.forPort(0).addService(new GreeterGrpc.GreeterImplBase() {
|
||||
@Override
|
||||
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
|
||||
responseObserver.onError(Status.INTERNAL
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
package io.grpc.examples.header;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
|
@ -49,7 +51,8 @@ public class HeaderClientInterceptor implements ClientInterceptor {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(HeaderClientInterceptor.class.getName());
|
||||
|
||||
private static Metadata.Key<String> customHeadKey =
|
||||
@VisibleForTesting
|
||||
static final Metadata.Key<String> CUSTOM_HEADER_KEY =
|
||||
Metadata.Key.of("custom_client_header_key", Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
@Override
|
||||
|
@ -60,13 +63,13 @@ public class HeaderClientInterceptor implements ClientInterceptor {
|
|||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
/* put custom header */
|
||||
headers.put(customHeadKey, "customRequestValue");
|
||||
headers.put(CUSTOM_HEADER_KEY, "customRequestValue");
|
||||
super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
|
||||
@Override
|
||||
public void onHeaders(Metadata headers) {
|
||||
/**
|
||||
* if you don't need receive header from server,
|
||||
* you can use {@link io.grpc.stub.MetadataUtils attachHeaders}
|
||||
* you can use {@link io.grpc.stub.MetadataUtils#attachHeaders}
|
||||
* directly to send header
|
||||
*/
|
||||
logger.info("header received from server:" + headers);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
package io.grpc.examples.header;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
|
@ -46,7 +48,8 @@ public class HeaderServerInterceptor implements ServerInterceptor {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(HeaderServerInterceptor.class.getName());
|
||||
|
||||
private static Metadata.Key<String> customHeadKey =
|
||||
@VisibleForTesting
|
||||
static final Metadata.Key<String> CUSTOM_HEADER_KEY =
|
||||
Metadata.Key.of("custom_server_header_key", Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
|
||||
|
@ -59,7 +62,7 @@ public class HeaderServerInterceptor implements ServerInterceptor {
|
|||
return next.startCall(new SimpleForwardingServerCall<ReqT, RespT>(call) {
|
||||
@Override
|
||||
public void sendHeaders(Metadata responseHeaders) {
|
||||
responseHeaders.put(customHeadKey, "customRespondValue");
|
||||
responseHeaders.put(CUSTOM_HEADER_KEY, "customRespondValue");
|
||||
super.sendHeaders(responseHeaders);
|
||||
}
|
||||
}, requestHeaders);
|
||||
|
|
|
@ -50,11 +50,15 @@ public class HelloWorldClient {
|
|||
|
||||
/** Construct client connecting to HelloWorld server at {@code host:port}. */
|
||||
public HelloWorldClient(String host, int port) {
|
||||
channel = ManagedChannelBuilder.forAddress(host, port)
|
||||
this(ManagedChannelBuilder.forAddress(host, port)
|
||||
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
|
||||
// needing certificates.
|
||||
.usePlaintext(true)
|
||||
.build();
|
||||
.usePlaintext(true));
|
||||
}
|
||||
|
||||
/** Construct client for accessing RouteGuide server using the existing channel. */
|
||||
HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
|
||||
channel = channelBuilder.build();
|
||||
blockingStub = GreeterGrpc.newBlockingStub(channel);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,11 +44,11 @@ import java.util.logging.Logger;
|
|||
public class HelloWorldServer {
|
||||
private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());
|
||||
|
||||
/* The port on which the server should run */
|
||||
private int port = 50051;
|
||||
private Server server;
|
||||
|
||||
private void start() throws IOException {
|
||||
/* The port on which the server should run */
|
||||
int port = 50051;
|
||||
server = ServerBuilder.forPort(port)
|
||||
.addService(new GreeterImpl())
|
||||
.build()
|
||||
|
@ -89,7 +89,7 @@ public class HelloWorldServer {
|
|||
server.blockUntilShutdown();
|
||||
}
|
||||
|
||||
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
|
||||
static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
|
||||
|
||||
@Override
|
||||
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
|
||||
package io.grpc.examples.routeguide;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Status;
|
||||
|
@ -58,6 +61,9 @@ public class RouteGuideClient {
|
|||
private final RouteGuideBlockingStub blockingStub;
|
||||
private final RouteGuideStub asyncStub;
|
||||
|
||||
private Random random = new Random();
|
||||
private TestHelper testHelper;
|
||||
|
||||
/** Construct client for accessing RouteGuide server at {@code host:port}. */
|
||||
public RouteGuideClient(String host, int port) {
|
||||
this(ManagedChannelBuilder.forAddress(host, port).usePlaintext(true));
|
||||
|
@ -85,8 +91,14 @@ public class RouteGuideClient {
|
|||
Feature feature;
|
||||
try {
|
||||
feature = blockingStub.getFeature(request);
|
||||
if (testHelper != null) {
|
||||
testHelper.onMessage(feature);
|
||||
}
|
||||
} catch (StatusRuntimeException e) {
|
||||
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
|
||||
warning("RPC failed: {0}", e.getStatus());
|
||||
if (testHelper != null) {
|
||||
testHelper.onRpcError(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (RouteGuideUtil.exists(feature)) {
|
||||
|
@ -116,17 +128,19 @@ public class RouteGuideClient {
|
|||
Iterator<Feature> features;
|
||||
try {
|
||||
features = blockingStub.listFeatures(request);
|
||||
for (int i = 1; features.hasNext(); i++) {
|
||||
Feature feature = features.next();
|
||||
info("Result #" + i + ": {0}", feature);
|
||||
if (testHelper != null) {
|
||||
testHelper.onMessage(feature);
|
||||
}
|
||||
}
|
||||
} catch (StatusRuntimeException e) {
|
||||
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
|
||||
return;
|
||||
warning("RPC failed: {0}", e.getStatus());
|
||||
if (testHelper != null) {
|
||||
testHelper.onRpcError(e);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder responseLog = new StringBuilder("Result: ");
|
||||
while (features.hasNext()) {
|
||||
Feature feature = features.next();
|
||||
responseLog.append(feature);
|
||||
}
|
||||
info(responseLog.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,12 +157,17 @@ public class RouteGuideClient {
|
|||
info("Finished trip with {0} points. Passed {1} features. "
|
||||
+ "Travelled {2} meters. It took {3} seconds.", summary.getPointCount(),
|
||||
summary.getFeatureCount(), summary.getDistance(), summary.getElapsedTime());
|
||||
if (testHelper != null) {
|
||||
testHelper.onMessage(summary);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
Status status = Status.fromThrowable(t);
|
||||
logger.log(Level.WARNING, "RecordRoute Failed: {0}", status);
|
||||
warning("RecordRoute Failed: {0}", Status.fromThrowable(t));
|
||||
if (testHelper != null) {
|
||||
testHelper.onRpcError(t);
|
||||
}
|
||||
finishLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -162,15 +181,14 @@ public class RouteGuideClient {
|
|||
StreamObserver<Point> requestObserver = asyncStub.recordRoute(responseObserver);
|
||||
try {
|
||||
// Send numPoints points randomly selected from the features list.
|
||||
Random rand = new Random();
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
int index = rand.nextInt(features.size());
|
||||
int index = random.nextInt(features.size());
|
||||
Point point = features.get(index).getLocation();
|
||||
info("Visiting point {0}, {1}", RouteGuideUtil.getLatitude(point),
|
||||
RouteGuideUtil.getLongitude(point));
|
||||
requestObserver.onNext(point);
|
||||
// Sleep for a bit before sending the next one.
|
||||
Thread.sleep(rand.nextInt(1000) + 500);
|
||||
Thread.sleep(random.nextInt(1000) + 500);
|
||||
if (finishLatch.getCount() == 0) {
|
||||
// RPC completed or errored before we finished sending.
|
||||
// Sending further requests won't error, but they will just be thrown away.
|
||||
|
@ -186,15 +204,17 @@ public class RouteGuideClient {
|
|||
requestObserver.onCompleted();
|
||||
|
||||
// Receiving happens asynchronously
|
||||
finishLatch.await(1, TimeUnit.MINUTES);
|
||||
if (!finishLatch.await(1, TimeUnit.MINUTES)) {
|
||||
warning("recordRoute can not finish within 1 minutes");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bi-directional example, which can only be asynchronous. Send some chat messages, and print any
|
||||
* chat messages that are sent from the server.
|
||||
*/
|
||||
public void routeChat() throws InterruptedException {
|
||||
info("*** RoutChat");
|
||||
public CountDownLatch routeChat() {
|
||||
info("*** RouteChat");
|
||||
final CountDownLatch finishLatch = new CountDownLatch(1);
|
||||
StreamObserver<RouteNote> requestObserver =
|
||||
asyncStub.routeChat(new StreamObserver<RouteNote>() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>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 <ReqT, RespT> Listener<ReqT> interceptCall(
|
||||
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> 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<Metadata> 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.<ServerCall<HelloRequest, HelloReply>>any(),
|
||||
metadataCaptor.capture(),
|
||||
Matchers.<ServerCallHandler<HelloRequest, HelloReply>>any());
|
||||
assertEquals(
|
||||
"customRequestValue",
|
||||
metadataCaptor.getValue().get(HeaderClientInterceptor.CUSTOM_HEADER_KEY));
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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<HelloReply> 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 <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
|
||||
return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
spyListener = responseListener = spy(responseListener);
|
||||
super.start(responseListener, headers);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
SpyingClientInterceptor clientInterceptor = new SpyingClientInterceptor();
|
||||
GreeterBlockingStub blockingStub =
|
||||
GreeterGrpc.newBlockingStub(inProcessChannel).withInterceptors(clientInterceptor);
|
||||
ArgumentCaptor<Metadata> 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));
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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<HelloRequest> requestCaptor = ArgumentCaptor.forClass(HelloRequest.class);
|
||||
String testName = "test name";
|
||||
|
||||
client.greet(testName);
|
||||
|
||||
verify(serviceImpl)
|
||||
.sayHello(requestCaptor.capture(), Matchers.<StreamObserver<HelloReply>>any());
|
||||
assertEquals(testName, requestCaptor.getValue().getName());
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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());
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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<Point> pointDelivered = new AtomicReference<Point>();
|
||||
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<Feature> 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<Point> pointDelivered = new AtomicReference<Point>();
|
||||
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.DATA_LOSS);
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase getFeatureImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public void getFeature(Point point, StreamObserver<Feature> responseObserver) {
|
||||
pointDelivered.set(point);
|
||||
responseObserver.onError(fakeError);
|
||||
}
|
||||
};
|
||||
serviceRegistry.addService(getFeatureImpl);
|
||||
|
||||
client.getFeature(-1, -1);
|
||||
|
||||
assertEquals(requestPoint, pointDelivered.get());
|
||||
ArgumentCaptor<Throwable> 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<Rectangle> rectangleDelivered = new AtomicReference<Rectangle>();
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase listFeaturesImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public void listFeatures(Rectangle rectangle, StreamObserver<Feature> 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<Rectangle> rectangleDelivered = new AtomicReference<Rectangle>();
|
||||
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase listFeaturesImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public void listFeatures(Rectangle rectangle, StreamObserver<Feature> 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<Throwable> 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<Feature> features = Arrays.asList(
|
||||
requestFeature1, requestFeature2, requestFeature3);
|
||||
final List<Point> pointsDelivered = new ArrayList<Point>();
|
||||
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<Point> recordRoute(
|
||||
final StreamObserver<RouteSummary> responseObserver) {
|
||||
StreamObserver<Point> requestObserver = new StreamObserver<Point>() {
|
||||
@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<Feature> features = Arrays.asList(requestFeature1);
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase recordRouteImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public StreamObserver<Point> recordRoute(StreamObserver<RouteSummary> 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<Point>() {
|
||||
@Override
|
||||
public void onNext(Point value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
serviceRegistry.addService(recordRouteImpl);
|
||||
|
||||
client.recordRoute(features, 4);
|
||||
|
||||
ArgumentCaptor<Throwable> 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<Feature> features = Arrays.asList(requestFeature1);
|
||||
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase recordRouteImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public StreamObserver<Point> recordRoute(StreamObserver<RouteSummary> responseObserver) {
|
||||
// send an error immediately
|
||||
responseObserver.onError(fakeError);
|
||||
|
||||
StreamObserver<Point> requestObserver = new StreamObserver<Point>() {
|
||||
@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<Throwable> 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<String> messagesDelivered = new ArrayList<String>();
|
||||
final List<Point> locationsDelivered = new ArrayList<Point>();
|
||||
final AtomicReference<StreamObserver<RouteNote>> responseObserverRef =
|
||||
new AtomicReference<StreamObserver<RouteNote>>();
|
||||
final CountDownLatch allRequestsDelivered = new CountDownLatch(1);
|
||||
// implement the fake service
|
||||
RouteGuideImplBase routeChatImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public StreamObserver<RouteNote> routeChat(StreamObserver<RouteNote> responseObserver) {
|
||||
responseObserverRef.set(responseObserver);
|
||||
|
||||
StreamObserver<RouteNote> requestObserver = new StreamObserver<RouteNote>() {
|
||||
@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<RouteNote> notesDelivered = new ArrayList<RouteNote>();
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase routeChatImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public StreamObserver<RouteNote> routeChat(
|
||||
final StreamObserver<RouteNote> responseObserver) {
|
||||
StreamObserver<RouteNote> requestObserver = new StreamObserver<RouteNote>() {
|
||||
@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<RouteNote> notesDelivered = new ArrayList<RouteNote>();
|
||||
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.PERMISSION_DENIED);
|
||||
|
||||
// implement the fake service
|
||||
RouteGuideImplBase routeChatImpl =
|
||||
new RouteGuideImplBase() {
|
||||
@Override
|
||||
public StreamObserver<RouteNote> routeChat(
|
||||
final StreamObserver<RouteNote> responseObserver) {
|
||||
StreamObserver<RouteNote> requestObserver = new StreamObserver<RouteNote>() {
|
||||
@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<Throwable> errorCaptor = ArgumentCaptor.forClass(Throwable.class);
|
||||
verify(testHelper).onRpcError(errorCaptor.capture());
|
||||
assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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<Feature> features;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
String uniqueServerName = "in-process server for " + getClass();
|
||||
features = new ArrayList<Feature>();
|
||||
// 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<Feature> result = new HashSet<Feature>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
StreamObserver<Feature> responseObserver =
|
||||
new StreamObserver<Feature>() {
|
||||
@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<Feature>(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<RouteSummary> responseObserver =
|
||||
(StreamObserver<RouteSummary>) mock(StreamObserver.class);
|
||||
RouteGuideGrpc.RouteGuideStub stub = RouteGuideGrpc.newStub(inProcessChannel);
|
||||
ArgumentCaptor<RouteSummary> routeSummaryCaptor = ArgumentCaptor.forClass(RouteSummary.class);
|
||||
|
||||
StreamObserver<Point> 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<RouteNote> responseObserver =
|
||||
(StreamObserver<RouteNote>) mock(StreamObserver.class);
|
||||
RouteGuideGrpc.RouteGuideStub stub = RouteGuideGrpc.newStub(inProcessChannel);
|
||||
|
||||
StreamObserver<RouteNote> 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<RouteNote> 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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue