/*
* 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.android.integrationtest;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import android.os.AsyncTask;
import android.util.Log;
import com.google.protobuf.nano.EmptyProtos;
import com.google.protobuf.nano.MessageNano;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ClientInterceptors;
import io.grpc.ClientInterceptors.CheckedForwardingClientCall;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.StatusRuntimeException;
import io.grpc.android.integrationtest.nano.Messages;
import io.grpc.android.integrationtest.nano.Messages.Payload;
import io.grpc.android.integrationtest.nano.Messages.ResponseParameters;
import io.grpc.android.integrationtest.nano.Messages.SimpleRequest;
import io.grpc.android.integrationtest.nano.Messages.SimpleResponse;
import io.grpc.android.integrationtest.nano.Messages.StreamingInputCallRequest;
import io.grpc.android.integrationtest.nano.Messages.StreamingInputCallResponse;
import io.grpc.android.integrationtest.nano.Messages.StreamingOutputCallRequest;
import io.grpc.android.integrationtest.nano.Messages.StreamingOutputCallResponse;
import io.grpc.android.integrationtest.nano.TestServiceGrpc;
import io.grpc.android.integrationtest.nano.UnimplementedServiceGrpc;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.StreamRecorder;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Implementation of the integration tests, as an AsyncTask.
*/
final class InteropTester extends AsyncTask<Void, Void, String> {
static final String SUCCESS_MESSAGE = "Succeed!!!";
static final String LOG_TAG = "GrpcTest";
private ManagedChannel channel;
private TestServiceGrpc.TestServiceBlockingStub blockingStub;
private TestServiceGrpc.TestServiceStub asyncStub;
private String testCase;
private TestListener listener;
private static final int TIMEOUT_MILLIS = 5000;
private static final class ResponseObserver
implements StreamObserver<Messages.StreamingOutputCallResponse> {
public LinkedBlockingQueue<Object> responses = new LinkedBlockingQueue<Object>();
final Object magicTailResponse = new Object();
@Override
public void onNext(Messages.StreamingOutputCallResponse value) {
responses.add(value);
}
@Override
public void onError(Throwable t) {
Log.e(LOG_TAG, "Encounter an error", t);
responses.add(t);
}
@Override
public void onCompleted() {
responses.add(magicTailResponse);
}
}
public InteropTester(String testCase,
ManagedChannel channel,
TestListener listener,
boolean useGet) {
this.testCase = testCase;
this.listener = listener;
this.channel = channel;
Channel channelToUse = channel;
if (useGet) {
channelToUse = ClientInterceptors.intercept(channel, new SafeMethodChannelInterceptor());
}
blockingStub = TestServiceGrpc.newBlockingStub(channelToUse);
asyncStub = TestServiceGrpc.newStub(channelToUse);
}
@Override
protected void onPreExecute() {
listener.onPreTest();
}
@Override
protected String doInBackground(Void... nothing) {
try {
runTest(testCase);
return SUCCESS_MESSAGE;
} catch (Throwable t) {
// Print the stack trace to logcat.
t.printStackTrace();
// Then print to the error message.
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return "Failed... : " + t.getMessage() + "\n" + sw.toString();
} finally {
shutdown();
}
}
@Override
protected void onPostExecute(String result) {
listener.onPostTest(result);
}
public void shutdown() {
channel.shutdown();
}
public void runTest(String testCase) throws Exception {
Log.i(LOG_TAG, "Running test " + testCase);
if ("all".equals(testCase)) {
runTest("empty_unary");
runTest("large_unary");
runTest("client_streaming");
runTest("server_streaming");
runTest("ping_pong");
runTest("empty_stream");
runTest("cancel_after_begin");
runTest("cancel_after_first_response");
runTest("full_duplex_call_should_succeed");
runTest("half_duplex_call_should_succeed");
runTest("server_streaming_should_be_flow_controlled");
runTest("very_large_request");
runTest("very_large_response");
runTest("deadline_not_exceeded");
runTest("deadline_exceeded");
runTest("deadline_exceeded_server_streaming");
runTest("unimplemented_method");
runTest("timeout_on_sleeping_server");
// This has to be the last one, because it will shut down the channel.
runTest("graceful_shutdown");
} else if ("empty_unary".equals(testCase)) {
emptyUnary();
} else if ("large_unary".equals(testCase)) {
largeUnary();
} else if ("client_streaming".equals(testCase)) {
clientStreaming();
} else if ("server_streaming".equals(testCase)) {
serverStreaming();
} else if ("ping_pong".equals(testCase)) {
pingPong();
} else if ("empty_stream".equals(testCase)) {
emptyStream();
} else if ("cancel_after_begin".equals(testCase)) {
cancelAfterBegin();
} else if ("cancel_after_first_response".equals(testCase)) {
cancelAfterFirstResponse();
} else if ("full_duplex_call_should_succeed".equals(testCase)) {
fullDuplexCallShouldSucceed();
} else if ("half_duplex_call_should_succeed".equals(testCase)) {
halfDuplexCallShouldSucceed();
} else if ("server_streaming_should_be_flow_controlled".equals(testCase)) {
serverStreamingShouldBeFlowControlled();
} else if ("very_large_request".equals(testCase)) {
veryLargeRequest();
} else if ("very_large_response".equals(testCase)) {
veryLargeResponse();
} else if ("deadline_not_exceeded".equals(testCase)) {
deadlineNotExceeded();
} else if ("deadline_exceeded".equals(testCase)) {
deadlineExceeded();
} else if ("deadline_exceeded_server_streaming".equals(testCase)) {
deadlineExceededServerStreaming();
} else if ("unimplemented_method".equals(testCase)) {
unimplementedMethod();
} else if ("timeout_on_sleeping_server".equals(testCase)) {
timeoutOnSleepingServer();
} else if ("graceful_shutdown".equals(testCase)) {
gracefulShutdown();
} else {
throw new IllegalArgumentException("Unimplemented/Unknown test case: " + testCase);
}
}
public void emptyUnary() {
assertMessageEquals(new EmptyProtos.Empty(), blockingStub.emptyCall(new EmptyProtos.Empty()));
}
public void largeUnary() {
if (shouldSkip()) {
return;
}
final Messages.SimpleRequest request = new Messages.SimpleRequest();
request.responseSize = 314159;
request.responseType = Messages.COMPRESSABLE;
request.payload = new Payload();
request.payload.body = new byte[271828];
final Messages.SimpleResponse goldenResponse = new Messages.SimpleResponse();
goldenResponse.payload = new Payload();
goldenResponse.payload.body = new byte[314159];
Messages.SimpleResponse response = blockingStub.unaryCall(request);
assertMessageEquals(goldenResponse, response);
}
public void serverStreaming() throws Exception {
final Messages.StreamingOutputCallRequest request = new Messages.StreamingOutputCallRequest();
request.responseType = Messages.COMPRESSABLE;
request.responseParameters = new Messages.ResponseParameters[4];
for (int i = 0; i < 4; i++) {
request.responseParameters[i] = new Messages.ResponseParameters();
}
request.responseParameters[0].size = 31415;
request.responseParameters[1].size = 9;
request.responseParameters[2].size = 2653;
request.responseParameters[3].size = 58979;
final Messages.StreamingOutputCallResponse[] goldenResponses =
new Messages.StreamingOutputCallResponse[4];
for (int i = 0; i < 4; i++) {
goldenResponses[i] = new Messages.StreamingOutputCallResponse();
goldenResponses[i].payload = new Payload();
goldenResponses[i].payload.type = Messages.COMPRESSABLE;
}
goldenResponses[0].payload.body = new byte[31415];
goldenResponses[1].payload.body = new byte[9];
goldenResponses[2].payload.body = new byte[2653];
goldenResponses[3].payload.body = new byte[58979];
StreamRecorder<Messages.StreamingOutputCallResponse> recorder = StreamRecorder.create();
asyncStub.streamingOutputCall(request, recorder);
assertTrue(recorder.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertSuccess(recorder);
assertMessageEquals(Arrays.asList(goldenResponses), recorder.getValues());
}
public void clientStreaming() throws Exception {
final Messages.StreamingInputCallRequest[] requests = new Messages.StreamingInputCallRequest[4];
for (int i = 0; i < 4; i++) {
requests[i] = new Messages.StreamingInputCallRequest();
requests[i].payload = new Payload();
}
requests[0].payload.body = new byte[27182];
requests[1].payload.body = new byte[8];
requests[2].payload.body = new byte[1828];
requests[3].payload.body = new byte[45904];
final Messages.StreamingInputCallResponse goldenResponse =
new Messages.StreamingInputCallResponse();
goldenResponse.aggregatedPayloadSize = 74922;
StreamRecorder<Messages.StreamingInputCallResponse> responseObserver = StreamRecorder.create();
StreamObserver<Messages.StreamingInputCallRequest> requestObserver =
asyncStub.streamingInputCall(responseObserver);
for (Messages.StreamingInputCallRequest request : requests) {
requestObserver.onNext(request);
}
requestObserver.onCompleted();
assertMessageEquals(goldenResponse, responseObserver.firstValue().get());
}
public void pingPong() throws Exception {
final Messages.StreamingOutputCallRequest[] requests =
new Messages.StreamingOutputCallRequest[4];
for (int i = 0; i < 4; i++) {
requests[i] = new Messages.StreamingOutputCallRequest();
requests[i].responseParameters = new Messages.ResponseParameters[1];
requests[i].responseParameters[0] = new Messages.ResponseParameters();
requests[i].payload = new Payload();
}
requests[0].responseParameters[0].size = 31415;
requests[0].payload.body = new byte[27182];
requests[1].responseParameters[0].size = 9;
requests[1].payload.body = new byte[8];
requests[2].responseParameters[0].size = 2653;
requests[2].payload.body = new byte[1828];
requests[3].responseParameters[0].size = 58979;
requests[3].payload.body = new byte[45904];
final Messages.StreamingOutputCallResponse[] goldenResponses =
new Messages.StreamingOutputCallResponse[4];
for (int i = 0; i < 4; i++) {
goldenResponses[i] = new Messages.StreamingOutputCallResponse();
goldenResponses[i].payload = new Payload();
goldenResponses[i].payload.type = Messages.COMPRESSABLE;
}
goldenResponses[0].payload.body = new byte[31415];
goldenResponses[1].payload.body = new byte[9];
goldenResponses[2].payload.body = new byte[2653];
goldenResponses[3].payload.body = new byte[58979];
ResponseObserver responseObserver = new ResponseObserver();
StreamObserver<Messages.StreamingOutputCallRequest> requestObserver
= asyncStub.fullDuplexCall(responseObserver);
for (int i = 0; i < requests.length; i++) {
requestObserver.onNext(requests[i]);
Object response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
if (!(response instanceof Messages.StreamingOutputCallResponse)) {
fail("Unexpected: " + response);
}
assertMessageEquals(goldenResponses[i], (Messages.StreamingOutputCallResponse) response);
assertTrue("More than 1 responses received for ping pong test.",
responseObserver.responses.isEmpty());
}
requestObserver.onCompleted();
assertEquals(responseObserver.magicTailResponse,
responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
}
public void emptyStream() throws Exception {
ResponseObserver responseObserver = new ResponseObserver();
StreamObserver<StreamingOutputCallRequest> requestObserver
= asyncStub.fullDuplexCall(responseObserver);
requestObserver.onCompleted();
assertEquals(responseObserver.magicTailResponse,
responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
}
public void cancelAfterBegin() throws Exception {
StreamRecorder<StreamingInputCallResponse> responseObserver = StreamRecorder.create();
StreamObserver<StreamingInputCallRequest> requestObserver =
asyncStub.streamingInputCall(responseObserver);
requestObserver.onError(new RuntimeException());
assertTrue(responseObserver.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertEquals(Arrays.<StreamingInputCallResponse>asList(), responseObserver.getValues());
assertCodeEquals(io.grpc.Status.CANCELLED,
io.grpc.Status.fromThrowable(responseObserver.getError()));
}
public void cancelAfterFirstResponse() throws Exception {
final StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseParameters = new Messages.ResponseParameters[1];
request.responseParameters[0] = new ResponseParameters();
request.responseParameters[0].size = 31415;
request.payload = new Payload();
request.payload.body = new byte[27182];
final StreamingOutputCallResponse goldenResponse = new StreamingOutputCallResponse();
goldenResponse.payload = new Payload();
goldenResponse.payload.type = Messages.COMPRESSABLE;
goldenResponse.payload.body = new byte[31415];
ResponseObserver responseObserver = new ResponseObserver();
StreamObserver<StreamingOutputCallRequest> requestObserver
= asyncStub.fullDuplexCall(responseObserver);
requestObserver.onNext(request);
Object response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
if (!(response instanceof Messages.StreamingOutputCallResponse)) {
fail("Unexpected: " + response);
}
assertMessageEquals(goldenResponse, (Messages.StreamingOutputCallResponse) response);
requestObserver.onError(new RuntimeException());
response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
if (!(response instanceof Throwable)) {
fail("Unexpected: " + response);
}
assertCodeEquals(io.grpc.Status.CANCELLED, io.grpc.Status.fromThrowable((Throwable) response));
}
public void fullDuplexCallShouldSucceed() throws Exception {
// Build the request.
Integer[] responseSizes = {50, 100, 150, 200};
final StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseParameters = new ResponseParameters[responseSizes.length];
request.responseType = Messages.COMPRESSABLE;
for (int i = 0; i < responseSizes.length; ++i) {
request.responseParameters[i] = new ResponseParameters();
request.responseParameters[i].size = responseSizes[i];
request.responseParameters[i].intervalUs = 0;
}
StreamRecorder<StreamingOutputCallResponse> recorder = StreamRecorder.create();
StreamObserver<StreamingOutputCallRequest> requestStream =
asyncStub.fullDuplexCall(recorder);
final int numRequests = 10;
for (int ix = numRequests; ix > 0; --ix) {
requestStream.onNext(request);
}
requestStream.onCompleted();
assertTrue(recorder.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertSuccess(recorder);
assertEquals(responseSizes.length * numRequests, recorder.getValues().size());
for (int ix = 0; ix < recorder.getValues().size(); ++ix) {
StreamingOutputCallResponse response = recorder.getValues().get(ix);
assertEquals(Messages.COMPRESSABLE, response.payload.type);
int length = response.payload.body.length;
int expectedSize = responseSizes[ix % responseSizes.length];
assertEquals("comparison failed at index " + ix, expectedSize, length);
}
}
public void halfDuplexCallShouldSucceed() throws Exception {
// Build the request.
Integer[] responseSizes = {50, 100, 150, 200};
final StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseParameters = new ResponseParameters[responseSizes.length];
request.responseType = Messages.COMPRESSABLE;
for (int i = 0; i < responseSizes.length; ++i) {
request.responseParameters[i] = new ResponseParameters();
request.responseParameters[i].size = responseSizes[i];
request.responseParameters[i].intervalUs = 0;
}
StreamRecorder<StreamingOutputCallResponse> recorder = StreamRecorder.create();
StreamObserver<StreamingOutputCallRequest> requestStream = asyncStub.halfDuplexCall(recorder);
final int numRequests = 10;
for (int ix = numRequests; ix > 0; --ix) {
requestStream.onNext(request);
}
requestStream.onCompleted();
assertTrue(recorder.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertSuccess(recorder);
assertEquals(responseSizes.length * numRequests, recorder.getValues().size());
for (int ix = 0; ix < recorder.getValues().size(); ++ix) {
StreamingOutputCallResponse response = recorder.getValues().get(ix);
assertEquals(Messages.COMPRESSABLE, response.payload.type);
int length = response.payload.body.length;
int expectedSize = responseSizes[ix % responseSizes.length];
assertEquals("comparison failed at index " + ix, expectedSize, length);
}
}
public void serverStreamingShouldBeFlowControlled() throws Exception {
final StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseType = Messages.COMPRESSABLE;
request.responseParameters = new ResponseParameters[2];
request.responseParameters[0] = new ResponseParameters();
request.responseParameters[0].size = 100000;
request.responseParameters[1] = new ResponseParameters();
request.responseParameters[1].size = 100001;
final StreamingOutputCallResponse[] goldenResponses = new StreamingOutputCallResponse[2];
goldenResponses[0] = new StreamingOutputCallResponse();
goldenResponses[0].payload = new Payload();
goldenResponses[0].payload.type = Messages.COMPRESSABLE;
goldenResponses[0].payload.body = new byte[100000];
goldenResponses[1] = new StreamingOutputCallResponse();
goldenResponses[1].payload = new Payload();
goldenResponses[1].payload.type = Messages.COMPRESSABLE;
goldenResponses[1].payload.body = new byte[100001];
long start = System.nanoTime();
final ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
ClientCall<StreamingOutputCallRequest, StreamingOutputCallResponse> call =
channel.newCall(TestServiceGrpc.METHOD_STREAMING_OUTPUT_CALL, CallOptions.DEFAULT);
call.start(new ClientCall.Listener<StreamingOutputCallResponse>() {
@Override
public void onHeaders(Metadata headers) {}
@Override
public void onMessage(final StreamingOutputCallResponse message) {
queue.add(message);
}
@Override
public void onClose(io.grpc.Status status, Metadata trailers) {
queue.add(status);
}
}, new Metadata());
call.sendMessage(request);
call.halfClose();
// Time how long it takes to get the first response.
call.request(1);
assertMessageEquals(goldenResponses[0],
(StreamingOutputCallResponse) queue.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
long firstCallDuration = System.nanoTime() - start;
// Without giving additional flow control, make sure that we don't get another response. We wait
// until we are comfortable the next message isn't coming. We may have very low nanoTime
// resolution (like on Windows) or be using a testing, in-process transport where message
// handling is instantaneous. In both cases, firstCallDuration may be 0, so round up sleep time
// to at least 1ms.
assertNull(queue.poll(Math.max(firstCallDuration * 4, 1 * 1000 * 1000), TimeUnit.NANOSECONDS));
// Make sure that everything still completes.
call.request(1);
assertMessageEquals(goldenResponses[1],
(StreamingOutputCallResponse) queue.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertCodeEquals(io.grpc.Status.OK,
(io.grpc.Status) queue.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
}
public void veryLargeRequest() throws Exception {
if (shouldSkip()) {
return;
}
final SimpleRequest request = new SimpleRequest();
request.payload = new Payload();
request.payload.type = Messages.COMPRESSABLE;
request.payload.body = new byte[unaryPayloadLength()];
request.responseSize = 10;
request.responseType = Messages.COMPRESSABLE;
final SimpleResponse goldenResponse = new SimpleResponse();
goldenResponse.payload = new Payload();
goldenResponse.payload.type = Messages.COMPRESSABLE;
goldenResponse.payload.body = new byte[10];
assertMessageEquals(goldenResponse, blockingStub.unaryCall(request));
}
public void veryLargeResponse() throws Exception {
if (shouldSkip()) {
return;
}
final SimpleRequest request = new SimpleRequest();
request.responseSize = unaryPayloadLength();
request.responseType = Messages.COMPRESSABLE;
SimpleResponse resp = blockingStub.unaryCall(request);
final SimpleResponse goldenResponse = new SimpleResponse();
goldenResponse.payload = new Payload();
goldenResponse.payload.type = Messages.COMPRESSABLE;
goldenResponse.payload.body = new byte[unaryPayloadLength()];
assertMessageSizeEquals(goldenResponse, resp);
}
public void deadlineNotExceeded() {
// warm up the channel and JVM
blockingStub.emptyCall(new EmptyProtos.Empty());
StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseParameters = new ResponseParameters[1];
request.responseParameters[0] = new ResponseParameters();
request.responseParameters[0].intervalUs = 0;
TestServiceGrpc.newBlockingStub(channel)
.withDeadlineAfter(10, TimeUnit.SECONDS)
.streamingOutputCall(request);
}
public void deadlineExceeded() {
// warm up the channel and JVM
blockingStub.emptyCall(new EmptyProtos.Empty());
TestServiceGrpc.TestServiceBlockingStub stub = TestServiceGrpc.newBlockingStub(channel)
.withDeadlineAfter(10, TimeUnit.MILLISECONDS);
StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseParameters = new ResponseParameters[1];
request.responseParameters[0] = new ResponseParameters();
request.responseParameters[0].intervalUs = 20000;
try {
stub.streamingOutputCall(request).next();
fail("Expected deadline to be exceeded");
} catch (StatusRuntimeException ex) {
assertCodeEquals(io.grpc.Status.DEADLINE_EXCEEDED, ex.getStatus());
}
}
public void deadlineExceededServerStreaming() throws Exception {
// warm up the channel and JVM
blockingStub.emptyCall(new EmptyProtos.Empty());
ResponseParameters responseParameters = new ResponseParameters();
responseParameters.size = 1;
responseParameters.intervalUs = 10000;
StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.responseType = Messages.COMPRESSABLE;
request.responseParameters = new ResponseParameters[4];
request.responseParameters[0] = responseParameters;
request.responseParameters[1] = responseParameters;
request.responseParameters[2] = responseParameters;
request.responseParameters[3] = responseParameters;
StreamRecorder<StreamingOutputCallResponse> recorder = StreamRecorder.create();
TestServiceGrpc.newStub(channel)
.withDeadlineAfter(30, TimeUnit.MILLISECONDS)
.streamingOutputCall(request, recorder);
assertTrue(recorder.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertCodeEquals(io.grpc.Status.DEADLINE_EXCEEDED,
io.grpc.Status.fromThrowable(recorder.getError()));
}
protected int unaryPayloadLength() {
// 10MiB.
return 10485760;
}
public void gracefulShutdown() throws Exception {
StreamingOutputCallRequest[] requests = new StreamingOutputCallRequest[3];
requests[0] = new StreamingOutputCallRequest();
requests[0].responseParameters = new ResponseParameters[1];
requests[0].responseParameters[0] = new ResponseParameters();
requests[0].responseParameters[0].size = 3;
requests[0].payload = new Payload();
requests[0].payload.body = new byte[2];
requests[1] = new StreamingOutputCallRequest();
requests[1].responseParameters = new ResponseParameters[1];
requests[1].responseParameters[0] = new ResponseParameters();
requests[1].responseParameters[0].size = 1;
requests[1].payload = new Payload();
requests[1].payload.body = new byte[7];
requests[2] = new StreamingOutputCallRequest();
requests[2].responseParameters = new ResponseParameters[1];
requests[2].responseParameters[0] = new ResponseParameters();
requests[2].responseParameters[0].size = 4;
requests[2].payload = new Payload();
requests[2].payload.body = new byte[1];
StreamingOutputCallResponse[] goldenResponses = new StreamingOutputCallResponse[3];
goldenResponses[0] = new StreamingOutputCallResponse();
goldenResponses[0].payload = new Payload();
goldenResponses[0].payload.type = Messages.COMPRESSABLE;
goldenResponses[0].payload.body = new byte[3];
goldenResponses[1] = new StreamingOutputCallResponse();
goldenResponses[1].payload = new Payload();
goldenResponses[1].payload.type = Messages.COMPRESSABLE;
goldenResponses[1].payload.body = new byte[1];
goldenResponses[2] = new StreamingOutputCallResponse();
goldenResponses[2].payload = new Payload();
goldenResponses[2].payload.type = Messages.COMPRESSABLE;
goldenResponses[2].payload.body = new byte[4];
ResponseObserver responseObserver = new ResponseObserver();
StreamObserver<StreamingOutputCallRequest> requestObserver
= asyncStub.fullDuplexCall(responseObserver);
requestObserver.onNext(requests[0]);
Object response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
assertTrue(response instanceof Messages.StreamingOutputCallResponse);
assertMessageEquals(goldenResponses[0], (Messages.StreamingOutputCallResponse) response);
// Initiate graceful shutdown.
channel.shutdown();
// The previous ping-pong could have raced with the shutdown, but this one certainly shouldn't.
requestObserver.onNext(requests[1]);
response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
assertTrue(response instanceof Messages.StreamingOutputCallResponse);
assertMessageEquals(goldenResponses[1], (Messages.StreamingOutputCallResponse) response);
requestObserver.onNext(requests[2]);
response = responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
assertTrue(response instanceof Messages.StreamingOutputCallResponse);
assertMessageEquals(goldenResponses[2], (Messages.StreamingOutputCallResponse) response);
requestObserver.onCompleted();
assertEquals(responseObserver.magicTailResponse,
responseObserver.responses.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
}
/** Sends an rpc to an unimplemented method on the server. */
public void unimplementedMethod() {
UnimplementedServiceGrpc.UnimplementedServiceBlockingStub stub =
UnimplementedServiceGrpc.newBlockingStub(channel);
try {
stub.unimplementedCall(new EmptyProtos.Empty());
fail();
} catch (StatusRuntimeException e) {
assertCodeEquals(io.grpc.Status.UNIMPLEMENTED, e.getStatus());
}
}
/** Start a fullDuplexCall which the server will not respond, and verify the deadline expires. */
public void timeoutOnSleepingServer() throws Exception {
TestServiceGrpc.TestServiceStub stub = TestServiceGrpc.newStub(channel)
.withDeadlineAfter(1, TimeUnit.MILLISECONDS);
StreamRecorder<StreamingOutputCallResponse> recorder = StreamRecorder.create();
StreamObserver<StreamingOutputCallRequest> requestObserver
= stub.fullDuplexCall(recorder);
try {
StreamingOutputCallRequest request = new StreamingOutputCallRequest();
request.payload = new Messages.Payload();
request.payload.body = new byte[27182];
requestObserver.onNext(request);
} catch (IllegalStateException expected) {
// This can happen if the stream has already been terminated due to deadline exceeded.
}
assertTrue(recorder.awaitCompletion(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
assertCodeEquals(io.grpc.Status.DEADLINE_EXCEEDED,
io.grpc.Status.fromThrowable(recorder.getError()));
}
public static void assertMessageSizeEquals(MessageNano expected, MessageNano actual) {
assertEquals(expected.getSerializedSize(), actual.getSerializedSize());
}
private static void assertSuccess(StreamRecorder<?> recorder) {
if (recorder.getError() != null) {
throw new AssertionError(recorder.getError());
}
}
public static void assertMessageEquals(MessageNano expected, MessageNano actual) {
if (!MessageNano.messageNanoEquals(expected, actual)) {
assertEquals(expected.toString(), actual.toString());
fail("Messages not equal, but assertEquals didn't throw");
}
}
public static void assertMessageEquals(List<? extends MessageNano> expected,
List<? extends MessageNano> actual) {
if (expected == null || actual == null) {
assertEquals(expected, actual);
} else if (expected.size() != actual.size()) {
assertEquals(expected, actual);
} else {
for (int i = 0; i < expected.size(); i++) {
assertMessageEquals(expected.get(i), actual.get(i));
}
}
}
private static void assertCodeEquals(io.grpc.Status expected, io.grpc.Status actual) {
if (expected == null) {
fail("expected should not be null");
}
if (actual == null || !expected.getCode().equals(actual.getCode())) {
assertEquals(expected, actual);
}
}
public interface TestListener {
void onPreTest();
void onPostTest(String result);
}
/**
* Some tests run on memory constrained environments. Rather than OOM, just give up. 64 is
* choosen as a maximum amount of memory a large test would need.
*/
private static boolean shouldSkip() {
Runtime r = Runtime.getRuntime();
long usedMem = r.totalMemory() - r.freeMemory();
long actuallyFreeMemory = r.maxMemory() - usedMem;
long wantedFreeMemory = 64 * 1024 * 1024;
if (actuallyFreeMemory < wantedFreeMemory) {
Log.i(LOG_TAG, "Skipping due to lack of memory. "
+ "Have: " + actuallyFreeMemory + " Want: " + wantedFreeMemory);
return true;
}
return false;
}
private static final class SafeMethodChannelInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new CheckedForwardingClientCall<ReqT, RespT>(
next.newCall(method.toBuilder().setSafe(true).build(), callOptions)) {
@Override
public void checkedStart(Listener<RespT> responseListener, Metadata headers) {
delegate().start(responseListener, headers);
}
};
}
}
}