Chromium Code Reviews| Index: components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/CronetUrlRequestTest.java |
| diff --git a/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/CronetUrlRequestTest.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1f73fe206a91dd2c852a80a162e2b5d8d48b59b0 |
| --- /dev/null |
| +++ b/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/CronetUrlRequestTest.java |
| @@ -0,0 +1,473 @@ |
| +// Copyright 2014 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.cronet_test_apk; |
| + |
| +import android.os.ConditionVariable; |
| + |
| +import android.test.suitebuilder.annotation.SmallTest; |
| + |
| +import org.chromium.base.test.util.Feature; |
| +import org.chromium.net.ExtendedResponseInfo; |
| +import org.chromium.net.ResponseInfo; |
| +import org.chromium.net.UrlRequest; |
| +import org.chromium.net.UrlRequestException; |
| +import org.chromium.net.UrlRequestListener; |
| + |
| +import java.net.URL; |
| +import java.nio.ByteBuffer; |
| +import java.util.concurrent.ExecutorService; |
| +import java.util.concurrent.Executors; |
| + |
| +/** |
| + * Test basic functionality of UrlRequest. |
| + */ |
| +public class CronetUrlRequestTest extends CronetTestBase { |
| + // URL used for base tests. |
| + private static final String TEST_URL = "http://127.0.0.1:8000"; |
| + private static final String MOCK_SUCCESS_PATH = "success.txt"; |
| + |
| + private static final String MOCK_CRONET_TEST_SUCCESS_URL = |
| + "http://mock.http/success.txt"; |
| + private static final String MOCK_CRONET_TEST_REDIRECT_URL = |
| + "http://mock.http/redirect.html"; |
| + private static final String MOCK_CRONET_TEST_NOTFOUND_URL = |
| + "http://mock.http/notfound.html"; |
| + private static final String MOCK_CRONET_TEST_FAILED_URL = |
| + "http://mock.failed.request/-2"; |
| + |
| + // Executor for Cronet callbacks. |
| + ExecutorService mExecutor = Executors.newSingleThreadExecutor(); |
|
mmenke
2014/10/24 16:34:36
Can just use this as an Executor - don't think we
mef
2014/10/24 19:57:55
Not really, as I'm using ExecutorService shutdown
|
| + CronetTestActivity mActivity; |
| + |
| + private enum LastCalled { |
| + Nothing, |
| + OnRedirect, |
| + OnResponseStarted, |
| + OnDataReceived, |
| + OnComplete |
| + }; |
| + |
| + private enum FailureType { |
| + None, |
| + CancelSync, |
| + CancelAsync, |
| + ThrowSync |
| + }; |
| + |
| + class SimpleUrlRequestListener implements UrlRequestListener { |
| + |
|
mmenke
2014/10/24 16:34:36
nit: Blank line not needed.
mef
2014/10/27 21:00:03
Done.
|
| + protected ConditionVariable mComplete = new ConditionVariable(); |
|
mmenke
2014/10/24 16:34:36
Optional: Should we get in the habit of putting l
mmenke
2014/10/24 16:34:36
optional: Suggest making this private, and adding
mef
2014/10/27 21:00:03
Done.
mef
2014/10/27 21:00:03
Done.
|
| + |
| + public ResponseInfo mResponseInfo; |
| + public UrlRequestException mError; |
| + |
| + public int mHttpResponseDataLength = 0; |
| + |
| + public LastCalled mLastCalled = LastCalled.Nothing; |
| + |
| + public boolean mOnRedirectCalled = false; |
| + public boolean mOnErrorCalled = false; |
| + |
| + public byte[] mLastDataReceivedAsBytes; |
| + public String mResponseAsString = ""; |
| + |
| + public SimpleUrlRequestListener() { |
| + } |
| + |
| + @Override |
| + public void onRedirect(UrlRequest request, |
| + ResponseInfo info, |
| + URL newLocation) { |
| + assertEquals(LastCalled.Nothing, mLastCalled); |
|
mmenke
2014/10/24 16:34:36
Can have multiple redirects, though we aren't curr
mef
2014/10/27 21:00:03
Done.
|
| + mLastCalled = LastCalled.OnRedirect; |
| + mOnRedirectCalled = true; |
|
mmenke
2014/10/24 16:34:36
We should store info somewhere, so we can check th
mef
2014/10/27 21:00:03
Done.
|
| + } |
| + |
| + @Override |
| + public void onResponseStarted(UrlRequest request, ResponseInfo info) { |
| + assertTrue(mLastCalled == LastCalled.Nothing || |
| + mLastCalled == LastCalled.OnRedirect); |
| + mLastCalled = LastCalled.OnResponseStarted; |
| + mResponseInfo = info; |
| + } |
| + |
| + @Override |
| + public void onDataReceived(UrlRequest request, |
| + ResponseInfo info, |
| + ByteBuffer byteBuffer) { |
| + assertTrue(mLastCalled == LastCalled.OnResponseStarted || |
| + mLastCalled == LastCalled.OnDataReceived); |
|
mmenke
2014/10/24 16:34:36
What happens if these assertions fail? Do we just
mef
2014/10/27 21:00:02
I don't think they just throw.
|
| + mLastCalled = LastCalled.OnDataReceived; |
| + |
| + mHttpResponseDataLength += byteBuffer.capacity(); |
| + mLastDataReceivedAsBytes = new byte[byteBuffer.capacity()]; |
| + byteBuffer.get(mLastDataReceivedAsBytes); |
| + mResponseAsString += new String(mLastDataReceivedAsBytes); |
| + } |
| + |
| + @Override |
| + public void onComplete(UrlRequest request, ExtendedResponseInfo info) { |
|
mmenke
2014/10/24 16:34:36
Should also store the ExtendedResponseInfo.
mef
2014/10/27 21:00:03
Done.
|
| + assertTrue(mLastCalled == LastCalled.OnResponseStarted || |
| + mLastCalled == LastCalled.OnDataReceived); |
|
mmenke
2014/10/24 16:34:36
Hrm...Perhaps we should catch onComplete exception
mef
2014/10/27 21:00:03
Um, with your example, embedder could've cleaned s
|
| + mLastCalled = LastCalled.OnComplete; |
| + mComplete.open(); |
| + } |
| + |
| + @Override |
| + public void onError(UrlRequest request, |
| + ResponseInfo info, |
| + UrlRequestException error) { |
| + mOnErrorCalled = true; |
| + mError = error; |
| + mComplete.open(); |
| + } |
| + |
| + public void blockForComplete() { |
| + mComplete.block(); |
| + } |
| + } |
| + |
| + @Override |
| + protected void setUp() throws Exception { |
| + super.setUp(); |
| + mActivity = launchCronetTestApp(); |
| + // Make sure the activity was created as expected. |
| + waitForActiveShellToBeDoneLoading(); |
| + assertTrue(UploadTestServer.startUploadTestServer()); |
| + // AddUrlInterceptors() after native application context is initialized. |
| + MockUrlRequestJobUtil.addUrlInterceptors(); |
| + } |
| + |
| + public SimpleUrlRequestListener startAndWaitForComplete(String url) |
| + throws Exception { |
| + SimpleUrlRequestListener listener = new SimpleUrlRequestListener(); |
| + // Create request. |
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + url, listener, mExecutor); |
| + urlRequest.start(); |
| + listener.blockForComplete(); |
| + return listener; |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testSimpleGet() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + UploadTestServer.getEchoMethodURL()); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
|
mmenke
2014/10/24 16:34:36
Should check all other methods of mResponseInfo he
mef
2014/10/27 21:00:02
Done.
|
| + // Default method is 'GET'. |
| + assertEquals("GET", listener.mResponseAsString); |
| + assertFalse(listener.mOnRedirectCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnComplete); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testSetHttpMethod() throws Exception { |
| + SimpleUrlRequestListener listener = new SimpleUrlRequestListener(); |
| + String methodName = "head"; |
|
mmenke
2014/10/24 16:34:36
Using head here is weird (HEAD requests don't typi
mef
2014/10/24 19:57:55
I've tried that, but our http server doesn't know
mmenke
2014/10/24 20:16:22
If you capitalized HEAD, do you get an empty body?
mef
2014/10/27 21:00:03
Yes, Done
|
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + UploadTestServer.getEchoMethodURL(), listener, |
| + mExecutor); |
| + // Try to set 'null' method. |
| + try { |
| + urlRequest.setHttpMethod(null); |
| + fail("Exception not thrown"); |
| + } catch (NullPointerException e) { |
| + assertEquals("Method is required.", e.getMessage()); |
| + } |
| + |
| + urlRequest.setHttpMethod(methodName); |
| + urlRequest.start(); |
| + try { |
| + urlRequest.setHttpMethod("toolate"); |
| + fail("Exception not thrown"); |
| + } catch (IllegalStateException e) { |
| + assertEquals("Request is already started.", e.getMessage()); |
| + } |
| + listener.blockForComplete(); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
| + assertEquals(methodName, listener.mResponseAsString); |
| + } |
| + |
|
mmenke
2014/10/24 16:34:36
We never test that we get response headers (Should
mef
2014/10/27 21:00:02
Done.
|
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testAddHeader() throws Exception { |
| + SimpleUrlRequestListener listener = new SimpleUrlRequestListener(); |
| + String headerName = "header-name"; |
| + String headerValue = "header-value"; |
|
mmenke
2014/10/24 16:34:36
Should have a test where we add two values for the
mef
2014/10/27 21:00:03
Done.
|
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + UploadTestServer.getEchoHeaderURL(headerName), listener, |
| + mExecutor); |
| + urlRequest.addHeader(headerName, headerValue); |
| + urlRequest.start(); |
| + try { |
| + urlRequest.addHeader("header2", "value"); |
| + fail("Exception not thrown"); |
| + } catch (IllegalStateException e) { |
| + assertEquals("Request is already started.", e.getMessage()); |
| + } |
| + listener.blockForComplete(); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
| + assertEquals(headerValue, listener.mResponseAsString); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testBadHeaderName() throws Exception { |
| + SimpleUrlRequestListener listener = new SimpleUrlRequestListener(); |
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + TEST_URL, listener, mExecutor); |
| + try { |
| + urlRequest.addHeader("header:name", "headervalue"); |
| + fail("IllegalArgumentException not thrown."); |
| + } catch (IllegalArgumentException e) { |
| + assertEquals("Invalid header.", e.getMessage()); |
| + } |
| + |
| + try { |
| + urlRequest.addHeader("headername", "bad header\r\nvalue"); |
| + fail("IllegalArgumentException not thrown."); |
| + } catch (IllegalArgumentException e) { |
| + assertEquals("Invalid header.", e.getMessage()); |
| + } |
| + |
| + try { |
| + urlRequest.addHeader("headername", null); |
| + fail("NullPointerException not thrown."); |
| + } catch (NullPointerException e) { |
| + assertEquals("Invalid header.", e.getMessage()); |
| + } |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockSuccess() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MOCK_CRONET_TEST_SUCCESS_URL); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
| + assertTrue(listener.mHttpResponseDataLength != 0); |
| + assertEquals(listener.mLastCalled, LastCalled.OnComplete); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockRedirect() throws Exception { |
|
mmenke
2014/10/24 16:34:36
Should also check the response info we get when fo
mef
2014/10/27 21:00:03
Done.
|
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MOCK_CRONET_TEST_REDIRECT_URL); |
| + ResponseInfo mResponseInfo = listener.mResponseInfo; |
| + assertTrue(listener.mOnRedirectCalled); |
| + assertEquals(200, mResponseInfo.getHttpStatusCode()); |
| + assertEquals(MOCK_CRONET_TEST_SUCCESS_URL, |
| + mResponseInfo.getUrl()); |
| + assertEquals(2, mResponseInfo.getUrlChain().length); |
| + assertEquals(MOCK_CRONET_TEST_REDIRECT_URL, |
| + mResponseInfo.getUrlChain()[0]); |
| + assertEquals(MOCK_CRONET_TEST_SUCCESS_URL, |
| + mResponseInfo.getUrlChain()[1]); |
| + assertTrue(listener.mHttpResponseDataLength != 0); |
| + assertTrue(listener.mOnRedirectCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnComplete); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockNotFound() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MOCK_CRONET_TEST_NOTFOUND_URL); |
| + assertEquals(404, listener.mResponseInfo.getHttpStatusCode()); |
| + assertTrue(listener.mHttpResponseDataLength != 0); |
| + assertFalse(listener.mOnRedirectCalled); |
| + assertFalse(listener.mOnErrorCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnComplete); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockFailed() throws Exception { |
|
mmenke
2014/10/24 16:34:36
Still don't think we get anything from this test.
mef
2014/10/27 21:00:02
Done.
|
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MOCK_CRONET_TEST_FAILED_URL); |
| + assertEquals(null, listener.mResponseInfo); |
| + assertEquals(0, listener.mHttpResponseDataLength); |
| + assertNotNull(listener.mError); |
| + assertEquals(-2, listener.mError.netError()); |
|
mmenke
2014/10/24 16:34:36
We should also be checking the strings (In fact, I
mef
2014/10/27 21:00:03
Well, in these tests I'm actually checking that we
|
| + assertFalse(listener.mOnRedirectCalled); |
| + assertTrue(listener.mOnErrorCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.Nothing); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockStartAsyncError() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MockUrlRequestJobUtil.getMockUrlWithFailure( |
| + MOCK_SUCCESS_PATH, |
| + MockUrlRequestJobUtil.FailurePhase.START, |
| + -3)); |
| + assertNull(listener.mResponseInfo); |
| + assertNotNull(listener.mError); |
| + assertEquals(-3, listener.mError.netError()); |
| + assertFalse(listener.mOnRedirectCalled); |
| + assertTrue(listener.mOnErrorCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.Nothing); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockReadDataSyncError() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MockUrlRequestJobUtil.getMockUrlWithFailure( |
| + MOCK_SUCCESS_PATH, |
| + MockUrlRequestJobUtil.FailurePhase.READ_SYNC, |
| + -5)); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
| + assertNotNull(listener.mError); |
| + assertEquals(-5, listener.mError.netError()); |
| + assertFalse(listener.mOnRedirectCalled); |
| + assertTrue(listener.mOnErrorCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnResponseStarted); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testMockReadDataAsyncError() throws Exception { |
| + SimpleUrlRequestListener listener = startAndWaitForComplete( |
| + MockUrlRequestJobUtil.getMockUrlWithFailure( |
| + MOCK_SUCCESS_PATH, |
| + MockUrlRequestJobUtil.FailurePhase.READ_ASYNC, |
| + -5)); |
| + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); |
| + assertNotNull(listener.mError); |
| + assertEquals(-5, listener.mError.netError()); |
| + assertFalse(listener.mOnRedirectCalled); |
| + assertTrue(listener.mOnErrorCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnResponseStarted); |
| + } |
| + |
| + class CancelingUrlRequestListener extends SimpleUrlRequestListener { |
| + |
| + FailureType mFailureType = FailureType.None; |
| + LastCalled mFailureStep = LastCalled.Nothing; |
| + |
| + ExecutorService mExecutor; |
| + |
| + public CancelingUrlRequestListener(ExecutorService executor) { |
| + mExecutor = executor; |
| + } |
| + |
| + @Override |
| + public void onRedirect(UrlRequest request, |
| + ResponseInfo info, |
| + URL newLocation) { |
| + super.onRedirect(request, info, newLocation); |
| + maybeThrowOrCancel(request); |
| + } |
| + |
| + @Override |
| + public void onResponseStarted(UrlRequest request, ResponseInfo info) { |
| + super.onResponseStarted(request, info); |
| + maybeThrowOrCancel(request); |
| + } |
| + |
| + @Override |
| + public void onDataReceived(UrlRequest request, |
| + ResponseInfo info, |
| + ByteBuffer byteBuffer) { |
| + super.onDataReceived(request, info, byteBuffer); |
| + maybeThrowOrCancel(request); |
| + } |
| + |
| + @Override |
| + public void onComplete(UrlRequest request, ExtendedResponseInfo info) { |
| + super.onComplete(request, info); |
| + maybeThrowOrCancel(request); |
| + } |
| + |
| + private void maybeThrowOrCancel(final UrlRequest request) { |
| + if (mLastCalled != mFailureStep) { |
| + return; |
| + } |
| + if (mFailureType == FailureType.None) { |
| + return; |
| + } |
| + if (mFailureType == FailureType.ThrowSync) { |
| + throw new IllegalStateException("Listener Exception."); |
| + } |
| + Runnable task = new Runnable() { |
| + public void run() { |
| + request.cancel(); |
| + mComplete.open(); |
| + } |
| + }; |
| + if (mFailureType == FailureType.CancelAsync) { |
| + mExecutor.execute(task); |
| + } else { |
| + task.run(); |
| + } |
| + } |
| + } |
| + |
| + private void throwOrCancel(FailureType failureType, LastCalled failureStep, |
| + boolean expectResponseInfo, boolean expectError) { |
| + CancelingUrlRequestListener listener = |
| + new CancelingUrlRequestListener(mExecutor); |
| + listener.mFailureType = failureType; |
| + listener.mFailureStep = failureStep; |
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + MOCK_CRONET_TEST_REDIRECT_URL, listener, mExecutor); |
| + urlRequest.start(); |
| + listener.blockForComplete(); |
| + assertTrue(listener.mOnRedirectCalled); |
| + assertEquals(listener.mLastCalled, failureStep); |
| + assertTrue(urlRequest.isCanceled()); |
| + assertEquals(expectResponseInfo, listener.mResponseInfo != null); |
| + assertEquals(expectError, listener.mError != null); |
| + assertEquals(expectError, listener.mOnErrorCalled); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testFailures() throws Exception { |
| + throwOrCancel(FailureType.CancelSync, LastCalled.OnRedirect, |
| + false, false); |
| + throwOrCancel(FailureType.CancelAsync, LastCalled.OnRedirect, |
| + false, false); |
| + throwOrCancel(FailureType.ThrowSync, LastCalled.OnRedirect, |
| + false, true); |
| + |
| + throwOrCancel(FailureType.CancelSync, LastCalled.OnResponseStarted, |
| + true, false); |
| + throwOrCancel(FailureType.CancelAsync, LastCalled.OnResponseStarted, |
| + true, false); |
| + throwOrCancel(FailureType.ThrowSync, LastCalled.OnResponseStarted, |
| + true, true); |
| + |
| + throwOrCancel(FailureType.CancelSync, LastCalled.OnDataReceived, |
| + true, false); |
| + throwOrCancel(FailureType.CancelAsync, LastCalled.OnDataReceived, |
| + true, false); |
| + throwOrCancel(FailureType.ThrowSync, LastCalled.OnDataReceived, |
| + true, true); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Cronet"}) |
| + public void testThrowOnComplete() { |
| + CancelingUrlRequestListener listener = |
| + new CancelingUrlRequestListener(mExecutor); |
| + listener.mFailureType = FailureType.ThrowSync; |
| + listener.mFailureStep = LastCalled.OnComplete; |
| + UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest( |
| + MOCK_CRONET_TEST_REDIRECT_URL, listener, mExecutor); |
| + urlRequest.start(); |
| + listener.blockForComplete(); |
| + assertTrue(listener.mOnRedirectCalled); |
| + assertEquals(listener.mLastCalled, LastCalled.OnComplete); |
| + assertFalse(urlRequest.isCanceled()); |
| + assertNotNull(listener.mResponseInfo); |
| + assertNull(listener.mError); |
| + assertFalse(listener.mOnErrorCalled); |
| + } |
| +} |