diff options
author | Eric Gribkoff <ericgribkoff@google.com> | 2018-06-13 10:06:43 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-13 10:06:43 -0700 |
commit | 9f023819386f39a635e0999a3894715fd8436031 (patch) | |
tree | a78ecf78634a35c172eb731e55424b8c7d3296c8 /android-interop-testing | |
parent | 1c5007b866cb7728cb8ba73225975df5428a7a87 (diff) | |
download | grpc-grpc-java-9f023819386f39a635e0999a3894715fd8436031.tar.gz |
android-interop-testing,interop-testing: use AbstractInteropTest on Android (#4541)
Diffstat (limited to 'android-interop-testing')
15 files changed, 433 insertions, 1215 deletions
diff --git a/android-interop-testing/README.md b/android-interop-testing/README.md index 950907b08..c1abcee4d 100644 --- a/android-interop-testing/README.md +++ b/android-interop-testing/README.md @@ -35,21 +35,19 @@ $ ../gradlew installDebug Then manually test it with the UI. -Commandline test +Instrumentation tests ---------------- -Run the test with arguments: -``` -$ adb shell am instrument -w -e server_host <hostname or ip address> -e server_port <port> -e server_host_override foo.test.google.fr -e use_tls true -e use_test_ca true -e test_case all io.grpc.android.integrationtest/.TesterInstrumentation -``` +Instrumentation tests must be run on a connected device or emulator. Run with the +following gradle command: -If the test passed successfully, it will output: -``` -INSTRUMENTATION_RESULT: grpc test result=Success! -INSTRUMENTATION_CODE: -1 ``` -otherwise, output something like: -``` -INSTRUMENTATION_RESULT: grpc test result=Failed... : <exception stacktrace if applicable> -INSTRUMENTATION_CODE: 0 +$ ../gradlew connectedAndroidTest \ + -Pandroid.testInstrumentationRunnerArguments.server_host=10.0.2.2 \ + -Pandroid.testInstrumentationRunnerArguments.server_port=8080 \ + -Pandroid.testInstrumentationRunnerArguments.use_tls=true \ + -Pandroid.testInstrumentationRunnerArguments.server_host_override=foo.test.google.fr \ + -Pandroid.testInstrumentationRunnerArguments.use_test_ca=true \ + -Pandroid.testInstrumentationRunnerArguments.test_case=all ``` + diff --git a/android-interop-testing/app/build.gradle b/android-interop-testing/app/build.gradle index 51f7a2103..f20ab5d72 100644 --- a/android-interop-testing/app/build.gradle +++ b/android-interop-testing/app/build.gradle @@ -2,6 +2,20 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.protobuf' android { + sourceSets { + main { + java { + srcDirs += "${projectDir}/../../interop-testing/src/main/java/" + setIncludes(["io/grpc/android/integrationtest/**", + "io/grpc/testing/integration/AbstractInteropTest.java", + "io/grpc/testing/integration/TestServiceImpl.java", + "io/grpc/testing/integration/Util.java"]) + } + proto { + srcDirs += "${projectDir}/../../interop-testing/src/main/proto/" + } + } + } compileSdkVersion 26 defaultConfig { @@ -12,6 +26,7 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + multiDexEnabled true } buildTypes { debug { minifyEnabled false } @@ -26,41 +41,47 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.5.1-1' } plugins { + javalite { artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" } grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { all().each { task -> - task.builtins { - javanano { - // Options added to --javanano_out - option 'ignore_services=true' - option 'enum_style=java' - } - } - task.plugins { - grpc { // Options added to --grpc_out - option 'nano' } + javalite {} + grpc { + // Options added to --grpc_out + option 'lite' + } } } } } dependencies { - compile 'com.android.support:appcompat-v7:26.1.0' - compile 'com.android.support:support-annotations:27.1.1' - compile 'com.google.android.gms:play-services-base:12.0.1' + implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'com.android.support:multidex:1.0.3' + implementation 'com.android.support:support-annotations:27.1.1' + implementation 'com.google.android.gms:play-services-base:15.0.1' + implementation ('com.google.auth:google-auth-library-oauth2-http:0.9.0') { + exclude group: 'org.apache.httpcomponents', module: 'httpclient' + } + implementation 'com.google.truth:truth:0.36' + implementation 'javax.annotation:javax.annotation-api:1.2' + implementation 'junit:junit:4.12' + // You need to build grpc-java to obtain the grpc libraries below. - compile 'io.grpc:grpc-protobuf-nano:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION - compile 'io.grpc:grpc-okhttp:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION - compile 'io.grpc:grpc-stub:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION - compile 'io.grpc:grpc-testing:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION - compile 'javax.annotation:javax.annotation-api:1.2' - compile 'junit:junit:4.12' + implementation 'io.grpc:grpc-auth:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-testing:1.14.0-SNAPSHOT' // CURRENT_GRPC_VERSION + + // workaround for https://github.com/google/protobuf/issues/1889 + protobuf 'com.google.protobuf:protobuf-java:3.0.2' - androidTestCompile 'com.android.support.test:rules:1.0.1' - androidTestCompile 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test:rules:1.0.1' + androidTestImplementation 'com.android.support.test:runner:1.0.1' } gradle.projectsEvaluated { diff --git a/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropInstrumentationTest.java b/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropInstrumentationTest.java new file mode 100644 index 000000000..a7adf4b2d --- /dev/null +++ b/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropInstrumentationTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2017 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.android.integrationtest; + +import static junit.framework.Assert.assertEquals; + +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; +import com.google.android.gms.common.GooglePlayServicesNotAvailableException; +import com.google.android.gms.common.GooglePlayServicesRepairableException; +import com.google.android.gms.security.ProviderInstaller; +import com.google.common.util.concurrent.SettableFuture; +import io.grpc.ClientInterceptor; +import io.grpc.android.integrationtest.InteropTask.Listener; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class InteropInstrumentationTest { + private static final int TIMEOUT_SECONDS = 10; + private static String LOG_TAG = "GrpcInteropInstrumentationTest"; + + private String host; + private int port; + private boolean useTls; + private String serverHostOverride; + private boolean useTestCa; + private String testCase; + + @Before + public void setUp() throws Exception { + host = InstrumentationRegistry.getArguments().getString("server_host", "10.0.2.2"); + port = + Integer.parseInt(InstrumentationRegistry.getArguments().getString("server_port", "8080")); + useTls = + Boolean.parseBoolean(InstrumentationRegistry.getArguments().getString("use_tls", "true")); + serverHostOverride = InstrumentationRegistry.getArguments().getString("server_host_override"); + useTestCa = + Boolean.parseBoolean( + InstrumentationRegistry.getArguments().getString("use_test_ca", "false")); + testCase = InstrumentationRegistry.getArguments().getString("test_case", "all"); + + if (useTls) { + try { + ProviderInstaller.installIfNeeded(InstrumentationRegistry.getTargetContext()); + } catch (GooglePlayServicesRepairableException e) { + // The provider is helpful, but it is possible to succeed without it. + // Hope that the system-provided libraries are new enough. + Log.i(LOG_TAG, "Failed installing security provider", e); + } catch (GooglePlayServicesNotAvailableException e) { + // The provider is helpful, but it is possible to succeed without it. + // Hope that the system-provided libraries are new enough. + Log.i(LOG_TAG, "Failed installing security provider", e); + } + } + } + + @Test + public void interopTests() throws Exception { + if (testCase.equals("all")) { + 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"); + runTest("graceful_shutdown"); + } else { + runTest(testCase); + } + } + + private void runTest(String testCase) throws Exception { + final SettableFuture<String> resultFuture = SettableFuture.create(); + InteropTask.Listener listener = + new Listener() { + @Override + public void onComplete(String result) { + + resultFuture.set(result); + } + }; + InputStream testCa; + if (useTestCa) { + testCa = InstrumentationRegistry.getTargetContext().getResources().openRawResource(R.raw.ca); + } else { + testCa = null; + } + new InteropTask( + listener, + TesterOkHttpChannelBuilder.build(host, port, serverHostOverride, useTls, testCa), + new ArrayList<ClientInterceptor>(), + testCase) + .execute(); + String result = resultFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertEquals(testCase + " failed", result, InteropTask.SUCCESS_MESSAGE); + } +} diff --git a/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropTesterTest.java b/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropTesterTest.java deleted file mode 100644 index 8f83542de..000000000 --- a/android-interop-testing/app/src/androidTest/java/io/grpc/android/integrationtest/InteropTesterTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.android.integrationtest; - -import static junit.framework.Assert.assertEquals; - -import android.support.test.runner.AndroidJUnit4; -import com.google.common.util.concurrent.SettableFuture; -import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class InteropTesterTest { - private final int TIMEOUT_SECONDS = 120; - - @Test - public void interopTests() throws Exception { - final SettableFuture<String> resultFuture = SettableFuture.create(); - new InteropTester( - "all", - TesterOkHttpChannelBuilder.build( - "grpc-test.sandbox.googleapis.com", 443, null, true, null, null), - new InteropTester.TestListener() { - @Override - public void onPreTest() {} - - @Override - public void onPostTest(String result) { - resultFuture.set(result); - } - }, - false) - .execute(); - String result = resultFuture.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - assertEquals(result, InteropTester.SUCCESS_MESSAGE); - } -} diff --git a/android-interop-testing/app/src/main/AndroidManifest.xml b/android-interop-testing/app/src/main/AndroidManifest.xml index 193d8e01b..4e0409f33 100644 --- a/android-interop-testing/app/src/main/AndroidManifest.xml +++ b/android-interop-testing/app/src/main/AndroidManifest.xml @@ -25,9 +25,4 @@ </activity> </application> - <instrumentation android:functionalTest="true" - android:label="@string/app_name" - android:name=".TesterInstrumentation" - android:targetPackage="io.grpc.android.integrationtest" /> - </manifest> diff --git a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTask.java b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTask.java new file mode 100644 index 000000000..2ce2b1ab6 --- /dev/null +++ b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTask.java @@ -0,0 +1,174 @@ +/* + * Copyright 2018 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.android.integrationtest; + +import android.os.AsyncTask; +import android.util.Log; +import io.grpc.ClientInterceptor; +import io.grpc.ManagedChannel; +import io.grpc.testing.integration.AbstractInteropTest; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.ref.WeakReference; +import java.util.List; +import org.junit.AssumptionViolatedException; + +/** AsyncTask for interop test cases. */ +final class InteropTask extends AsyncTask<Void, Void, String> { + private static final String LOG_TAG = "GrpcInteropTask"; + + interface Listener { + void onComplete(String result); + } + + static final String SUCCESS_MESSAGE = "Success!"; + + private final WeakReference<Listener> listenerReference; + private final String testCase; + private final Tester tester; + + InteropTask( + Listener listener, + ManagedChannel channel, + List<ClientInterceptor> interceptors, + String testCase) { + this.listenerReference = new WeakReference<Listener>(listener); + this.testCase = testCase; + this.tester = new Tester(channel, interceptors); + } + + @Override + protected void onPreExecute() { + tester.setUp(); + } + + @Override + protected String doInBackground(Void... ignored) { + 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 { + try { + tester.tearDown(); + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } + + private void runTest(String testCase) throws Exception { + Log.i(LOG_TAG, "Running test case: " + testCase); + if ("empty_unary".equals(testCase)) { + tester.emptyUnary(); + } else if ("large_unary".equals(testCase)) { + try { + tester.largeUnary(); + } catch (AssumptionViolatedException e) { + // This test case requires more memory than most Android devices have available + Log.w(LOG_TAG, "Skipping " + testCase + " due to assumption violation", e); + } + } else if ("client_streaming".equals(testCase)) { + tester.clientStreaming(); + } else if ("server_streaming".equals(testCase)) { + tester.serverStreaming(); + } else if ("ping_pong".equals(testCase)) { + tester.pingPong(); + } else if ("empty_stream".equals(testCase)) { + tester.emptyStream(); + } else if ("cancel_after_begin".equals(testCase)) { + tester.cancelAfterBegin(); + } else if ("cancel_after_first_response".equals(testCase)) { + tester.cancelAfterFirstResponse(); + } else if ("full_duplex_call_should_succeed".equals(testCase)) { + tester.fullDuplexCallShouldSucceed(); + } else if ("half_duplex_call_should_succeed".equals(testCase)) { + tester.halfDuplexCallShouldSucceed(); + } else if ("server_streaming_should_be_flow_controlled".equals(testCase)) { + tester.serverStreamingShouldBeFlowControlled(); + } else if ("very_large_request".equals(testCase)) { + try { + tester.veryLargeRequest(); + } catch (AssumptionViolatedException e) { + // This test case requires more memory than most Android devices have available + Log.w(LOG_TAG, "Skipping " + testCase + " due to assumption violation", e); + } + } else if ("very_large_response".equals(testCase)) { + try { + tester.veryLargeResponse(); + } catch (AssumptionViolatedException e) { + // This test case requires more memory than most Android devices have available + Log.w(LOG_TAG, "Skipping " + testCase + " due to assumption violation", e); + } + } else if ("deadline_not_exceeded".equals(testCase)) { + tester.deadlineNotExceeded(); + } else if ("deadline_exceeded".equals(testCase)) { + tester.deadlineExceeded(); + } else if ("deadline_exceeded_server_streaming".equals(testCase)) { + tester.deadlineExceededServerStreaming(); + } else if ("unimplemented_method".equals(testCase)) { + tester.unimplementedMethod(); + } else if ("timeout_on_sleeping_server".equals(testCase)) { + tester.timeoutOnSleepingServer(); + } else if ("graceful_shutdown".equals(testCase)) { + tester.gracefulShutdown(); + } else { + throw new IllegalArgumentException("Unimplemented/Unknown test case: " + testCase); + } + } + + @Override + protected void onPostExecute(String result) { + Listener listener = listenerReference.get(); + if (listener != null) { + listener.onComplete(result); + } + } + + private class Tester extends AbstractInteropTest { + private final ManagedChannel channel; + private final List<ClientInterceptor> interceptors; + + private Tester(ManagedChannel channel, List<ClientInterceptor> interceptors) { + this.channel = channel; + this.interceptors = interceptors; + } + + @Override + protected ManagedChannel createChannel() { + return channel; + } + + @Override + protected ClientInterceptor[] getAdditionalInterceptors() { + return interceptors.toArray(new ClientInterceptor[0]); + } + + @Override + protected boolean metricsExpected() { + return false; + } + } +} diff --git a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTester.java b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTester.java deleted file mode 100644 index e7ea66bcf..000000000 --- a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/InteropTester.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright 2015 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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.internal.testing.StreamRecorder; -import io.grpc.stub.StreamObserver; -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 = "Success!"; - 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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.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.PayloadType.COMPRESSABLE; - goldenResponses[0].payload.body = new byte[100000]; - goldenResponses[1] = new StreamingOutputCallResponse(); - goldenResponses[1].payload = new Payload(); - goldenResponses[1].payload.type = Messages.PayloadType.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.getStreamingOutputCallMethod(), 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.PayloadType.COMPRESSABLE; - request.payload.body = new byte[unaryPayloadLength()]; - request.responseSize = 10; - request.responseType = Messages.PayloadType.COMPRESSABLE; - final SimpleResponse goldenResponse = new SimpleResponse(); - goldenResponse.payload = new Payload(); - goldenResponse.payload.type = Messages.PayloadType.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.PayloadType.COMPRESSABLE; - - SimpleResponse resp = blockingStub.unaryCall(request); - final SimpleResponse goldenResponse = new SimpleResponse(); - goldenResponse.payload = new Payload(); - goldenResponse.payload.type = Messages.PayloadType.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.PayloadType.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.PayloadType.COMPRESSABLE; - goldenResponses[0].payload.body = new byte[3]; - goldenResponses[1] = new StreamingOutputCallResponse(); - goldenResponses[1].payload = new Payload(); - goldenResponses[1].payload.type = Messages.PayloadType.COMPRESSABLE; - goldenResponses[1].payload.body = new byte[1]; - goldenResponses[2] = new StreamingOutputCallResponse(); - goldenResponses[2].payload = new Payload(); - goldenResponses[2].payload.type = Messages.PayloadType.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); - } - }; - } - } -} diff --git a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterActivity.java b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterActivity.java index 02013a7e1..cbf63e3ca 100644 --- a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterActivity.java +++ b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterActivity.java @@ -29,16 +29,29 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import com.google.android.gms.security.ProviderInstaller; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ClientInterceptors.CheckedForwardingClientCall; +import io.grpc.ManagedChannel; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import java.io.InputStream; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class TesterActivity extends AppCompatActivity - implements ProviderInstaller.ProviderInstallListener { + implements ProviderInstaller.ProviderInstallListener, InteropTask.Listener { + private static final String LOG_TAG = "GrpcTesterActivity"; + private List<Button> buttons; private EditText hostEdit; private EditText portEdit; private TextView resultText; private CheckBox getCheckBox; + private CheckBox testCertCheckBox; @Override protected void onCreate(Bundle savedInstanceState) { @@ -55,6 +68,7 @@ public class TesterActivity extends AppCompatActivity portEdit = (EditText) findViewById(R.id.port_edit_text); resultText = (TextView) findViewById(R.id.grpc_response_text); getCheckBox = (CheckBox) findViewById(R.id.get_checkbox); + testCertCheckBox = (CheckBox) findViewById(R.id.test_cert_checkbox); ProviderInstaller.installIfNeededAsync(this, this); // Disable buttons until the security provider installing finishes. @@ -87,28 +101,39 @@ public class TesterActivity extends AppCompatActivity } } + @Override + public void onComplete(String result) { + resultText.setText(result); + enableButtons(true); + } + private void startTest(String testCase) { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow( hostEdit.getWindowToken(), 0); enableButtons(false); + resultText.setText("Testing..."); + String host = hostEdit.getText().toString(); String portStr = portEdit.getText().toString(); int port = TextUtils.isEmpty(portStr) ? 8080 : Integer.valueOf(portStr); - // TODO (madongfly) support server_host_override, useTls and useTestCa in the App UI. - new InteropTester(testCase, - TesterOkHttpChannelBuilder.build(host, port, "foo.test.google.fr", true, - getResources().openRawResource(R.raw.ca), null), - new InteropTester.TestListener() { - @Override public void onPreTest() { - resultText.setText("Testing..."); - } - - @Override public void onPostTest(String result) { - resultText.setText(result); - enableButtons(true); - } - }, getCheckBox.isChecked()).execute(); + String serverHostOverride; + InputStream testCert; + if (testCertCheckBox.isChecked()) { + serverHostOverride = "foo.test.google.fr"; + testCert = getResources().openRawResource(R.raw.ca); + } else { + serverHostOverride = null; + testCert = null; + } + ManagedChannel channel = + TesterOkHttpChannelBuilder.build(host, port, serverHostOverride, true, testCert); + + List<ClientInterceptor> interceptors = new ArrayList<ClientInterceptor>(); + if (getCheckBox.isChecked()) { + interceptors.add(new SafeMethodChannelInterceptor()); + } + new InteropTask(this, channel, interceptors, testCase).execute(); } @Override @@ -121,7 +146,21 @@ public class TesterActivity extends AppCompatActivity public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) { // The provider is helpful, but it is possible to succeed without it. // Hope that the system-provided libraries are new enough. - Log.w(InteropTester.LOG_TAG, "Failed installing security provider, error code: " + errorCode); + Log.w(LOG_TAG, "Failed installing security provider, error code: " + errorCode); enableButtons(true); } + + 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); + } + }; + } + } } diff --git a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterInstrumentation.java b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterInstrumentation.java deleted file mode 100644 index c17bbed0b..000000000 --- a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterInstrumentation.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.android.integrationtest; - -import android.app.Activity; -import android.app.Instrumentation; -import android.os.Bundle; -import android.util.Log; -import com.google.android.gms.common.GooglePlayServicesNotAvailableException; -import com.google.android.gms.common.GooglePlayServicesRepairableException; -import com.google.android.gms.security.ProviderInstaller; -import java.io.InputStream; -import java.lang.Throwable; - - -/** - * The instrumentation used to run the interop test from command line. - */ -public class TesterInstrumentation extends Instrumentation { - private String testCase; - private String host; - private int port; - private String serverHostOverride; - private boolean useTls; - private boolean useTestCa; - private String androidSocketFactoryTls; - private boolean useGet; - - @Override - public void onCreate(Bundle args) { - super.onCreate(args); - - testCase = args.getString("test_case") != null ? args.getString("test_case") : "empty_unary"; - host = args.getString("server_host"); - port = Integer.parseInt(args.getString("server_port")); - serverHostOverride = args.getString("server_host_override"); - useTls = args.getString("use_tls") != null - ? Boolean.parseBoolean(args.getString("use_tls")) : true; - useTestCa = args.getString("use_test_ca") != null - ? Boolean.parseBoolean(args.getString("use_test_ca")) : false; - androidSocketFactoryTls = args.getString("android_socket_factory_tls"); - useGet = args.getString("use_get") != null - ? Boolean.parseBoolean(args.getString("use_get")) : false; - - InputStream testCa = null; - if (useTestCa) { - testCa = getContext().getResources().openRawResource(R.raw.ca); - } - - if (useTls) { - try { - ProviderInstaller.installIfNeeded(getContext()); - } catch (GooglePlayServicesRepairableException e) { - // The provider is helpful, but it is possible to succeed without it. - // Hope that the system-provided libraries are new enough. - Log.w(InteropTester.LOG_TAG, "Failed installing security provider", e); - } catch (GooglePlayServicesNotAvailableException e) { - // The provider is helpful, but it is possible to succeed without it. - // Hope that the system-provided libraries are new enough. - Log.w(InteropTester.LOG_TAG, "Failed installing security provider", e); - } - } - - try { - new InteropTester(testCase, - TesterOkHttpChannelBuilder.build(host, port, serverHostOverride, useTls, testCa, - androidSocketFactoryTls), - new InteropTester.TestListener() { - @Override - public void onPreTest() { - } - - @Override - public void onPostTest(String result) { - Bundle bundle = new Bundle(); - bundle.putString("grpc test result", result); - if (InteropTester.SUCCESS_MESSAGE.equals(result)) { - finish(Activity.RESULT_OK, bundle); - } else { - finish(Activity.RESULT_CANCELED, bundle); - } - } - }, - useGet).execute(); - } catch (Throwable t) { - Bundle bundle = new Bundle(); - bundle.putString("Exception encountered", t.toString()); - finish(Activity.RESULT_CANCELED, bundle); - } - } -} diff --git a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterOkHttpChannelBuilder.java b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterOkHttpChannelBuilder.java index bd719788c..e537eb778 100644 --- a/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterOkHttpChannelBuilder.java +++ b/android-interop-testing/app/src/main/java/io/grpc/android/integrationtest/TesterOkHttpChannelBuilder.java @@ -20,18 +20,14 @@ import android.annotation.TargetApi; import android.net.SSLCertificateSocketFactory; import android.os.Build; import android.support.annotation.Nullable; - import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import io.grpc.okhttp.NegotiationType; import io.grpc.okhttp.OkHttpChannelBuilder; - import java.io.InputStream; import java.lang.reflect.Method; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; - import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -42,8 +38,12 @@ import javax.security.auth.x500.X500Principal; * A helper class to create a OkHttp based channel. */ class TesterOkHttpChannelBuilder { - public static ManagedChannel build(String host, int port, @Nullable String serverHostOverride, - boolean useTls, @Nullable InputStream testCa, @Nullable String androidSocketFactoryTls) { + public static ManagedChannel build( + String host, + int port, + @Nullable String serverHostOverride, + boolean useTls, + @Nullable InputStream testCa) { ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forAddress(host, port) .maxInboundMessageSize(16 * 1024 * 1024); if (serverHostOverride != null) { @@ -52,14 +52,8 @@ class TesterOkHttpChannelBuilder { } if (useTls) { try { - SSLSocketFactory factory; - if (androidSocketFactoryTls != null) { - factory = getSslCertificateSocketFactory(testCa, androidSocketFactoryTls); - } else { - factory = getSslSocketFactory(testCa); - } - ((OkHttpChannelBuilder) channelBuilder).negotiationType(NegotiationType.TLS); - ((OkHttpChannelBuilder) channelBuilder).sslSocketFactory(factory); + ((OkHttpChannelBuilder) channelBuilder).useTransportSecurity(); + ((OkHttpChannelBuilder) channelBuilder).sslSocketFactory(getSslSocketFactory(testCa)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/android-interop-testing/app/src/main/proto/grpc/testing/empty.proto b/android-interop-testing/app/src/main/proto/grpc/testing/empty.proto deleted file mode 100644 index a5c9dc619..000000000 --- a/android-interop-testing/app/src/main/proto/grpc/testing/empty.proto +++ /dev/null @@ -1,30 +0,0 @@ - -// Copyright 2015 The gRPC Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -syntax = "proto2"; - -package grpc.testing; - -option java_package = "com.google.protobuf"; -option java_outer_classname = "EmptyProtos"; - -// An empty message that you can re-use to avoid defining duplicated empty -// messages in your project. A typical example is to use it as argument or the -// return value of a service API. For instance: -// -// service Foo { -// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; -// }; -// -message Empty {} diff --git a/android-interop-testing/app/src/main/proto/grpc/testing/messages.proto b/android-interop-testing/app/src/main/proto/grpc/testing/messages.proto deleted file mode 100644 index 64a10f4f7..000000000 --- a/android-interop-testing/app/src/main/proto/grpc/testing/messages.proto +++ /dev/null @@ -1,122 +0,0 @@ - -// Copyright 2015 The gRPC Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// Message definitions to be used by integration test service definitions. - -syntax = "proto3"; - -package grpc.testing; - -option java_package = "io.grpc.android.integrationtest"; - -// The type of payload that should be returned. -enum PayloadType { - // Compressable text format. - COMPRESSABLE = 0; - - // Uncompressable binary format. - UNCOMPRESSABLE = 1; - - // Randomly chosen from all other formats defined in this enum. - RANDOM = 2; -} - -// A block of data, to simply increase gRPC message size. -message Payload { - // The type of data in body. - PayloadType type = 1; - // Primary contents of payload. - bytes body = 2; -} - -// Unary request. -message SimpleRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, server randomly chooses one from other formats. - PayloadType response_type = 1; - - // Desired payload size in the response from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - int32 response_size = 2; - - // Optional input payload sent along with the request. - Payload payload = 3; - - // Whether SimpleResponse should include username. - bool fill_username = 4; - - // Whether SimpleResponse should include OAuth scope. - bool fill_oauth_scope = 5; -} - -// Unary response, as configured by the request. -message SimpleResponse { - // Payload to increase message size. - Payload payload = 1; - // The user the request came from, for verifying authentication was - // successful when the client expected it. - string username = 2; - // OAuth scope. - string oauth_scope = 3; -} - -message SimpleContext { - string value = 1; -} - -// Client-streaming request. -message StreamingInputCallRequest { - // Optional input payload sent along with the request. - Payload payload = 1; - - // Not expecting any payload from the response. -} - -// Client-streaming response. -message StreamingInputCallResponse { - // Aggregated size of payloads received from the client. - int32 aggregated_payload_size = 1; -} - -// Configuration for a particular response. -message ResponseParameters { - // Desired payload sizes in responses from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - int32 size = 1; - - // Desired interval between consecutive responses in the response stream in - // microseconds. - int32 interval_us = 2; -} - -// Server-streaming request. -message StreamingOutputCallRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, the payload from each response in the stream - // might be of different types. This is to simulate a mixed type of payload - // stream. - PayloadType response_type = 1; - - // Configuration for each expected response message. - repeated ResponseParameters response_parameters = 2; - - // Optional input payload sent along with the request. - Payload payload = 3; -} - -// Server-streaming response, as configured by the request and parameters. -message StreamingOutputCallResponse { - // Payload to increase response size. - Payload payload = 1; -} diff --git a/android-interop-testing/app/src/main/proto/grpc/testing/test.proto b/android-interop-testing/app/src/main/proto/grpc/testing/test.proto deleted file mode 100644 index 45871ed77..000000000 --- a/android-interop-testing/app/src/main/proto/grpc/testing/test.proto +++ /dev/null @@ -1,64 +0,0 @@ - -// Copyright 2015 The gRPC Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// An integration test service that covers all the method signature permutations -// of unary/streaming requests/responses. -syntax = "proto3"; - -import "grpc/testing/empty.proto"; -import "grpc/testing/messages.proto"; - -package grpc.testing; - -option java_package = "io.grpc.android.integrationtest"; - -// A simple service to test the various types of RPCs and experiment with -// performance with various types of payload. -service TestService { - // One empty request followed by one empty response. - rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); - - // One request followed by one response. - rpc UnaryCall(SimpleRequest) returns (SimpleResponse); - - // One request followed by a sequence of responses (streamed download). - // The server returns the payload with client desired type and sizes. - rpc StreamingOutputCall(StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by one response (streamed upload). - // The server returns the aggregated size of client payload as the result. - rpc StreamingInputCall(stream StreamingInputCallRequest) - returns (StreamingInputCallResponse); - - // A sequence of requests with each request served by the server immediately. - // As one request could lead to multiple responses, this interface - // demonstrates the idea of full duplexing. - rpc FullDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by a sequence of responses. - // The server buffers all the client requests and then serves them in order. A - // stream of responses are returned to the client when the server starts with - // first request. - rpc HalfDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); -} - -// A simple service NOT implemented at servers so clients can test for -// that case. -service UnimplementedService { - // A call that no server should implement - rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); -} diff --git a/android-interop-testing/app/src/main/res/layout/activity_tester.xml b/android-interop-testing/app/src/main/res/layout/activity_tester.xml index c7cef73b3..1fd55dd83 100644 --- a/android-interop-testing/app/src/main/res/layout/activity_tester.xml +++ b/android-interop-testing/app/src/main/res/layout/activity_tester.xml @@ -26,11 +26,23 @@ android:inputType="numberDecimal" android:hint="Enter Port" /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > <CheckBox android:id="@+id/get_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/use_get" /> + <CheckBox android:id="@+id/test_cert_checkbox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Use Test Cert" + /> </LinearLayout> <Button diff --git a/android-interop-testing/build.gradle b/android-interop-testing/build.gradle index a0baaceb9..5017248dd 100644 --- a/android-interop-testing/build.gradle +++ b/android-interop-testing/build.gradle @@ -6,8 +6,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.3" + classpath 'com.android.tools.build:gradle:3.1.2' + classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.5" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files |