Commit 7a4cba9f authored by kapishnikov's avatar kapishnikov Committed by Commit bot

Cronet smoke tests

1. Added test support classes that allow running the smoke tests in
   different environments against different servers. There is one
   support class for Java-only tests and one that requires H2 & QUIC.
   The concrete implementation of support classes is determined at
   runtime by reading the value of |TestSupportImplClass| from the
   Android string resource file.
2. Added two test APKs one for Java only deployment and one to test
   behavior when the native “.so” file is absent.
3. Enabled proguarding for the existing unit tests. The new APKS also
   run proguard in order to check the correctness of proguard configs
   for different deployments.
4. Added new HttpServer for Java-only scenario that does not depend on
   Chromium “base” and “net” modules.
5. Added two additional smoke QUIC & H2 tests to the existing test APK.

To run all smoke tests, execute:

ninja -C out/Debug cronet_smoketests_platform_only_instrumentation_apk \
cronet_smoketests_missing_native_library_instrumentation_apk \
cronet_test_instrumentation_apk -j 240 && \
./out/Debug/bin/run_cronet_smoketests_platform_only_instrumentation_apk && \
./out/Debug/bin/run_cronet_smoketests_missing_native_library_instrumentation_apk && \
./out/Debug/bin/run_cronet_test_instrumentation_apk \
--test-filter=org.chromium.net.smoke.*

The output jars that contain the smoke tests:
./out/Debug/lib.java/components/cronet/android/cronet_smoketests_missing_native_library_java.jar
./out/Debug/lib.java/components/cronet/android/cronet_smoketests_native_java.jar
./out/Debug/lib.java/components/cronet/android/cronet_smoketests_platform_only_java.jar

BUG=669264
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.android:android_cronet_tester

