Commit 20960cee authored by Ben Kirchman's avatar Ben Kirchman Committed by Commit Bot

Create FakeCronetEngine with Provider and Builder classes and associated tests.

FakeCronetEngine should be used in tests to test apps' uses of CronetEngine.

Design Doc: https://docs.google.com/document/d/1IsBwiqR5f6ksvmHPq5skV802C02We8zjK-c552Tp4lE/edit#heading=h.7nki9mck5t64

Bug: 669707
Change-Id: Ide40204e9d0942cc2fc5361f42cefcf56b405fd5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1680733
Commit-Queue: Benjamin Kirchman <kirchman@google.com>
Reviewed-by: default avatarPaul Jensen <pauljensen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#679958}
parent 8436c831
...@@ -339,6 +339,8 @@ android_library("cronet_impl_platform_base_java") { ...@@ -339,6 +339,8 @@ android_library("cronet_impl_platform_base_java") {
android_library("cronet_impl_fake_base_java") { android_library("cronet_impl_fake_base_java") {
java_files = [ java_files = [
"fake/java/org/chromium/net/test/FakeUrlResponse.java", "fake/java/org/chromium/net/test/FakeUrlResponse.java",
"fake/java/org/chromium/net/test/FakeCronetEngine.java",
"fake/java/org/chromium/net/test/FakeCronetProvider.java",
"fake/java/org/chromium/net/test/ResponseMatcher.java", "fake/java/org/chromium/net/test/ResponseMatcher.java",
"fake/java/org/chromium/net/test/UrlResponseMatcher.java", "fake/java/org/chromium/net/test/UrlResponseMatcher.java",
] ]
...@@ -354,6 +356,8 @@ android_library("cronet_fake_javatests") { ...@@ -354,6 +356,8 @@ android_library("cronet_fake_javatests") {
testonly = true testonly = true
java_files = [ java_files = [
"fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java", "fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java",
"fake/javatests/org/chromium/net/test/FakeCronetProviderTest.java",
"fake/javatests/org/chromium/net/test/FakeCronetEngineTest.java",
"fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java", "fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java",
] ]
......
// Copyright 2019 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.test;
import android.content.Context;
import android.support.annotation.GuardedBy;
import org.chromium.net.BidirectionalStream;
import org.chromium.net.CronetEngine;
import org.chromium.net.ExperimentalBidirectionalStream;
import org.chromium.net.NetworkQualityRttListener;
import org.chromium.net.NetworkQualityThroughputListener;
import org.chromium.net.RequestFinishedInfo;
import org.chromium.net.UrlRequest;
import org.chromium.net.impl.CronetEngineBase;
import org.chromium.net.impl.CronetEngineBuilderImpl;
import org.chromium.net.impl.ImplVersion;
import org.chromium.net.impl.UrlRequestBase;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandlerFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Fake {@link CronetEngine}. This implements CronetEngine.
*/
final class FakeCronetEngine extends CronetEngineBase {
/**
* Builds a {@link FakeCronetEngine}. This implements CronetEngine.Builder.
*/
static class Builder extends CronetEngineBuilderImpl {
/**
* Builder for {@link FakeCronetEngine}.
*
* @param context Android {@link Context}.
*/
Builder(Context context) {
super(context);
}
@Override
public FakeCronetEngine build() {
return new FakeCronetEngine(this);
}
}
private final Object mLock = new Object();
@GuardedBy("mLock")
private boolean mIsShutdown;
@GuardedBy("mLock")
private int mActiveRequestCount;
/**
* Creates a {@link FakeCronetEngine}. Used when {@link FakeCronetEngine} is created with the
* {@link FakeCronetEngine.Builder}.
*
* @param builder a {@link CronetEngineBuilderImpl} to build this {@link CronetEngine}
* implementation from.
*/
private FakeCronetEngine(CronetEngineBuilderImpl builder) {
// TODO(kirchman): Retrieve fields from the builder necessary for a URL request.
}
@Override
public ExperimentalBidirectionalStream.Builder newBidirectionalStreamBuilder(
String url, BidirectionalStream.Callback callback, Executor executor) {
synchronized (mLock) {
if (mIsShutdown) {
throw new IllegalStateException(
"This instance of CronetEngine has been shutdown and can no longer be "
+ "used.");
}
throw new UnsupportedOperationException(
"The bidirectional stream API is not supported by the Fake implementation "
+ "of CronetEngine.");
}
}
@Override
public String getVersionString() {
return "FakeCronet/" + ImplVersion.getCronetVersionWithLastChange();
}
@Override
public void shutdown() {
synchronized (mLock) {
if (mActiveRequestCount != 0) {
throw new IllegalStateException("Cannot shutdown with active requests.");
} else {
mIsShutdown = true;
}
}
}
@Override
public void startNetLogToFile(String fileName, boolean logAll) {}
@Override
public void startNetLogToDisk(String dirPath, boolean logAll, int maxSize) {}
@Override
public void stopNetLog() {}
@Override
public byte[] getGlobalMetricsDeltas() {
return new byte[0];
}
@Override
public int getEffectiveConnectionType() {
return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
}
@Override
public int getHttpRttMs() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public int getTransportRttMs() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public int getDownstreamThroughputKbps() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public void configureNetworkQualityEstimatorForTesting(boolean useLocalHostRequests,
boolean useSmallerResponses, boolean disableOfflineCheck) {}
@Override
public void addRttListener(NetworkQualityRttListener listener) {}
@Override
public void removeRttListener(NetworkQualityRttListener listener) {}
@Override
public void addThroughputListener(NetworkQualityThroughputListener listener) {}
@Override
public void removeThroughputListener(NetworkQualityThroughputListener listener) {}
@Override
public void addRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
@Override
public void removeRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
// TODO(crbug.com/669707) Instantiate a fake CronetHttpUrlConnection wrapping a FakeUrlRequest
// here.
@Override
public URLConnection openConnection(URL url) throws IOException {
throw new UnsupportedOperationException(
"The openConnection API is not supported by the Fake implementation of "
+ "CronetEngine.");
}
@Override
public URLConnection openConnection(URL url, Proxy proxy) throws IOException {
throw new UnsupportedOperationException(
"The openConnection API is not supported by the Fake implementation of "
+ "CronetEngine.");
}
@Override
public URLStreamHandlerFactory createURLStreamHandlerFactory() {
throw new UnsupportedOperationException(
"The URLStreamHandlerFactory API is not supported by the Fake implementation of "
+ "CronetEngine.");
}
@Override
protected UrlRequestBase createRequest(String url, UrlRequest.Callback callback,
Executor executor, int priority, Collection<Object> connectionAnnotations,
boolean disableCache, boolean disableConnectionMigration, boolean allowDirectExecutor,
boolean trafficStatsTagSet, int trafficStatsTag, boolean trafficStatsUidSet,
int trafficStatsUid, RequestFinishedInfo.Listener requestFinishedListener) {
synchronized (mLock) {
if (mIsShutdown) {
throw new IllegalStateException(
"This instance of CronetEngine has been shutdown and can no longer be "
+ "used.");
}
// TODO(kirchman): Implement FakeUrlRequest.
throw new UnsupportedOperationException(
"The UrlRequest API is not supported by the Fake implementation of "
+ "CronetEngine.");
}
}
@Override
protected ExperimentalBidirectionalStream createBidirectionalStream(String url,
BidirectionalStream.Callback callback, Executor executor, String httpMethod,
List<Map.Entry<String, String>> requestHeaders, @StreamPriority int priority,
boolean delayRequestHeadersUntilFirstFlush, Collection<Object> connectionAnnotations,
boolean trafficStatsTagSet, int trafficStatsTag, boolean trafficStatsUidSet,
int trafficStatsUid) {
synchronized (mLock) {
if (mIsShutdown) {
throw new IllegalStateException(
"This instance of CronetEngine has been shutdown and can no longer be "
+ "used.");
}
throw new UnsupportedOperationException(
"The BidirectionalStream API is not supported by the Fake implementation of "
+ "CronetEngine.");
}
}
/**
* Mark request as started to prevent shutdown when there are active
* requests, only if the engine is not shutdown.
*
* @return true if the engine is not shutdown and the request is marked as started.
*/
boolean startRequest() {
synchronized (mLock) {
if (!mIsShutdown) {
mActiveRequestCount++;
return true;
}
return false;
}
}
/**
* Mark request as finished to allow shutdown when there are no active
* requests.
*/
void onRequestDestroyed() {
synchronized (mLock) {
// Sanity check. We should not be able to shutdown if there are still running requests.
if (mIsShutdown) {
throw new IllegalStateException(
"This instance of CronetEngine was shutdown. All requests must have been "
+ "complete.");
}
mActiveRequestCount--;
}
}
}
// Copyright 2019 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.test;
import android.content.Context;
import org.chromium.net.CronetEngine;
import org.chromium.net.CronetProvider;
import org.chromium.net.ExperimentalCronetEngine;
import org.chromium.net.ICronetEngineBuilder;
import org.chromium.net.impl.ImplVersion;
import java.util.Arrays;
/**
* Implementation of {@link CronetProvider} that creates {@link CronetEngine.Builder}
* for building the Fake implementation of {@link CronetEngine}.
* {@hide}
*/
public class FakeCronetProvider extends CronetProvider {
/**
* String returned by {@link CronetProvider#getName} for {@link CronetProvider}
* that provides the fake Cronet implementation.
*/
public static final String PROVIDER_NAME_FAKE = "Fake-Cronet-Provider";
/**
* Constructs a {@link FakeCronetProvider}.
*
* @param context Android context to use
*/
public FakeCronetProvider(Context context) {
super(context);
}
@Override
public CronetEngine.Builder createBuilder() {
ICronetEngineBuilder impl = new FakeCronetEngine.Builder(mContext);
return new ExperimentalCronetEngine.Builder(impl);
}
@Override
public String getName() {
return PROVIDER_NAME_FAKE;
}
@Override
public String getVersion() {
return ImplVersion.getCronetVersion();
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] {FakeCronetProvider.class, mContext});
}
@Override
public boolean equals(Object other) {
return other == this
|| (other instanceof FakeCronetProvider
&& this.mContext.equals(((FakeCronetProvider) other).mContext));
}
}
// Copyright 2019 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.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.net.CronetException;
import org.chromium.net.UrlRequest;
import org.chromium.net.UrlResponseInfo;
import org.chromium.net.impl.ImplVersion;
import java.net.Proxy;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Test functionality of {@link FakeCronetEngine}.
*/
@RunWith(AndroidJUnit4.class)
public class FakeCronetEngineTest {
Context mContext;
FakeCronetEngine mFakeCronetEngine;
UrlRequest.Callback mCallback;
ExecutorService mExecutor;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mFakeCronetEngine =
(FakeCronetEngine) new FakeCronetProvider(mContext).createBuilder().build();
mCallback = new UrlRequest.Callback() {
@Override
public void onRedirectReceived(
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {}
@Override
public void onReadCompleted(
UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {}
@Override
public void onCanceled(UrlRequest request, UrlResponseInfo info) {}
};
mExecutor = Executors.newSingleThreadExecutor();
}
@Test
@SmallTest
public void testShutdownEngineThrowsExceptionWhenApiCalled() {
mFakeCronetEngine.shutdown();
try {
mFakeCronetEngine.newUrlRequestBuilder("", mCallback, mExecutor).build();
fail("newUrlRequestBuilder API not checked for shutdown engine.");
} catch (IllegalStateException e) {
assertEquals(
"This instance of CronetEngine has been shutdown and can no longer be used.",
e.getMessage());
}
}
@Test
@SmallTest
public void testShutdownEngineThrowsExceptionWhenBidirectionalStreamApiCalled() {
mFakeCronetEngine.shutdown();
try {
mFakeCronetEngine.newBidirectionalStreamBuilder("", null, null);
fail("newBidirectionalStreamBuilder API not checked for shutdown engine.");
} catch (IllegalStateException e) {
assertEquals(
"This instance of CronetEngine has been shutdown and can no longer be used.",
e.getMessage());
}
}
@Test
@SmallTest
public void testExceptionForNewBidirectionalStreamApi() {
try {
mFakeCronetEngine.newBidirectionalStreamBuilder("", null, null);
fail("newBidirectionalStreamBuilder API should not be available.");
} catch (UnsupportedOperationException e) {
assertEquals("The bidirectional stream API is not supported by the Fake implementation "
+ "of CronetEngine.",
e.getMessage());
}
}
@Test
@SmallTest
public void testExceptionForNewUrlRequestBuilderApi() {
try {
mFakeCronetEngine.newUrlRequestBuilder("", mCallback, mExecutor).build();
fail("newUrlRequestBuilder API should not be available.");
} catch (UnsupportedOperationException e) {
assertEquals("The UrlRequest API is not supported by the Fake implementation "
+ "of CronetEngine.",
e.getMessage());
}
}
@Test
@SmallTest
public void testExceptionForOpenConnectionApi() {
try {
mFakeCronetEngine.openConnection(null);
fail("openConnection API should not be available.");
} catch (Exception e) {
assertEquals("The openConnection API is not supported by the Fake implementation of "
+ "CronetEngine.",
e.getMessage());
}
}
@Test
@SmallTest
public void testExceptionForOpenConnectionApiWithProxy() {
try {
mFakeCronetEngine.openConnection(null, Proxy.NO_PROXY);
fail("openConnection API should not be available.");
} catch (Exception e) {
assertEquals("The openConnection API is not supported by the Fake implementation of "
+ "CronetEngine.",
e.getMessage());
}
}
@Test
@SmallTest
public void testExceptionForCreateStreamHandlerFactoryApi() {
try {
mFakeCronetEngine.createURLStreamHandlerFactory();
fail("createURLStreamHandlerFactory API should not be available.");
} catch (UnsupportedOperationException e) {
assertEquals(
"The URLStreamHandlerFactory API is not supported by the Fake implementation of"
+ " CronetEngine.",
e.getMessage());
}
}
@Test
@SmallTest
public void testGetVersionString() {
assertEquals("FakeCronet/" + ImplVersion.getCronetVersionWithLastChange(),
mFakeCronetEngine.getVersionString());
}
@Test
@SmallTest
public void testStartNetLogToFile() {
mFakeCronetEngine.startNetLogToFile("", false);
}
@Test
@SmallTest
public void testStartNetLogToDisk() {
mFakeCronetEngine.startNetLogToDisk("", false, 0);
}
@Test
@SmallTest
public void testStopNetLog() {
mFakeCronetEngine.stopNetLog();
}
@Test
@SmallTest
public void testGetGlobalMetricsDeltas() {
assertTrue(mFakeCronetEngine.getGlobalMetricsDeltas().length == 0);
}
@Test
@SmallTest
public void testGetEffectiveConnectionType() {
assertEquals(FakeCronetEngine.EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
mFakeCronetEngine.getEffectiveConnectionType());
}
@Test
@SmallTest
public void testGetHttpRttMs() {
assertEquals(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN, mFakeCronetEngine.getHttpRttMs());
}
@Test
@SmallTest
public void testGetTransportRttMs() {
assertEquals(
FakeCronetEngine.CONNECTION_METRIC_UNKNOWN, mFakeCronetEngine.getTransportRttMs());
}
@Test
@SmallTest
public void testGetDownstreamThroughputKbps() {
assertEquals(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN,
mFakeCronetEngine.getDownstreamThroughputKbps());
}
@Test
@SmallTest
public void testConfigureNetworkQualityEstimatorForTesting() {
mFakeCronetEngine.configureNetworkQualityEstimatorForTesting(false, false, false);
}
@Test
@SmallTest
public void testAddRttListener() {
mFakeCronetEngine.addRttListener(null);
}
@Test
@SmallTest
public void testRemoveRttListener() {
mFakeCronetEngine.removeRttListener(null);
}
@Test
@SmallTest
public void testAddThroughputListener() {
mFakeCronetEngine.addThroughputListener(null);
}
@Test
@SmallTest
public void testRemoveThroughputListener() {
mFakeCronetEngine.removeThroughputListener(null);
}
@Test
@SmallTest
public void testAddRequestFinishedListener() {
mFakeCronetEngine.addRequestFinishedListener(null);
}
@Test
@SmallTest
public void testRemoveRequestFinishedListener() {
mFakeCronetEngine.removeRequestFinishedListener(null);
}
@Test
@SmallTest
public void testShutdownBlockedWhenRequestCountNotZero() {
// Start a request and verify the engine can't be shutdown.
assertTrue(mFakeCronetEngine.startRequest());
try {
mFakeCronetEngine.shutdown();
fail("Shutdown not checked for active requests.");
} catch (IllegalStateException e) {
assertEquals("Cannot shutdown with active requests.", e.getMessage());
}
// Finish the request and verify the engine can be shutdown.
mFakeCronetEngine.onRequestDestroyed();
mFakeCronetEngine.shutdown();
}
@Test
@SmallTest
public void testCantStartRequestAfterEngineShutdown() {
mFakeCronetEngine.shutdown();
assertFalse(mFakeCronetEngine.startRequest());
}
@Test
@SmallTest
public void testCantDecrementOnceShutdown() {
mFakeCronetEngine.shutdown();
try {
mFakeCronetEngine.onRequestDestroyed();
fail("onRequestDestroyed not checked for shutdown engine");
} catch (IllegalStateException e) {
assertEquals("This instance of CronetEngine was shutdown. All requests must have been "
+ "complete.",
e.getMessage());
}
}
}
// Copyright 2019 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.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.net.impl.ImplVersion;
/**
* Test functionality of {@link FakeCronetProvider}.
*/
@RunWith(AndroidJUnit4.class)
public class FakeCronetProviderTest {
Context mContext;
FakeCronetProvider mProvider;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mProvider = new FakeCronetProvider(mContext);
}
@Test
@SmallTest
public void testGetName() {
String expectedName = "Fake-Cronet-Provider";
assertEquals(expectedName, mProvider.getName());
}
@Test
@SmallTest
public void testGetVersion() {
assertEquals(ImplVersion.getCronetVersion(), mProvider.getVersion());
}
@Test
@SmallTest
public void testIsEnabled() {
assertTrue(mProvider.isEnabled());
}
@Test
@SmallTest
public void testHashCode() {
FakeCronetProvider otherProvider = new FakeCronetProvider(mContext);
assertEquals(otherProvider.hashCode(), mProvider.hashCode());
}
@Test
@SmallTest
public void testEquals() {
assertTrue(mProvider.equals(new FakeCronetProvider(mContext)));
}
}
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