diff options
-rw-r--r-- | src/com/android/providers/downloads/DownloadThread.java | 1 | ||||
-rw-r--r-- | tests/Android.mk | 1 | ||||
-rw-r--r-- | tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java (renamed from tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java) | 59 | ||||
-rw-r--r-- | tests/src/com/android/providers/downloads/AbstractPublicApiTest.java | 6 | ||||
-rw-r--r-- | tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java (renamed from tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java) | 21 | ||||
-rw-r--r-- | tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java | 130 | ||||
-rw-r--r-- | tests/src/com/android/providers/downloads/ThreadingTest.java | 2 | ||||
-rw-r--r-- | tests/src/tests/http/MockResponse.java | 147 | ||||
-rw-r--r-- | tests/src/tests/http/MockWebServer.java | 356 | ||||
-rw-r--r-- | tests/src/tests/http/RecordedRequest.java | 93 |
10 files changed, 112 insertions, 704 deletions
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 197af664..1d2aceb1 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -974,5 +974,4 @@ public class DownloadThread extends Thread { mPolicyDirty = true; } }; - } diff --git a/tests/Android.mk b/tests/Android.mk index 80a1c761..aaf32ba4 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -8,6 +8,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_INSTRUMENTATION_FOR := DownloadProvider LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := mockwebserver LOCAL_PACKAGE_NAME := DownloadProviderTests LOCAL_CERTIFICATE := media diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java index d2ecf3e6..1912e84c 100644 --- a/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java @@ -29,9 +29,11 @@ import android.test.RenamingDelegatingContext; import android.test.ServiceTestCase; import android.test.mock.MockContentResolver; import android.util.Log; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; + +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; +import com.google.mockwebserver.SocketPolicy; import java.io.BufferedReader; import java.io.File; @@ -39,14 +41,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -public abstract class AbstractDownloadManagerFunctionalTest extends +public abstract class AbstractDownloadProviderFunctionalTest extends ServiceTestCase<DownloadService> { - protected static final String LOG_TAG = "DownloadManagerFunctionalTest"; + protected static final String LOG_TAG = "DownloadProviderFunctionalTest"; private static final String PROVIDER_AUTHORITY = "downloads"; protected static final long RETRY_DELAY_MILLIS = 61 * 1000; protected static final String FILE_CONTENT = "hello world hello world hello world hello world"; @@ -143,7 +146,7 @@ public abstract class AbstractDownloadManagerFunctionalTest extends } } - public AbstractDownloadManagerFunctionalTest(FakeSystemFacade systemFacade) { + public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) { super(DownloadService.class); mSystemFacade = systemFacade; } @@ -212,52 +215,42 @@ public abstract class AbstractDownloadManagerFunctionalTest extends mResolver.delete(Downloads.Impl.CONTENT_URI, null, null); } - /** - * Enqueue a String response from the MockWebServer. - */ - MockResponse enqueueResponse(int status, String body) { - MockResponse response = new MockResponse() - .setResponseCode(status) - .setBody(body) - .addHeader("Content-type", "text/plain") - .setCloseConnectionAfter(true); - mServer.enqueue(response); - return response; + void enqueueResponse(MockResponse resp) { + mServer.enqueue(resp); } - /** - * Enqueue a byte[] response from the MockWebServer. - */ - MockResponse enqueueResponse(int status, byte[] body) { - MockResponse response = new MockResponse() - .setResponseCode(status) - .setBody(body) - .addHeader("Content-type", "text/plain") - .setCloseConnectionAfter(true); - mServer.enqueue(response); - return response; + + MockResponse buildResponse(int status, String body) { + return new MockResponse().setResponseCode(status).setBody(body) + .setHeader("Content-type", "text/plain") + .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END); + } + + MockResponse buildResponse(int status, byte[] body) { + return new MockResponse().setResponseCode(status).setBody(body) + .setHeader("Content-type", "text/plain") + .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END); } - MockResponse enqueueEmptyResponse(int status) { - return enqueueResponse(status, ""); + MockResponse buildEmptyResponse(int status) { + return buildResponse(status, ""); } /** * Fetch the last request received by the MockWebServer. */ protected RecordedRequest takeRequest() throws InterruptedException { - RecordedRequest request = mServer.takeRequestWithTimeout(0); + RecordedRequest request = mServer.takeRequest(); assertNotNull("Expected request was not made", request); return request; } - String getServerUri(String path) throws MalformedURLException { + String getServerUri(String path) throws MalformedURLException, UnknownHostException { return mServer.getUrl(path).toString(); } public void runService() throws Exception { startService(null); mSystemFacade.runAllThreads(); - mServer.checkForExceptions(); } protected String readStream(InputStream inputStream) throws IOException { diff --git a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java index c38c2f1d..cda607aa 100644 --- a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java +++ b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java @@ -26,11 +26,12 @@ import android.util.Log; import java.io.FileInputStream; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.UnknownHostException; /** * Code common to tests that use the download manager public API. */ -public abstract class AbstractPublicApiTest extends AbstractDownloadManagerFunctionalTest { +public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunctionalTest { class Download { final long mId; @@ -171,7 +172,8 @@ public abstract class AbstractPublicApiTest extends AbstractDownloadManagerFunct mManager = new DownloadManager(mResolver, PACKAGE_NAME); } - protected DownloadManager.Request getRequest() throws MalformedURLException { + protected DownloadManager.Request getRequest() + throws MalformedURLException, UnknownHostException { return getRequest(getServerUri(REQUEST_PATH)); } diff --git a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java index e01b617f..bbc5c3e0 100644 --- a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java @@ -25,11 +25,12 @@ import android.provider.Downloads; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.UnknownHostException; /** * This test exercises the entire download manager working together -- it requests downloads through @@ -38,15 +39,15 @@ import java.net.MalformedURLException; * device to serve downloads. */ @LargeTest -public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFunctionalTest { +public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunctionalTest { private static final String TAG = "DownloadManagerFunctionalTest"; - public DownloadManagerFunctionalTest() { + public DownloadProviderFunctionalTest() { super(new FakeSystemFacade()); } public void testDownloadTextFile() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); String path = "/download_manager_test_path"; Uri downloadUri = requestDownload(path); @@ -63,7 +64,8 @@ public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFuncti } public void testDownloadToCache() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + Uri downloadUri = requestDownload("/path"); updateDownload(downloadUri, Downloads.Impl.COLUMN_DESTINATION, Integer.toString(Downloads.Impl.DESTINATION_CACHE_PARTITION)); @@ -74,11 +76,13 @@ public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFuncti } public void testRoaming() throws Exception { + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; mSystemFacade.mIsRoaming = true; // for a normal download, roaming is fine - enqueueResponse(HTTP_OK, FILE_CONTENT); Uri downloadUri = requestDownload("/path"); runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); @@ -89,7 +93,6 @@ public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFuncti runUntilStatus(downloadUri, Downloads.Impl.STATUS_WAITING_FOR_NETWORK); // ...and pick up when we're off roaming - enqueueResponse(HTTP_OK, FILE_CONTENT); mSystemFacade.mIsRoaming = false; runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); } @@ -145,7 +148,7 @@ public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFuncti /** * Request a download from the Download Manager. */ - private Uri requestDownload(String path) throws MalformedURLException { + private Uri requestDownload(String path) throws MalformedURLException, UnknownHostException { ContentValues values = new ContentValues(); values.put(Downloads.Impl.COLUMN_URI, getServerUri(path)); values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_EXTERNAL); diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java index f2a26f12..2f5282ae 100644 --- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java @@ -16,7 +16,6 @@ package com.android.providers.downloads; - import android.app.DownloadManager; import android.content.Intent; import android.database.Cursor; @@ -25,9 +24,10 @@ import android.net.Uri; import android.os.Environment; import android.provider.Downloads; import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; -import tests.http.MockResponse; -import tests.http.RecordedRequest; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.RecordedRequest; import java.io.File; import java.io.FileInputStream; @@ -76,7 +76,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testBasicRequest() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); Download download = enqueueRequest(getRequest()); assertEquals(DownloadManager.STATUS_PENDING, @@ -126,12 +126,12 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testDownloadError() throws Exception { - enqueueEmptyResponse(HTTP_NOT_FOUND); + enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); runSimpleFailureTest(HTTP_NOT_FOUND); } public void testUnhandledHttpStatus() throws Exception { - enqueueEmptyResponse(1234); // some invalid HTTP status + enqueueResponse(buildEmptyResponse(1234)); // some invalid HTTP status runSimpleFailureTest(DownloadManager.ERROR_UNHANDLED_HTTP_CODE); } @@ -168,21 +168,21 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { private void enqueueInterruptedDownloadResponses(int initialLength) { // the first response has normal headers but unexpectedly closes after initialLength bytes - enqueuePartialResponse(0, initialLength); + enqueueResponse(buildPartialResponse(0, initialLength)); // the second response returns partial content for the rest of the data - enqueuePartialResponse(initialLength, FILE_CONTENT.length()); + enqueueResponse(buildPartialResponse(initialLength, FILE_CONTENT.length())); } - private MockResponse enqueuePartialResponse(int start, int end) { + private MockResponse buildPartialResponse(int start, int end) { int totalLength = FILE_CONTENT.length(); boolean isFirstResponse = (start == 0); int status = isFirstResponse ? HTTP_OK : HTTP_PARTIAL_CONTENT; - MockResponse response = enqueueResponse(status, FILE_CONTENT.substring(start, end)) - .addHeader("Content-length", totalLength) - .addHeader("Etag", ETAG); + MockResponse response = buildResponse(status, FILE_CONTENT.substring(start, end)) + .setHeader("Content-length", totalLength) + .setHeader("Etag", ETAG); if (!isFirstResponse) { - response.addHeader("Content-range", - "bytes " + start + "-" + totalLength + "/" + totalLength); + response.setHeader( + "Content-range", "bytes " + start + "-" + totalLength + "/" + totalLength); } return response; } @@ -190,20 +190,21 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { // enqueue a huge response to keep the receiveing thread in DownloadThread.java busy for a while // give enough time to do something (cancel/remove etc) on that downloadrequest // while it is in progress - private void enqueueContinuingResponse() { + private MockResponse buildContinuingResponse() { int numPackets = 100; - int contentLength = STRING_1K.length() * numPackets; - enqueueResponse(HTTP_OK, STRING_1K) - .addHeader("Content-length", contentLength) - .addHeader("Etag", ETAG) - .setNumPackets(numPackets); + int contentLength = STRING_1K.length() * numPackets; + return buildResponse(HTTP_OK, STRING_1K) + .setHeader("Content-length", contentLength) + .setHeader("Etag", ETAG) + .setBytesPerSecond(1024); } public void testFiltering() throws Exception { - enqueueEmptyResponse(HTTP_OK); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); + Download download1 = enqueueRequest(getRequest()); download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); - enqueueEmptyResponse(HTTP_NOT_FOUND); mSystemFacade.incrementTimeMillis(1); // ensure downloads are correctly ordered by time Download download2 = enqueueRequest(getRequest()); @@ -240,17 +241,18 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testOrdering() throws Exception { - enqueueResponse(HTTP_OK, "small contents"); + enqueueResponse(buildResponse(HTTP_OK, "small contents")); + enqueueResponse(buildResponse(HTTP_OK, "large contents large contents")); + enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); + Download download1 = enqueueRequest(getRequest()); download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); mSystemFacade.incrementTimeMillis(1); - enqueueResponse(HTTP_OK, "large contents large contents"); Download download2 = enqueueRequest(getRequest()); download2.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); mSystemFacade.incrementTimeMillis(1); - enqueueEmptyResponse(HTTP_NOT_FOUND); Download download3 = enqueueRequest(getRequest()); download3.runUntilStatus(DownloadManager.STATUS_FAILED); @@ -299,7 +301,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testDestination() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); Uri destination = getExternalUri(); Download download = enqueueRequest(getRequest().setDestinationUri(destination)); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); @@ -320,7 +322,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testRequestHeaders() throws Exception { - enqueueEmptyResponse(HTTP_OK); + enqueueResponse(buildEmptyResponse(HTTP_OK)); Download download = enqueueRequest(getRequest().addRequestHeader("Header1", "value1") .addRequestHeader("Header2", "value2")); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); @@ -342,17 +344,17 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testSizeLimitOverMobile() throws Exception { - mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1; + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1; mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; - enqueueResponse(HTTP_OK, FILE_CONTENT); Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_PAUSED); mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; // first response was read, but aborted after the DL manager processed the Content-Length // header, so we need to enqueue a second one - enqueueResponse(HTTP_OK, FILE_CONTENT); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); } @@ -369,27 +371,28 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testNoEtag() throws Exception { - enqueuePartialResponse(0, 5).removeHeader("Etag"); + enqueueResponse(buildPartialResponse(0, 5).removeHeader("Etag")); runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME); } public void testSanitizeMediaType() throws Exception { - enqueueEmptyResponse(HTTP_OK).addHeader("Content-Type", "text/html; charset=ISO-8859-4"); + enqueueResponse(buildEmptyResponse(HTTP_OK) + .setHeader("Content-Type", "text/html; charset=ISO-8859-4")); Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); assertEquals("text/html", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); } public void testNoContentLength() throws Exception { - enqueueEmptyResponse(HTTP_OK).removeHeader("Content-Length"); + enqueueResponse(buildEmptyResponse(HTTP_OK).removeHeader("Content-length")); runSimpleFailureTest(DownloadManager.ERROR_HTTP_DATA_ERROR); } public void testInsufficientSpace() throws Exception { // this would be better done by stubbing the system API to check available space, but in the // meantime, just use an absurdly large header value - enqueueEmptyResponse(HTTP_OK).addHeader("Content-Length", - 1024L * 1024 * 1024 * 1024 * 1024); + enqueueResponse(buildEmptyResponse(HTTP_OK) + .setHeader("Content-Length", 1024L * 1024 * 1024 * 1024 * 1024)); runSimpleFailureTest(DownloadManager.ERROR_INSUFFICIENT_SPACE); } @@ -397,7 +400,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { mSystemFacade.setStartThreadsWithoutWaiting(true); // return 'real time' from FakeSystemFacade so that DownloadThread will report progress mSystemFacade.setReturnActualTime(true); - enqueueContinuingResponse(); + enqueueResponse(buildContinuingResponse()); Download download = enqueueRequest(getRequest()); startService(null); // give the download time to get started and progress to 1% completion @@ -413,7 +416,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testDownloadCompleteBroadcast() throws Exception { - enqueueEmptyResponse(HTTP_OK); + enqueueResponse(buildEmptyResponse(HTTP_OK)); Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); @@ -441,12 +444,11 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testBasicConnectivityChanges() throws Exception { + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + // without connectivity, download immediately pauses mSystemFacade.mActiveNetworkType = null; - - enqueueResponse(HTTP_OK, FILE_CONTENT); Download download = enqueueRequest(getRequest()); - download.runUntilStatus(DownloadManager.STATUS_PAUSED); // connecting should start the download @@ -455,10 +457,12 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testAllowedNetworkTypes() throws Exception { + enqueueResponse(buildEmptyResponse(HTTP_OK)); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; // by default, use any connection - enqueueEmptyResponse(HTTP_OK); Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); @@ -468,15 +472,16 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { download.runUntilStatus(DownloadManager.STATUS_PAUSED); // ...then enable wifi mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; - enqueueEmptyResponse(HTTP_OK); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); } public void testRoaming() throws Exception { + enqueueResponse(buildEmptyResponse(HTTP_OK)); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + mSystemFacade.mIsRoaming = true; // by default, allow roaming - enqueueEmptyResponse(HTTP_OK); Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); @@ -485,12 +490,11 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { download.runUntilStatus(DownloadManager.STATUS_PAUSED); // ...then turn off roaming mSystemFacade.mIsRoaming = false; - enqueueEmptyResponse(HTTP_OK); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); } public void testContentObserver() throws Exception { - enqueueEmptyResponse(HTTP_OK); + enqueueResponse(buildEmptyResponse(HTTP_OK)); enqueueRequest(getRequest()); mResolver.resetNotified(); runService(); @@ -498,13 +502,14 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testNotifications() throws Exception { - enqueueEmptyResponse(HTTP_OK); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + Download download = enqueueRequest(getRequest().setShowRunningNotification(false)); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); assertEquals(0, mSystemFacade.mActiveNotifications.size()); assertEquals(0, mSystemFacade.mCanceledNotifications.size()); - enqueueEmptyResponse(HTTP_OK); download = enqueueRequest(getRequest()); // notifications by default download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); assertEquals(1, mSystemFacade.mActiveNotifications.size()); @@ -518,43 +523,43 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { public void testRetryAfter() throws Exception { final int delay = 120; - enqueueEmptyResponse(HTTP_SERVICE_UNAVAILABLE).addHeader("Retry-after", delay); + enqueueResponse( + buildEmptyResponse(HTTP_SERVICE_UNAVAILABLE).setHeader("Retry-after", delay)); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_PAUSED); // download manager adds random 0-30s offset mSystemFacade.incrementTimeMillis((delay + 31) * 1000); - - enqueueEmptyResponse(HTTP_OK); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); } public void testManyInterruptions() throws Exception { - int bytesPerResponse = 1; - int start = 0; + final int length = FILE_CONTENT.length(); + for (int i = 0; i < length; i++) { + enqueueResponse(buildPartialResponse(i, i + 1)); + } Download download = enqueueRequest(getRequest()); - while (start + bytesPerResponse < FILE_CONTENT.length()) { - enqueuePartialResponse(start, start + bytesPerResponse); + for (int i = 0; i < length - 1; i++) { download.runUntilStatus(DownloadManager.STATUS_PAUSED); - takeRequest(); - start += bytesPerResponse; mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); } - enqueuePartialResponse(start, FILE_CONTENT.length()); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); checkCompleteDownload(download); } public void testExistingFile() throws Exception { + enqueueResponse(buildEmptyResponse(HTTP_OK)); + // download a file which already exists. // downloadservice should simply create filename with "-" and a number attached // at the end; i.e., download shouldnot fail. Uri destination = getExternalUri(); new File(destination.getPath()).createNewFile(); - enqueueEmptyResponse(HTTP_OK); Download download = enqueueRequest(getRequest().setDestinationUri(destination)); download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); } @@ -571,11 +576,12 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { } public void testRestart() throws Exception { - enqueueEmptyResponse(HTTP_NOT_FOUND); + enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); + enqueueResponse(buildEmptyResponse(HTTP_OK)); + Download download = enqueueRequest(getRequest()); download.runUntilStatus(DownloadManager.STATUS_FAILED); - enqueueEmptyResponse(HTTP_OK); mManager.restartDownload(download.mId); assertEquals(DownloadManager.STATUS_PENDING, download.getLongField(DownloadManager.COLUMN_STATUS)); @@ -604,8 +610,8 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { */ private RecordedRequest runRedirectionTest(int status) throws MalformedURLException, Exception { - enqueueEmptyResponse(status).addHeader("Location", - mServer.getUrl(REDIRECTED_PATH).toString()); + enqueueResponse(buildEmptyResponse(status) + .setHeader("Location", mServer.getUrl(REDIRECTED_PATH).toString())); enqueueInterruptedDownloadResponses(5); Download download = enqueueRequest(getRequest()); diff --git a/tests/src/com/android/providers/downloads/ThreadingTest.java b/tests/src/com/android/providers/downloads/ThreadingTest.java index 082579e6..8605c76b 100644 --- a/tests/src/com/android/providers/downloads/ThreadingTest.java +++ b/tests/src/com/android/providers/downloads/ThreadingTest.java @@ -46,7 +46,7 @@ public class ThreadingTest extends AbstractPublicApiTest { * a download. */ public void testFloodServiceWithStarts() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); Download download = enqueueRequest(getRequest()); while (download.getStatus() != DownloadManager.STATUS_SUCCESSFUL) { startService(null); diff --git a/tests/src/tests/http/MockResponse.java b/tests/src/tests/http/MockResponse.java deleted file mode 100644 index aec5490c..00000000 --- a/tests/src/tests/http/MockResponse.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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 tests.http; - -import static tests.http.MockWebServer.ASCII; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A scripted response to be replayed by the mock web server. - */ -public class MockResponse { - private static final byte[] EMPTY_BODY = new byte[0]; - - private String status = "HTTP/1.1 200 OK"; - private Map<String, String> headers = new HashMap<String, String>(); - private byte[] body = EMPTY_BODY; - private boolean closeConnectionAfter = false; - private int numPackets = 0; - - public MockResponse() { - addHeader("Content-Length", 0); - } - - /** - * Returns the HTTP response line, such as "HTTP/1.1 200 OK". - */ - public String getStatus() { - return status; - } - - public MockResponse setResponseCode(int code) { - this.status = "HTTP/1.1 " + code + " OK"; - return this; - } - - /** - * Returns the HTTP headers, such as "Content-Length: 0". - */ - public List<String> getHeaders() { - List<String> headerStrings = new ArrayList<String>(); - for (String header : headers.keySet()) { - headerStrings.add(header + ": " + headers.get(header)); - } - return headerStrings; - } - - public MockResponse addHeader(String header, String value) { - headers.put(header.toLowerCase(), value); - return this; - } - - public MockResponse addHeader(String header, long value) { - return addHeader(header, Long.toString(value)); - } - - public MockResponse removeHeader(String header) { - headers.remove(header.toLowerCase()); - return this; - } - - /** - * Returns an input stream containing the raw HTTP payload. - */ - public byte[] getBody() { - return body; - } - - public MockResponse setBody(byte[] body) { - addHeader("Content-Length", body.length); - this.body = body; - return this; - } - - public MockResponse setBody(String body) { - try { - return setBody(body.getBytes(ASCII)); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(); - } - } - - public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException { - addHeader("Transfer-encoding", "chunked"); - - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - int pos = 0; - while (pos < body.length) { - int chunkSize = Math.min(body.length - pos, maxChunkSize); - bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII)); - bytesOut.write("\r\n".getBytes(ASCII)); - bytesOut.write(body, pos, chunkSize); - bytesOut.write("\r\n".getBytes(ASCII)); - pos += chunkSize; - } - bytesOut.write("0\r\n".getBytes(ASCII)); - this.body = bytesOut.toByteArray(); - return this; - } - - public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException { - return setChunkedBody(body.getBytes(ASCII), maxChunkSize); - } - - @Override public String toString() { - return status; - } - - public boolean shouldCloseConnectionAfter() { - return closeConnectionAfter; - } - - public MockResponse setCloseConnectionAfter(boolean closeConnectionAfter) { - this.closeConnectionAfter = closeConnectionAfter; - return this; - } - - public int getNumPackets() { - return numPackets; - } - - public MockResponse setNumPackets(int numPackets) { - this.numPackets = numPackets; - return this; - } - -} diff --git a/tests/src/tests/http/MockWebServer.java b/tests/src/tests/http/MockWebServer.java deleted file mode 100644 index 6096783d..00000000 --- a/tests/src/tests/http/MockWebServer.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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 tests.http; - -import android.text.TextUtils; -import android.util.Log; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URL; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * A scriptable web server. Callers supply canned responses and the server - * replays them upon request in sequence. - * - * TODO: merge with the version from libcore/support/src/tests/java once it's in. - */ -public final class MockWebServer { - static final String ASCII = "US-ASCII"; - - private final BlockingQueue<RecordedRequest> requestQueue - = new LinkedBlockingQueue<RecordedRequest>(); - private final BlockingQueue<MockResponse> responseQueue - = new LinkedBlockingQueue<MockResponse>(); - private int bodyLimit = Integer.MAX_VALUE; - private final ExecutorService executor = Executors.newCachedThreadPool(); - // keep Futures around so we can rethrow any exceptions thrown by Callables - private final Queue<Future<?>> futures = new LinkedList<Future<?>>(); - - private int port = -1; - private ServerSocket serverSocket; - - public int getPort() { - if (port == -1) { - throw new IllegalStateException("Cannot retrieve port before calling play()"); - } - return port; - } - - /** - * Returns a URL for connecting to this server. - * - * @param path the request path, such as "/". - */ - public URL getUrl(String path) throws MalformedURLException { - return new URL("http://localhost:" + getPort() + path); - } - - /** - * Sets the number of bytes of the POST body to keep in memory to the given - * limit. - */ - public void setBodyLimit(int maxBodyLength) { - this.bodyLimit = maxBodyLength; - } - - public void enqueue(MockResponse response) { - responseQueue.add(response); - } - - /** - * Awaits the next HTTP request, removes it, and returns it. Callers should - * use this to verify the request sent was as intended. - */ - public RecordedRequest takeRequest() throws InterruptedException { - return requestQueue.take(); - } - - public RecordedRequest takeRequestWithTimeout(long timeoutMillis) throws InterruptedException { - return requestQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS); - } - - public List<RecordedRequest> drainRequests() { - List<RecordedRequest> requests = new ArrayList<RecordedRequest>(); - requestQueue.drainTo(requests); - return requests; - } - - /** - * Starts the server, serves all enqueued requests, and shuts the server - * down. - */ - public void play() throws IOException { - serverSocket = new ServerSocket(0); - serverSocket.setReuseAddress(true); - port = serverSocket.getLocalPort(); - submitCallable(new Callable<Void>() { - public Void call() throws Exception { - int count = 0; - while (true) { - if (count > 0 && responseQueue.isEmpty()) { - serverSocket.close(); - executor.shutdown(); - return null; - } - - serveConnection(serverSocket.accept()); - count++; - } - } - }); - } - - /** - * shutdown the webserver - */ - public void shutdown() throws IOException { - responseQueue.clear(); - serverSocket.close(); - executor.shutdown(); - } - - private void serveConnection(final Socket s) { - submitCallable(new Callable<Void>() { - public Void call() throws Exception { - InputStream in = new BufferedInputStream(s.getInputStream()); - OutputStream out = new BufferedOutputStream(s.getOutputStream()); - - int sequenceNumber = 0; - while (true) { - RecordedRequest request = readRequest(in, sequenceNumber); - if (request == null) { - if (sequenceNumber == 0) { - throw new IllegalStateException("Connection without any request!"); - } else { - break; - } - } - requestQueue.add(request); - MockResponse response = sendResponse(out, request); - if (response.shouldCloseConnectionAfter()) { - break; - } - sequenceNumber++; - } - - in.close(); - out.close(); - return null; - } - }); - } - - private void submitCallable(Callable<?> callable) { - Future<?> future = executor.submit(callable); - futures.add(future); - } - - /** - * Check for and raise any exceptions that have been thrown by child threads. Will not block on - * children still running. - * @throws ExecutionException for the first child thread that threw an exception - */ - public void checkForExceptions() throws ExecutionException, InterruptedException { - final int originalSize = futures.size(); - for (int i = 0; i < originalSize; i++) { - Future<?> future = futures.remove(); - try { - future.get(0, TimeUnit.SECONDS); - } catch (TimeoutException e) { - futures.add(future); // still running - } - } - } - - /** - * @param sequenceNumber the index of this request on this connection. - */ - private RecordedRequest readRequest(InputStream in, int sequenceNumber) throws IOException { - String request = readAsciiUntilCrlf(in); - if (request.equals("")) { - return null; // end of data; no more requests - } - - List<String> headers = new ArrayList<String>(); - int contentLength = -1; - boolean chunked = false; - String header; - while (!(header = readAsciiUntilCrlf(in)).equals("")) { - headers.add(header); - String lowercaseHeader = header.toLowerCase(); - if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) { - contentLength = Integer.parseInt(header.substring(15).trim()); - } - if (lowercaseHeader.startsWith("transfer-encoding:") && - lowercaseHeader.substring(18).trim().equals("chunked")) { - chunked = true; - } - } - - boolean hasBody = false; - TruncatingOutputStream requestBody = new TruncatingOutputStream(); - List<Integer> chunkSizes = new ArrayList<Integer>(); - if (contentLength != -1) { - hasBody = true; - transfer(contentLength, in, requestBody); - } else if (chunked) { - hasBody = true; - while (true) { - int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16); - if (chunkSize == 0) { - readEmptyLine(in); - break; - } - chunkSizes.add(chunkSize); - transfer(chunkSize, in, requestBody); - readEmptyLine(in); - } - } - - if (request.startsWith("GET ")) { - if (hasBody) { - throw new IllegalArgumentException("GET requests should not have a body!"); - } - } else if (request.startsWith("POST ")) { - if (!hasBody) { - throw new IllegalArgumentException("POST requests must have a body!"); - } - } else { - throw new UnsupportedOperationException("Unexpected method: " + request); - } - return new RecordedRequest(request, headers, chunkSizes, - requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber); - } - - /** - * Returns a response to satisfy {@code request}. - */ - private MockResponse sendResponse(OutputStream out, RecordedRequest request) - throws InterruptedException, IOException { - if (responseQueue.isEmpty()) { - throw new IllegalStateException("Unexpected request: " + request); - } - MockResponse response = responseQueue.take(); - writeResponse(out, response, false); - if (response.getNumPackets() > 0) { - // there are continuing packets to send as part of this response. - for (int i = 0; i < response.getNumPackets(); i++) { - writeResponse(out, response, true); - // delay sending next continuing response just a little bit - Thread.sleep(100); - } - } - return response; - } - - private void writeResponse(OutputStream out, MockResponse response, - boolean continuingPacket) throws IOException { - if (continuingPacket) { - // this is a continuing response - just send the body - no headers, status - out.write(response.getBody()); - out.flush(); - return; - } - out.write((response.getStatus() + "\r\n").getBytes(ASCII)); - for (String header : response.getHeaders()) { - out.write((header + "\r\n").getBytes(ASCII)); - } - out.write(("\r\n").getBytes(ASCII)); - out.write(response.getBody()); - out.flush(); - } - - /** - * Transfer bytes from {@code in} to {@code out} until either {@code length} - * bytes have been transferred or {@code in} is exhausted. - */ - private void transfer(int length, InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[1024]; - while (length > 0) { - int count = in.read(buffer, 0, Math.min(buffer.length, length)); - if (count == -1) { - return; - } - out.write(buffer, 0, count); - length -= count; - } - } - - /** - * Returns the text from {@code in} until the next "\r\n", or null if - * {@code in} is exhausted. - */ - private String readAsciiUntilCrlf(InputStream in) throws IOException { - StringBuilder builder = new StringBuilder(); - while (true) { - int c = in.read(); - if (c == '\n' && builder.length() > 0 && builder.charAt(builder.length() - 1) == '\r') { - builder.deleteCharAt(builder.length() - 1); - return builder.toString(); - } else if (c == -1) { - return builder.toString(); - } else { - builder.append((char) c); - } - } - } - - private void readEmptyLine(InputStream in) throws IOException { - String line = readAsciiUntilCrlf(in); - if (!line.equals("")) { - throw new IllegalStateException("Expected empty but was: " + line); - } - } - - /** - * An output stream that drops data after bodyLimit bytes. - */ - private class TruncatingOutputStream extends ByteArrayOutputStream { - private int numBytesReceived = 0; - @Override public void write(byte[] buffer, int offset, int len) { - numBytesReceived += len; - super.write(buffer, offset, Math.min(len, bodyLimit - count)); - } - @Override public void write(int oneByte) { - numBytesReceived++; - if (count < bodyLimit) { - super.write(oneByte); - } - } - } -} diff --git a/tests/src/tests/http/RecordedRequest.java b/tests/src/tests/http/RecordedRequest.java deleted file mode 100644 index 6b67af20..00000000 --- a/tests/src/tests/http/RecordedRequest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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 tests.http; - -import java.util.List; - -/** - * An HTTP request that came into the mock web server. - */ -public final class RecordedRequest { - private final String requestLine; - private final List<String> headers; - private final List<Integer> chunkSizes; - private final int bodySize; - private final byte[] body; - private final int sequenceNumber; - - RecordedRequest(String requestLine, List<String> headers, List<Integer> chunkSizes, - int bodySize, byte[] body, int sequenceNumber) { - this.requestLine = requestLine; - this.headers = headers; - this.chunkSizes = chunkSizes; - this.bodySize = bodySize; - this.body = body; - this.sequenceNumber = sequenceNumber; - } - - public String getRequestLine() { - return requestLine; - } - - public List<String> getHeaders() { - return headers; - } - - /** - * Returns the sizes of the chunks of this request's body, or an empty list - * if the request's body was empty or unchunked. - */ - public List<Integer> getChunkSizes() { - return chunkSizes; - } - - /** - * Returns the total size of the body of this POST request (before - * truncation). - */ - public int getBodySize() { - return bodySize; - } - - /** - * Returns the body of this POST request. This may be truncated. - */ - public byte[] getBody() { - return body; - } - - /** - * Returns the index of this request on its HTTP connection. Since a single - * HTTP connection may serve multiple requests, each request is assigned its - * own sequence number. - */ - public int getSequenceNumber() { - return sequenceNumber; - } - - @Override public String toString() { - return requestLine; - } - - public String getMethod() { - return getRequestLine().split(" ")[0]; - } - - public String getPath() { - return getRequestLine().split(" ")[1]; - } -} |