Review-Url: https://codereview.chromium.org/2561803002
Cr-Commit-Position: refs/heads/master@{#440760}
parent 9273ccb3
...@@ -553,7 +553,10 @@ shared_library("cronet_tests") { ...@@ -553,7 +553,10 @@ shared_library("cronet_tests") {
android_resources("cronet_test_apk_resources") { android_resources("cronet_test_apk_resources") {
testonly = true testonly = true
resource_dirs = [ "test/res" ] resource_dirs = [
"test/res",
"test/smoketests/res/native",
]
android_manifest = "test/AndroidManifest.xml" android_manifest = "test/AndroidManifest.xml"
} }
...@@ -588,6 +591,35 @@ android_library("cronet_test_apk_java") { ...@@ -588,6 +591,35 @@ android_library("cronet_test_apk_java") {
run_findbugs_override = true run_findbugs_override = true
} }
cronet_smoketests_platform_only_common_srcs = [
"test/smoketests/src/org/chromium/net/smoke/ChromiumPlatformOnlyTestSupport.java",
"test/smoketests/src/org/chromium/net/smoke/CronetSmokeTestCase.java",
"test/smoketests/src/org/chromium/net/smoke/HttpTestServer.java",
"test/smoketests/src/org/chromium/net/smoke/SmokeTestRequestCallback.java",
"test/smoketests/src/org/chromium/net/smoke/TestSupport.java",
]
cronet_smoketests_native_common_srcs = cronet_smoketests_platform_only_common_srcs + [
"test/smoketests/src/org/chromium/net/smoke/ChromiumNativeTestSupport.java",
"test/smoketests/src/org/chromium/net/smoke/NativeCronetTestCase.java",
]
android_library("cronet_smoketests_native_java") {
testonly = true
java_files = [
"test/smoketests/src/org/chromium/net/smoke/Http2Test.java",
"test/smoketests/src/org/chromium/net/smoke/QuicTest.java",
] + cronet_smoketests_native_common_srcs
deps = [
":cronet_api_java",
":cronet_test_apk_java",
"//base:base_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/netty4:netty_all_java",
]
}
android_assets("cronet_test_apk_assets") { android_assets("cronet_test_apk_assets") {
testonly = true testonly = true
...@@ -639,13 +671,21 @@ android_apk("cronet_test_apk") { ...@@ -639,13 +671,21 @@ android_apk("cronet_test_apk") {
loadable_modules = [ "$root_out_dir/libnetty-tcnative.so" ] loadable_modules = [ "$root_out_dir/libnetty-tcnative.so" ]
deps = [ deps = [
":cronet_combine_proguard_flags",
":cronet_test_apk_assets", ":cronet_test_apk_assets",
":cronet_test_apk_java",
":cronet_test_apk_resources", ":cronet_test_apk_resources",
"//base:base_java", "//base:base_java",
"//third_party/netty-tcnative:netty-tcnative-so", "//third_party/netty-tcnative:netty-tcnative-so",
] ]
proguard_enabled = true
proguard_configs = [
"$target_gen_dir/cronet_impl_native_proguard.cfg",
"cronet_impl_common_proguard.cfg",
"cronet_impl_platform_proguard.cfg",
]
run_findbugs_override = true run_findbugs_override = true
} }
...@@ -710,6 +750,7 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") { ...@@ -710,6 +750,7 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") {
":cronet_api_java", ":cronet_api_java",
":cronet_impl_all_java", ":cronet_impl_all_java",
":cronet_javatests", ":cronet_javatests",
":cronet_smoketests_native_java",
":cronet_test_apk_java", ":cronet_test_apk_java",
"//base:base_java", "//base:base_java",
"//base:base_java_test_support", "//base:base_java_test_support",
...@@ -723,6 +764,113 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") { ...@@ -723,6 +764,113 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") {
"//net:test_support", "//net:test_support",
] ]
proguard_enabled = true
proguard_configs = [ "test/proguard.cfg" ]
run_findbugs_override = true
}
android_resources("cronet_smoketests_platform_only_apk_resources") {
testonly = true
resource_dirs = [ "test/smoketests/res/platform_only" ]
android_manifest = "test/AndroidManifest.xml"
}
android_library("cronet_smoketests_platform_only_java") {
testonly = true
java_files = [ "test/smoketests/src/org/chromium/net/smoke/PlatformOnlyEngineTest.java" ] + cronet_smoketests_platform_only_common_srcs
deps = [
":cronet_api_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/netty4:netty_all_java",
]
}
android_apk("cronet_smoketests_platform_only_apk") {
testonly = true
apk_name = "PlatformOnlyEngineSmokeTest"
android_manifest = "test/AndroidManifest.xml"
java_files = [ "test/src/org/chromium/net/CronetTestApplication.java" ]
proguard_enabled = true
proguard_configs = [
"cronet_impl_common_proguard.cfg",
"cronet_impl_platform_proguard.cfg",
]
deps = [
":cronet_api_java",
":cronet_impl_common_java",
":cronet_impl_platform_java",
":cronet_smoketests_platform_only_apk_resources",
]
run_findbugs_override = true
}
instrumentation_test_apk(
"cronet_smoketests_platform_only_instrumentation_apk") {
apk_name = "PlatformOnlyEngineSmokeTestInstrumentation"
apk_under_test = ":cronet_smoketests_platform_only_apk"
android_manifest = "test/javatests/AndroidManifest.xml"
deps = [
":cronet_smoketests_platform_only_java",
]
proguard_enabled = true
proguard_configs = [ "test/proguard.cfg" ]
run_findbugs_override = true
}
android_library("cronet_smoketests_missing_native_library_java") {
testonly = true
java_files = [ "test/smoketests/src/org/chromium/net/smoke/MissingNativeLibraryTest.java" ] + cronet_smoketests_native_common_srcs
deps = [
":cronet_api_java",
":cronet_test_apk_java",
"//base:base_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/netty4:netty_all_java",
]
}
android_apk("cronet_smoketests_missing_native_library_apk") {
testonly = true
apk_name = "MissingNativeLibrarySmokeTest"
android_manifest = "test/AndroidManifest.xml"
deps = [
":cronet_api_java",
":cronet_combine_proguard_flags",
":cronet_impl_common_java",
":cronet_impl_platform_java",
":cronet_test_apk_resources",
]
proguard_enabled = true
proguard_configs = [
"$target_gen_dir/cronet_impl_native_proguard.cfg",
"cronet_impl_common_proguard.cfg",
"cronet_impl_platform_proguard.cfg",
]
run_findbugs_override = true
}
instrumentation_test_apk(
"cronet_smoketests_missing_native_library_instrumentation_apk") {
apk_name = "MissingNativeLibrarySmokeTestInstrumentation"
apk_under_test = ":cronet_smoketests_missing_native_library_apk"
android_manifest = "test/javatests/AndroidManifest.xml"
deps = [
":cronet_smoketests_missing_native_library_java",
]
proguard_enabled = true
proguard_configs = [ "test/proguard.cfg" ]
run_findbugs_override = true run_findbugs_override = true
} }
...@@ -763,7 +911,7 @@ android_apk("cronet_perf_test_apk") { ...@@ -763,7 +911,7 @@ android_apk("cronet_perf_test_apk") {
proguard_configs = [ proguard_configs = [
"$target_gen_dir/cronet_impl_native_proguard.cfg", "$target_gen_dir/cronet_impl_native_proguard.cfg",
"cronet_impl_common_proguard.cfg", "cronet_impl_common_proguard.cfg",
"test/javaperftests/proguard.cfg", "test/proguard.cfg",
"//base/android/proguard/chromium_apk.flags", "//base/android/proguard/chromium_apk.flags",
] ]
} }
......
...@@ -3,4 +3,8 @@ ...@@ -3,4 +3,8 @@
# This constructor is called using the reflection from Cronet API (cronet_api.jar). # This constructor is called using the reflection from Cronet API (cronet_api.jar).
-keep class org.chromium.net.impl.CronetEngineBuilderImpl { -keep class org.chromium.net.impl.CronetEngineBuilderImpl {
public <init>(android.content.Context); public <init>(android.content.Context);
} }
\ No newline at end of file
# This class should be explicitly kept to avoid failure if
# class/merging/horizontal proguard optimization is enabled.
-keep class org.chromium.net.impl.ImplVersion
\ No newline at end of file
...@@ -23,3 +23,7 @@ ...@@ -23,3 +23,7 @@
# https://android.googlesource.com/platform/sdk/+/marshmallow-mr1-release/files/proguard-android.txt#54 # https://android.googlesource.com/platform/sdk/+/marshmallow-mr1-release/files/proguard-android.txt#54
-dontwarn android.support.** -dontwarn android.support.**
# This class should be explicitly kept to avoid failure if
# class/merging/horizontal proguard optimization is enabled.
-keep class org.chromium.base.CollectionUtil
# TODO(pauljensen): Remove when crbug.com/488192 is fixed.
-dontwarn org.apache.http.**
# Javaperftest doesn't use Netty so ignore warnings.
-dontwarn io.netty.**
# Proguard configuration that is common for all type of tests.
-keepattributes Signature,InnerClasses,SourceFile,LineNumberTable
-dontwarn io.netty.**
-keep class io.netty.** { *; }
-keep class org.chromium.net.smoke.ChromiumNativeTestSupport
-keep class org.chromium.net.smoke.ChromiumPlatformOnlyTestSupport
# TODO(jbudorick): Remove when crbug.com/488192 is fixed.
-dontwarn org.apache.http.**
-dontwarn android.support.test.runner.MonitoringInstrumentation
# These classes should be explicitly kept to avoid failure if
# class/merging/horizontal proguard optimization is enabled.
# NOTE: make sure that only test classes are added to this list.
-keep class org.chromium.base.test.util.**
-keep class org.chromium.net.TestFilesInstaller
-keep class org.chromium.net.MetricsTestUtil
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<resources>
<string name="TestSupportImplClass">org.chromium.net.smoke.ChromiumNativeTestSupport</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<network-security-config/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<resources>
<string name="TestSupportImplClass">org.chromium.net.smoke.ChromiumPlatformOnlyTestSupport</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<network-security-config/>
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.content.Context;
import org.json.JSONObject;
import org.chromium.base.Log;
import org.chromium.net.CronetTestUtil;
import org.chromium.net.ExperimentalCronetEngine;
/**
* Provides support for tests that depend on QUIC and HTTP2 servers.
*/
class ChromiumNativeTestSupport extends ChromiumPlatformOnlyTestSupport {
private static final String TAG = "NativeTestSupport";
@Override
public TestServer createTestServer(Context context, Protocol protocol) {
switch (protocol) {
case QUIC:
return new QuicTestServer(context);
case HTTP2:
return new Http2TestServer(context);
case HTTP1:
return super.createTestServer(context, protocol);
default:
throw new RuntimeException("Unknown server protocol: " + protocol);
}
}
@Override
public void addHostResolverRules(JSONObject experimentalOptionsJson) {
try {
JSONObject hostResolverParams = CronetTestUtil.generateHostResolverRules();
experimentalOptionsJson.put("HostResolverRules", hostResolverParams);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void installMockCertVerifierForTesting(ExperimentalCronetEngine.Builder builder) {
CronetTestUtil.setMockCertVerifierForTesting(
builder, org.chromium.net.QuicTestServer.createMockCertVerifier());
}
@Override
public void loadTestNativeLibrary() {
System.loadLibrary("cronet_tests");
}
private static class QuicTestServer implements TestServer {
private final Context mContext;
QuicTestServer(Context context) {
mContext = context;
}
@Override
public boolean start() {
org.chromium.net.QuicTestServer.startQuicTestServer(mContext);
return true;
}
@Override
public void shutdown() {
org.chromium.net.QuicTestServer.shutdownQuicTestServer();
}
@Override
public String getSuccessURL() {
return org.chromium.net.QuicTestServer.getServerURL() + "/simple.txt";
}
}
private static class Http2TestServer implements TestServer {
private final Context mContext;
Http2TestServer(Context context) {
mContext = context;
}
@Override
public boolean start() {
try {
return org.chromium.net.Http2TestServer.startHttp2TestServer(mContext,
org.chromium.net.QuicTestServer.getServerCert(),
org.chromium.net.QuicTestServer.getServerCertKey());
} catch (Exception e) {
Log.e(TAG, "Exception during Http2TestServer start", e);
return false;
}
}
@Override
public void shutdown() {
try {
org.chromium.net.Http2TestServer.shutdownHttp2TestServer();
} catch (Exception e) {
Log.e(TAG, "Exception during Http2TestServer shutdown", e);
}
}
@Override
public String getSuccessURL() {
return org.chromium.net.Http2TestServer.getEchoMethodUrl();
}
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.content.Context;
import org.json.JSONObject;
import org.chromium.net.ExperimentalCronetEngine;
import java.io.File;
/**
* Tests support for Java only Cronet engine tests. This class should not depend on
* Chromium 'base' or 'net'.
*/
public class ChromiumPlatformOnlyTestSupport implements TestSupport {
@Override
public TestServer createTestServer(Context context, Protocol protocol) {
switch (protocol) {
case QUIC:
throw new IllegalArgumentException("QUIC is not supported");
case HTTP2:
throw new IllegalArgumentException("HTTP2 is not supported");
case HTTP1:
return new HttpTestServer();
default:
throw new IllegalArgumentException("Unknown server protocol: " + protocol);
}
}
@Override
public void processNetLog(Context context, File file) {
// Do nothing
}
@Override
public void addHostResolverRules(JSONObject experimentalOptionsJson) {
throw new UnsupportedOperationException("Unsupported by ChromiumPlatformOnlyTestSupport");
}
@Override
public void installMockCertVerifierForTesting(ExperimentalCronetEngine.Builder builder) {
throw new UnsupportedOperationException("Unsupported by ChromiumPlatformOnlyTestSupport");
}
@Override
public void loadTestNativeLibrary() {
throw new UnsupportedOperationException("Unsupported by ChromiumPlatformOnlyTestSupport");
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.content.Context;
import android.test.AndroidTestCase;
import org.chromium.net.CronetEngine;
import org.chromium.net.ExperimentalCronetEngine;
import org.chromium.net.UrlResponseInfo;
/**
* Base test class. This class should not import any classes from the org.chromium.base package.
*/
public class CronetSmokeTestCase extends AndroidTestCase {
/**
* The key in the string resource file that specifies {@link TestSupport} that should
* be instantiated.
*/
private static final String SUPPORT_IMPL_RES_KEY = "TestSupportImplClass";
protected ExperimentalCronetEngine.Builder mCronetEngineBuilder;
protected CronetEngine mCronetEngine;
protected TestSupport mTestSupport;
@Override
protected void setUp() throws Exception {
super.setUp();
mCronetEngineBuilder = new ExperimentalCronetEngine.Builder(getContext());
initTestSupport();
}
@Override
protected void tearDown() throws Exception {
if (mCronetEngine != null) {
mCronetEngine.shutdown();
}
super.tearDown();
}
protected void initCronetEngine() {
mCronetEngine = mCronetEngineBuilder.build();
}
static void assertSuccessfulNonEmptyResponse(SmokeTestRequestCallback callback, String url) {
// Check the request state
if (callback.getFinalState() == SmokeTestRequestCallback.State.Failed) {
throw new RuntimeException(
"The request failed with an error", callback.getFailureError());
}
assertEquals(SmokeTestRequestCallback.State.Succeeded, callback.getFinalState());
// Check the response info
UrlResponseInfo responseInfo = callback.getResponseInfo();
assertNotNull(responseInfo);
assertFalse(responseInfo.wasCached());
assertEquals(url, responseInfo.getUrl());
assertEquals(url, responseInfo.getUrlChain().get(responseInfo.getUrlChain().size() - 1));
assertEquals(200, responseInfo.getHttpStatusCode());
assertTrue(responseInfo.toString().length() > 0);
}
static void assertJavaEngine(CronetEngine engine) {
assertNotNull(engine);
assertEquals("org.chromium.net.impl.JavaCronetEngine", engine.getClass().getName());
}
static void assertNativeEngine(CronetEngine engine) {
assertNotNull(engine);
assertEquals("org.chromium.net.impl.CronetUrlRequestContext", engine.getClass().getName());
}
/**
* Instantiates a concrete implementation of {@link TestSupport} interface.
* The name of the implementation class is determined dynamically by reading
* the value of |TestSupportImplClass| from the Android string resource file.
*
* @throws Exception if the class cannot be instantiated.
*/
private void initTestSupport() throws Exception {
Context ctx = getContext();
String packageName = ctx.getPackageName();
int resId = ctx.getResources().getIdentifier(SUPPORT_IMPL_RES_KEY, "string", packageName);
String className = ctx.getResources().getString(resId);
Class<? extends TestSupport> cl = Class.forName(className).asSubclass(TestSupport.class);
mTestSupport = cl.newInstance();
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.support.test.filters.SmallTest;
import org.chromium.net.UrlRequest;
/**
* HTTP2 Tests.
*/
public class Http2Test extends NativeCronetTestCase {
private TestSupport.TestServer mServer;
@Override
protected void setUp() throws Exception {
super.setUp();
mServer = mTestSupport.createTestServer(getContext(), TestSupport.Protocol.HTTP2);
}
@Override
protected void tearDown() throws Exception {
mServer.shutdown();
super.tearDown();
}
// Test that HTTP/2 is enabled by default but QUIC is not.
@SmallTest
public void testHttp2() throws Exception {
mTestSupport.installMockCertVerifierForTesting(mCronetEngineBuilder);
initCronetEngine();
assertTrue(mServer.start());
SmokeTestRequestCallback callback = new SmokeTestRequestCallback();
UrlRequest.Builder requestBuilder = mCronetEngine.newUrlRequestBuilder(
mServer.getSuccessURL(), callback, callback.getExecutor());
requestBuilder.build().start();
callback.blockForDone();
assertSuccessfulNonEmptyResponse(callback, mServer.getSuccessURL());
assertEquals("h2", callback.getResponseInfo().getNegotiatedProtocol());
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.os.ConditionVariable;
import android.util.Log;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.CharsetUtil;
/**
* A simple HTTP server for testing.
*/
public class HttpTestServer implements TestSupport.TestServer {
private static final String TAG = "HttpTestServer";
private static final String HOST = "127.0.0.1";
private static final int PORT = 8080;
private Channel mServerChannel;
private ConditionVariable mStartBlock = new ConditionVariable();
private ConditionVariable mShutdownBlock = new ConditionVariable();
@Override
public boolean start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpTestServer.this.run();
} catch (Exception e) {
Log.e(TAG, "Unable to start HttpTestServer", e);
}
}
}).start();
// Return false if the server cannot start within 5 seconds.
return mStartBlock.block(5000);
}
@Override
public void shutdown() {
if (mServerChannel != null) {
mServerChannel.close();
boolean success = mShutdownBlock.block(10000);
if (!success) {
Log.e(TAG, "Unable to shutdown the server. Is it already dead?");
}
mServerChannel = null;
}
}
@Override
public String getSuccessURL() {
return getServerUrl() + "/success";
}
private String getServerUrl() {
return "http://" + HOST + ":" + PORT;
}
private void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(4);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpRequestDecoder());
p.addLast(new HttpResponseEncoder());
p.addLast(new TestServerHandler());
}
});
// Start listening fo incoming connections.
mServerChannel = b.bind(PORT).sync().channel();
mStartBlock.open();
// Block until the channel is closed.
mServerChannel.closeFuture().sync();
mShutdownBlock.open();
Log.i(TAG, "HttpServer stopped");
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
private static class TestServerHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, OK, Unpooled.copiedBuffer("Hello!", CharsetUtil.UTF_8));
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.support.test.filters.SmallTest;
import org.chromium.net.CronetEngine;
import org.chromium.net.ExperimentalCronetEngine;
/**
* Tests scenarios when the native shared library file is missing in the APK or was built for a
* wrong architecture.
*/
public class MissingNativeLibraryTest extends CronetSmokeTestCase {
/**
* If the ".so" file is missing, instantiating the Cronet engine should throw an exception.
*/
@SmallTest
public void testExceptionWhenSoFileIsAbsent() throws Exception {
ExperimentalCronetEngine.Builder builder =
new ExperimentalCronetEngine.Builder(getContext());
try {
builder.build();
fail("Expected exception since the shared library '.so' file is absent");
} catch (Throwable t) {
// Find the root cause.
while (t.getCause() != null) {
t = t.getCause();
}
assertEquals(UnsatisfiedLinkError.class, t.getClass());
}
}
/**
* Tests the enableLegacyMode API that allows the embedder to force JavaCronetEngine
* instantiation even when the native Cronet engine implementation is available.
*/
@SmallTest
public void testEnableLegacyMode() throws Exception {
ExperimentalCronetEngine.Builder builder =
new ExperimentalCronetEngine.Builder(getContext());
builder.enableLegacyMode(true);
CronetEngine engine = builder.build();
assertJavaEngine(engine);
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import org.chromium.base.ContextUtils;
import org.chromium.base.PathUtils;
import java.io.File;
/**
* Test base class for testing native Engine implementation. This class can import classes from the
* org.chromium.base package.
*/
public class NativeCronetTestCase extends CronetSmokeTestCase {
private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "cronet_test";
private static final String LOGFILE_NAME = "cronet-netlog.json";
@Override
protected void setUp() throws Exception {
super.setUp();
ContextUtils.initApplicationContext(getContext().getApplicationContext());
PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
mTestSupport.loadTestNativeLibrary();
}
@Override
protected void tearDown() throws Exception {
stopAndSaveNetLog();
super.tearDown();
}
@Override
protected void initCronetEngine() {
super.initCronetEngine();
assertNativeEngine(mCronetEngine);
startNetLog();
}
private void startNetLog() {
if (mCronetEngine != null) {
mCronetEngine.startNetLogToFile(
PathUtils.getDataDirectory() + "/" + LOGFILE_NAME, false);
}
}
private void stopAndSaveNetLog() {
if (mCronetEngine == null) return;
mCronetEngine.stopNetLog();
File netLogFile = new File(PathUtils.getDataDirectory(), LOGFILE_NAME);
if (!netLogFile.exists()) return;
mTestSupport.processNetLog(getContext(), netLogFile);
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.support.test.filters.SmallTest;
import org.chromium.net.UrlRequest;
/**
* Tests scenario when an app doesn't contain the native Cronet implementation.
*/
public class PlatformOnlyEngineTest extends CronetSmokeTestCase {
private String mURL;
private TestSupport.TestServer mServer;
@Override
protected void setUp() throws Exception {
super.setUp();
// Java-only implementation of the Cronet engine only supports Http/1 protocol.
mServer = mTestSupport.createTestServer(getContext(), TestSupport.Protocol.HTTP1);
assertTrue(mServer.start());
mURL = mServer.getSuccessURL();
}
@Override
protected void tearDown() throws Exception {
mServer.shutdown();
super.tearDown();
}
/**
* Test a successful response when a request is sent by the Java Cronet Engine.
*/
@SmallTest
public void testSuccessfulResponse() {
initCronetEngine();
assertJavaEngine(mCronetEngine);
SmokeTestRequestCallback callback = new SmokeTestRequestCallback();
UrlRequest.Builder requestBuilder =
mCronetEngine.newUrlRequestBuilder(mURL, callback, callback.getExecutor());
requestBuilder.build().start();
callback.blockForDone();
assertSuccessfulNonEmptyResponse(callback, mURL);
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.support.test.filters.SmallTest;
import org.json.JSONObject;
import static org.chromium.net.smoke.TestSupport.Protocol.QUIC;
import org.chromium.net.UrlRequest;
import java.net.URL;
/**
* QUIC Tests.
*/
public class QuicTest extends NativeCronetTestCase {
private TestSupport.TestServer mServer;
@Override
protected void setUp() throws Exception {
super.setUp();
mServer = mTestSupport.createTestServer(getContext(), QUIC);
}
@Override
protected void tearDown() throws Exception {
mServer.shutdown();
super.tearDown();
}
@SmallTest
public void testQuic() throws Exception {
assertTrue(mServer.start());
final String urlString = mServer.getSuccessURL();
final URL url = new URL(urlString);
mCronetEngineBuilder.enableQuic(true);
mCronetEngineBuilder.addQuicHint(url.getHost(), url.getPort(), url.getPort());
mTestSupport.installMockCertVerifierForTesting(mCronetEngineBuilder);
JSONObject quicParams = new JSONObject().put("delay_tcp_race", true);
JSONObject experimentalOptions = new JSONObject().put("QUIC", quicParams);
mTestSupport.addHostResolverRules(experimentalOptions);
experimentalOptions.put("host_whitelist", url.getHost());
mCronetEngineBuilder.setExperimentalOptions(experimentalOptions.toString());
initCronetEngine();
// QUIC is not guaranteed to win the race even with |delay_tcp_race| set, so try
// multiple times.
boolean quicNegotiated = false;
for (int i = 0; i < 5; i++) {
SmokeTestRequestCallback callback = new SmokeTestRequestCallback();
UrlRequest.Builder requestBuilder =
mCronetEngine.newUrlRequestBuilder(urlString, callback, callback.getExecutor());
requestBuilder.build().start();
callback.blockForDone();
assertSuccessfulNonEmptyResponse(callback, urlString);
if (callback.getResponseInfo().getNegotiatedProtocol().startsWith("http/2+quic/")) {
quicNegotiated = true;
break;
}
}
assertTrue(quicNegotiated);
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import static junit.framework.Assert.assertTrue;
import android.os.ConditionVariable;
import org.chromium.net.CronetException;
import org.chromium.net.UrlRequest;
import org.chromium.net.UrlResponseInfo;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* A simple boilerplate implementation of {@link UrlRequest.Callback} that is used by smoke tests.
*/
class SmokeTestRequestCallback extends UrlRequest.Callback {
private static final int READ_BUFFER_SIZE = 10000;
// An executor that is used to execute {@link UrlRequest.Callback UrlRequest callbacks}.
private ExecutorService mExecutor = Executors.newSingleThreadExecutor();
// Signals when the request is done either successfully or not.
private final ConditionVariable mDone = new ConditionVariable();
// The state of the request.
public enum State { NotSet, Succeeded, Failed, Canceled }
// The current state of the request.
private State mState = State.NotSet;
// Response info of the finished request.
private UrlResponseInfo mResponseInfo;
// Holds an error if the request failed.
private CronetException mError;
@Override
public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl)
throws Exception {
request.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) throws Exception {
request.read(ByteBuffer.allocateDirect(READ_BUFFER_SIZE));
}
@Override
public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer)
throws Exception {
request.read(ByteBuffer.allocateDirect(READ_BUFFER_SIZE));
}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
done(State.Succeeded, info);
}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
mError = error;
done(State.Failed, info);
}
@Override
public void onCanceled(UrlRequest request, UrlResponseInfo info) {
done(State.Canceled, info);
}
/**
* Returns the request executor.
*
* @return the executor.
*/
public Executor getExecutor() {
return mExecutor;
}
/**
* Blocks until the request is either succeeded, failed or canceled.
*/
public void blockForDone() {
mDone.block();
}
/**
* Returns the final state of the request.
*
* @return the state.
*/
public State getFinalState() {
return mState;
}
/**
* Returns an error that was passed to {@link #onFailed} when the request failed.
*
* @return the error if the request failed; {@code null} otherwise.
*/
public CronetException getFailureError() {
return mError;
}
/**
* Returns {@link UrlResponseInfo} of the finished response.
*
* @return the response info. {@code null} if the request hasn't completed yet.
*/
public UrlResponseInfo getResponseInfo() {
return mResponseInfo;
}
private void done(State finalState, UrlResponseInfo responseInfo) {
assertTrue(mState == State.NotSet);
mResponseInfo = responseInfo;
mState = finalState;
mDone.open();
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.smoke;
import android.content.Context;
import org.json.JSONObject;
import org.chromium.net.ExperimentalCronetEngine;
import java.io.File;
/**
* Provides support for tests, so they can be run in different environments against different
* servers. It contains methods, which behavior can be different in different testing environments.
* The concrete implementation of this interface is determined dynamically at runtime by reading
* the value of |TestSupportImplClass| from the Android string resource file.
*/
public interface TestSupport {
enum Protocol {
HTTP1,
HTTP2,
QUIC,
}
/**
* Creates a new test server that supports a given {@code protocol}.
*
* @param context context.
* @param protocol protocol that should be supported by the server.
* @return an instance of the server.
*
* @throws UnsupportedOperationException if the implementation of this interface
* does not support a given {@code protocol}.
*/
TestServer createTestServer(Context context, Protocol protocol);
/**
* This method is called at the end of a test run if the netlog is available. An implementer
* of {@link TestSupport} can use it to process the result netlog; e.g., to copy the netlog
* to a directory where all test logs are collected. This method is optional and can be no-op.
*
* @param file the netlog file.
*/
void processNetLog(Context context, File file);
/**
* Adds host resolver rules to a given experimental option JSON file.
* This method is optional.
*
* @param experimentalOptionsJson experimental options.
*/
void addHostResolverRules(JSONObject experimentalOptionsJson);
/**
* Installs mock certificate verifier for a given {@code builder}.
* This method is optional.
*
* @param builder that should have the verifier installed.
*/
void installMockCertVerifierForTesting(ExperimentalCronetEngine.Builder builder);
/**
* Loads a native library that is required for testing if any required.
*/
void loadTestNativeLibrary();
/**
* A test server.
*/
interface TestServer {
/**
* Starts the server.
*
* @return true if the server started successfully.
*/
boolean start();
/**
* Shuts down the server.
*/
void shutdown();
/**
* Return a URL that can be used by the test code to receive a successful response.
*
* @return the URL as a string.
*/
String getSuccessURL();
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment