From ae6856b0fca5215f45619dd031a7e7beae7bd8cc Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 29 Mar 2012 13:38:24 -0700 Subject: Migrate to shared MockWebServer. Bug: 4726601 Change-Id: Ibe537bd5c2a092dbf974360cd454751881f7f4ea --- tests/Android.mk | 1 + .../AbstractDownloadManagerFunctionalTest.java | 279 ---------------- .../AbstractDownloadProviderFunctionalTest.java | 272 ++++++++++++++++ .../providers/downloads/AbstractPublicApiTest.java | 6 +- .../downloads/DownloadManagerFunctionalTest.java | 164 ---------- .../downloads/DownloadProviderFunctionalTest.java | 167 ++++++++++ .../downloads/PublicApiFunctionalTest.java | 130 ++++---- .../android/providers/downloads/ThreadingTest.java | 2 +- tests/src/tests/http/MockResponse.java | 147 --------- tests/src/tests/http/MockWebServer.java | 356 --------------------- tests/src/tests/http/RecordedRequest.java | 93 ------ 11 files changed, 513 insertions(+), 1104 deletions(-) delete mode 100644 tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java create mode 100644 tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java delete mode 100644 tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java create mode 100644 tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java delete mode 100644 tests/src/tests/http/MockResponse.java delete mode 100644 tests/src/tests/http/MockWebServer.java delete mode 100644 tests/src/tests/http/RecordedRequest.java (limited to 'tests') 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/AbstractDownloadManagerFunctionalTest.java deleted file mode 100644 index d2ecf3e6..00000000 --- a/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java +++ /dev/null @@ -1,279 +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 com.android.providers.downloads; - -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.Uri; -import android.provider.Downloads; -import android.test.MoreAsserts; -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 java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public abstract class AbstractDownloadManagerFunctionalTest extends - ServiceTestCase { - - protected static final String LOG_TAG = "DownloadManagerFunctionalTest"; - 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"; - protected static final int HTTP_OK = 200; - protected static final int HTTP_PARTIAL_CONTENT = 206; - protected static final int HTTP_NOT_FOUND = 404; - protected static final int HTTP_SERVICE_UNAVAILABLE = 503; - - protected MockWebServer mServer; - protected MockContentResolverWithNotify mResolver; - protected TestContext mTestContext; - protected FakeSystemFacade mSystemFacade; - protected static String STRING_1K; - static { - StringBuilder buff = new StringBuilder(); - for (int i = 0; i < 1024; i++) { - buff.append("a" + i % 26); - } - STRING_1K = buff.toString(); - } - - static class MockContentResolverWithNotify extends MockContentResolver { - public boolean mNotifyWasCalled = false; - - public synchronized void resetNotified() { - mNotifyWasCalled = false; - } - - @Override - public synchronized void notifyChange(Uri uri, ContentObserver observer, - boolean syncToNetwork) { - mNotifyWasCalled = true; - notifyAll(); - } - } - - /** - * Context passed to the provider and the service. Allows most methods to pass through to the - * real Context (this is a LargeTest), with a few exceptions, including renaming file operations - * to avoid file and DB conflicts (via RenamingDelegatingContext). - */ - static class TestContext extends RenamingDelegatingContext { - private static final String FILENAME_PREFIX = "test."; - - private Context mRealContext; - private Set mAllowedSystemServices; - private ContentResolver mResolver; - - boolean mHasServiceBeenStarted = false; - - public TestContext(Context realContext) { - super(realContext, FILENAME_PREFIX); - mRealContext = realContext; - mAllowedSystemServices = new HashSet(Arrays.asList(new String[] { - Context.NOTIFICATION_SERVICE, - Context.POWER_SERVICE, - })); - } - - public void setResolver(ContentResolver resolver) { - mResolver = resolver; - } - - /** - * Direct DownloadService to our test instance of DownloadProvider. - */ - @Override - public ContentResolver getContentResolver() { - assert mResolver != null; - return mResolver; - } - - /** - * Stub some system services, allow access to others, and block the rest. - */ - @Override - public Object getSystemService(String name) { - if (mAllowedSystemServices.contains(name)) { - return mRealContext.getSystemService(name); - } - return super.getSystemService(name); - } - - /** - * Record when DownloadProvider starts DownloadService. - */ - @Override - public ComponentName startService(Intent service) { - if (service.getComponent().getClassName().equals(DownloadService.class.getName())) { - mHasServiceBeenStarted = true; - return service.getComponent(); - } - throw new UnsupportedOperationException("Unexpected service: " + service); - } - } - - public AbstractDownloadManagerFunctionalTest(FakeSystemFacade systemFacade) { - super(DownloadService.class); - mSystemFacade = systemFacade; - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Context realContext = getContext(); - mTestContext = new TestContext(realContext); - setupProviderAndResolver(); - - mTestContext.setResolver(mResolver); - setContext(mTestContext); - setupService(); - getService().mSystemFacade = mSystemFacade; - assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data - mServer = new MockWebServer(); - mServer.play(); - } - - @Override - protected void tearDown() throws Exception { - cleanUpDownloads(); - mServer.shutdown(); - super.tearDown(); - } - - private boolean isDatabaseEmpty() { - Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, - null, null, null, null); - try { - return cursor.getCount() == 0; - } finally { - cursor.close(); - } - } - - void setupProviderAndResolver() { - DownloadProvider provider = new DownloadProvider(); - provider.mSystemFacade = mSystemFacade; - provider.attachInfo(mTestContext, null); - mResolver = new MockContentResolverWithNotify(); - mResolver.addProvider(PROVIDER_AUTHORITY, provider); - } - - /** - * Remove any downloaded files and delete any lingering downloads. - */ - void cleanUpDownloads() { - if (mResolver == null) { - return; - } - String[] columns = new String[] {Downloads.Impl._DATA}; - Cursor cursor = mResolver.query(Downloads.Impl.CONTENT_URI, columns, null, null, null); - try { - for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { - String filePath = cursor.getString(0); - if (filePath == null) continue; - Log.d(LOG_TAG, "Deleting " + filePath); - new File(filePath).delete(); - } - } finally { - cursor.close(); - } - 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; - } - /** - * 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 enqueueEmptyResponse(int status) { - return enqueueResponse(status, ""); - } - - /** - * Fetch the last request received by the MockWebServer. - */ - protected RecordedRequest takeRequest() throws InterruptedException { - RecordedRequest request = mServer.takeRequestWithTimeout(0); - assertNotNull("Expected request was not made", request); - return request; - } - - String getServerUri(String path) throws MalformedURLException { - return mServer.getUrl(path).toString(); - } - - public void runService() throws Exception { - startService(null); - mSystemFacade.runAllThreads(); - mServer.checkForExceptions(); - } - - protected String readStream(InputStream inputStream) throws IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - try { - char[] buffer = new char[1024]; - int length = reader.read(buffer); - assertTrue("Failed to read anything from input stream", length > -1); - return String.valueOf(buffer, 0, length); - } finally { - reader.close(); - } - } - - protected void assertStartsWith(String expectedPrefix, String actual) { - String regex = "^" + expectedPrefix + ".*"; - MoreAsserts.assertMatchesRegex(regex, actual); - } -} diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java new file mode 100644 index 00000000..1912e84c --- /dev/null +++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java @@ -0,0 +1,272 @@ +/* + * 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 com.android.providers.downloads; + +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.database.Cursor; +import android.net.Uri; +import android.provider.Downloads; +import android.test.MoreAsserts; +import android.test.RenamingDelegatingContext; +import android.test.ServiceTestCase; +import android.test.mock.MockContentResolver; +import android.util.Log; + +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; +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 AbstractDownloadProviderFunctionalTest extends + ServiceTestCase { + + 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"; + protected static final int HTTP_OK = 200; + protected static final int HTTP_PARTIAL_CONTENT = 206; + protected static final int HTTP_NOT_FOUND = 404; + protected static final int HTTP_SERVICE_UNAVAILABLE = 503; + + protected MockWebServer mServer; + protected MockContentResolverWithNotify mResolver; + protected TestContext mTestContext; + protected FakeSystemFacade mSystemFacade; + protected static String STRING_1K; + static { + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < 1024; i++) { + buff.append("a" + i % 26); + } + STRING_1K = buff.toString(); + } + + static class MockContentResolverWithNotify extends MockContentResolver { + public boolean mNotifyWasCalled = false; + + public synchronized void resetNotified() { + mNotifyWasCalled = false; + } + + @Override + public synchronized void notifyChange(Uri uri, ContentObserver observer, + boolean syncToNetwork) { + mNotifyWasCalled = true; + notifyAll(); + } + } + + /** + * Context passed to the provider and the service. Allows most methods to pass through to the + * real Context (this is a LargeTest), with a few exceptions, including renaming file operations + * to avoid file and DB conflicts (via RenamingDelegatingContext). + */ + static class TestContext extends RenamingDelegatingContext { + private static final String FILENAME_PREFIX = "test."; + + private Context mRealContext; + private Set mAllowedSystemServices; + private ContentResolver mResolver; + + boolean mHasServiceBeenStarted = false; + + public TestContext(Context realContext) { + super(realContext, FILENAME_PREFIX); + mRealContext = realContext; + mAllowedSystemServices = new HashSet(Arrays.asList(new String[] { + Context.NOTIFICATION_SERVICE, + Context.POWER_SERVICE, + })); + } + + public void setResolver(ContentResolver resolver) { + mResolver = resolver; + } + + /** + * Direct DownloadService to our test instance of DownloadProvider. + */ + @Override + public ContentResolver getContentResolver() { + assert mResolver != null; + return mResolver; + } + + /** + * Stub some system services, allow access to others, and block the rest. + */ + @Override + public Object getSystemService(String name) { + if (mAllowedSystemServices.contains(name)) { + return mRealContext.getSystemService(name); + } + return super.getSystemService(name); + } + + /** + * Record when DownloadProvider starts DownloadService. + */ + @Override + public ComponentName startService(Intent service) { + if (service.getComponent().getClassName().equals(DownloadService.class.getName())) { + mHasServiceBeenStarted = true; + return service.getComponent(); + } + throw new UnsupportedOperationException("Unexpected service: " + service); + } + } + + public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) { + super(DownloadService.class); + mSystemFacade = systemFacade; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context realContext = getContext(); + mTestContext = new TestContext(realContext); + setupProviderAndResolver(); + + mTestContext.setResolver(mResolver); + setContext(mTestContext); + setupService(); + getService().mSystemFacade = mSystemFacade; + assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data + mServer = new MockWebServer(); + mServer.play(); + } + + @Override + protected void tearDown() throws Exception { + cleanUpDownloads(); + mServer.shutdown(); + super.tearDown(); + } + + private boolean isDatabaseEmpty() { + Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + null, null, null, null); + try { + return cursor.getCount() == 0; + } finally { + cursor.close(); + } + } + + void setupProviderAndResolver() { + DownloadProvider provider = new DownloadProvider(); + provider.mSystemFacade = mSystemFacade; + provider.attachInfo(mTestContext, null); + mResolver = new MockContentResolverWithNotify(); + mResolver.addProvider(PROVIDER_AUTHORITY, provider); + } + + /** + * Remove any downloaded files and delete any lingering downloads. + */ + void cleanUpDownloads() { + if (mResolver == null) { + return; + } + String[] columns = new String[] {Downloads.Impl._DATA}; + Cursor cursor = mResolver.query(Downloads.Impl.CONTENT_URI, columns, null, null, null); + try { + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + String filePath = cursor.getString(0); + if (filePath == null) continue; + Log.d(LOG_TAG, "Deleting " + filePath); + new File(filePath).delete(); + } + } finally { + cursor.close(); + } + mResolver.delete(Downloads.Impl.CONTENT_URI, null, null); + } + + void enqueueResponse(MockResponse resp) { + mServer.enqueue(resp); + } + + 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 buildEmptyResponse(int status) { + return buildResponse(status, ""); + } + + /** + * Fetch the last request received by the MockWebServer. + */ + protected RecordedRequest takeRequest() throws InterruptedException { + RecordedRequest request = mServer.takeRequest(); + assertNotNull("Expected request was not made", request); + return request; + } + + String getServerUri(String path) throws MalformedURLException, UnknownHostException { + return mServer.getUrl(path).toString(); + } + + public void runService() throws Exception { + startService(null); + mSystemFacade.runAllThreads(); + } + + protected String readStream(InputStream inputStream) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + try { + char[] buffer = new char[1024]; + int length = reader.read(buffer); + assertTrue("Failed to read anything from input stream", length > -1); + return String.valueOf(buffer, 0, length); + } finally { + reader.close(); + } + } + + protected void assertStartsWith(String expectedPrefix, String actual) { + String regex = "^" + expectedPrefix + ".*"; + MoreAsserts.assertMatchesRegex(regex, actual); + } +} 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/DownloadManagerFunctionalTest.java deleted file mode 100644 index e01b617f..00000000 --- a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java +++ /dev/null @@ -1,164 +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 com.android.providers.downloads; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.ConnectivityManager; -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.MockWebServer; -import tests.http.RecordedRequest; - -import java.io.InputStream; -import java.net.MalformedURLException; - -/** - * This test exercises the entire download manager working together -- it requests downloads through - * the {@link DownloadProvider}, just like a normal client would, and runs the - * {@link DownloadService} with start intents. It sets up a {@link MockWebServer} running on the - * device to serve downloads. - */ -@LargeTest -public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFunctionalTest { - private static final String TAG = "DownloadManagerFunctionalTest"; - - public DownloadManagerFunctionalTest() { - super(new FakeSystemFacade()); - } - - public void testDownloadTextFile() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); - - String path = "/download_manager_test_path"; - Uri downloadUri = requestDownload(path); - assertEquals(Downloads.Impl.STATUS_PENDING, getDownloadStatus(downloadUri)); - assertTrue(mTestContext.mHasServiceBeenStarted); - - runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); - RecordedRequest request = takeRequest(); - assertEquals("GET", request.getMethod()); - assertEquals(path, request.getPath()); - assertEquals(FILE_CONTENT, getDownloadContents(downloadUri)); - assertStartsWith(Environment.getExternalStorageDirectory().getPath(), - getDownloadFilename(downloadUri)); - } - - public void testDownloadToCache() throws Exception { - enqueueResponse(HTTP_OK, FILE_CONTENT); - Uri downloadUri = requestDownload("/path"); - updateDownload(downloadUri, Downloads.Impl.COLUMN_DESTINATION, - Integer.toString(Downloads.Impl.DESTINATION_CACHE_PARTITION)); - runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); - assertEquals(FILE_CONTENT, getDownloadContents(downloadUri)); - assertStartsWith(getContext().getCacheDir().getAbsolutePath(), - getDownloadFilename(downloadUri)); - } - - public void testRoaming() throws Exception { - 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); - - // when roaming is disallowed, the download should pause... - downloadUri = requestDownload("/path"); - updateDownload(downloadUri, Downloads.Impl.COLUMN_DESTINATION, - Integer.toString(Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING)); - 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); - } - - /** - * Read a downloaded file from disk. - */ - private String getDownloadContents(Uri downloadUri) throws Exception { - InputStream inputStream = mResolver.openInputStream(downloadUri); - try { - return readStream(inputStream); - } finally { - inputStream.close(); - } - } - - private void runUntilStatus(Uri downloadUri, int status) throws Exception { - runService(); - boolean done = false; - while (!done) { - int rslt = getDownloadStatus(downloadUri); - if (rslt == Downloads.Impl.STATUS_RUNNING || rslt == Downloads.Impl.STATUS_PENDING) { - Log.i(TAG, "status is: " + rslt + ", for: " + downloadUri); - DownloadHandler.getInstance().WaitUntilDownloadsTerminate(); - Thread.sleep(100); - } else { - done = true; - } - } - assertEquals(status, getDownloadStatus(downloadUri)); - } - - protected int getDownloadStatus(Uri downloadUri) { - return Integer.valueOf(getDownloadField(downloadUri, Downloads.Impl.COLUMN_STATUS)); - } - - private String getDownloadFilename(Uri downloadUri) { - return getDownloadField(downloadUri, Downloads.Impl._DATA); - } - - private String getDownloadField(Uri downloadUri, String column) { - final String[] columns = new String[] {column}; - Cursor cursor = mResolver.query(downloadUri, columns, null, null, null); - try { - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - return cursor.getString(0); - } finally { - cursor.close(); - } - } - - /** - * Request a download from the Download Manager. - */ - private Uri requestDownload(String path) throws MalformedURLException { - ContentValues values = new ContentValues(); - values.put(Downloads.Impl.COLUMN_URI, getServerUri(path)); - values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_EXTERNAL); - return mResolver.insert(Downloads.Impl.CONTENT_URI, values); - } - - /** - * Update one field of a download in the provider. - */ - private void updateDownload(Uri downloadUri, String column, String value) { - ContentValues values = new ContentValues(); - values.put(column, value); - int numChanged = mResolver.update(downloadUri, values, null, null); - assertEquals(1, numChanged); - } -} diff --git a/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java new file mode 100644 index 00000000..bbc5c3e0 --- /dev/null +++ b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java @@ -0,0 +1,167 @@ +/* + * 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 com.android.providers.downloads; + +import android.content.ContentValues; +import android.database.Cursor; +import android.net.ConnectivityManager; +import android.net.Uri; +import android.os.Environment; +import android.provider.Downloads; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +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 + * the {@link DownloadProvider}, just like a normal client would, and runs the + * {@link DownloadService} with start intents. It sets up a {@link MockWebServer} running on the + * device to serve downloads. + */ +@LargeTest +public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunctionalTest { + private static final String TAG = "DownloadManagerFunctionalTest"; + + public DownloadProviderFunctionalTest() { + super(new FakeSystemFacade()); + } + + public void testDownloadTextFile() throws Exception { + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + + String path = "/download_manager_test_path"; + Uri downloadUri = requestDownload(path); + assertEquals(Downloads.Impl.STATUS_PENDING, getDownloadStatus(downloadUri)); + assertTrue(mTestContext.mHasServiceBeenStarted); + + runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); + RecordedRequest request = takeRequest(); + assertEquals("GET", request.getMethod()); + assertEquals(path, request.getPath()); + assertEquals(FILE_CONTENT, getDownloadContents(downloadUri)); + assertStartsWith(Environment.getExternalStorageDirectory().getPath(), + getDownloadFilename(downloadUri)); + } + + public void testDownloadToCache() throws Exception { + enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); + + Uri downloadUri = requestDownload("/path"); + updateDownload(downloadUri, Downloads.Impl.COLUMN_DESTINATION, + Integer.toString(Downloads.Impl.DESTINATION_CACHE_PARTITION)); + runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); + assertEquals(FILE_CONTENT, getDownloadContents(downloadUri)); + assertStartsWith(getContext().getCacheDir().getAbsolutePath(), + getDownloadFilename(downloadUri)); + } + + 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 + Uri downloadUri = requestDownload("/path"); + runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); + + // when roaming is disallowed, the download should pause... + downloadUri = requestDownload("/path"); + updateDownload(downloadUri, Downloads.Impl.COLUMN_DESTINATION, + Integer.toString(Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING)); + runUntilStatus(downloadUri, Downloads.Impl.STATUS_WAITING_FOR_NETWORK); + + // ...and pick up when we're off roaming + mSystemFacade.mIsRoaming = false; + runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS); + } + + /** + * Read a downloaded file from disk. + */ + private String getDownloadContents(Uri downloadUri) throws Exception { + InputStream inputStream = mResolver.openInputStream(downloadUri); + try { + return readStream(inputStream); + } finally { + inputStream.close(); + } + } + + private void runUntilStatus(Uri downloadUri, int status) throws Exception { + runService(); + boolean done = false; + while (!done) { + int rslt = getDownloadStatus(downloadUri); + if (rslt == Downloads.Impl.STATUS_RUNNING || rslt == Downloads.Impl.STATUS_PENDING) { + Log.i(TAG, "status is: " + rslt + ", for: " + downloadUri); + DownloadHandler.getInstance().WaitUntilDownloadsTerminate(); + Thread.sleep(100); + } else { + done = true; + } + } + assertEquals(status, getDownloadStatus(downloadUri)); + } + + protected int getDownloadStatus(Uri downloadUri) { + return Integer.valueOf(getDownloadField(downloadUri, Downloads.Impl.COLUMN_STATUS)); + } + + private String getDownloadFilename(Uri downloadUri) { + return getDownloadField(downloadUri, Downloads.Impl._DATA); + } + + private String getDownloadField(Uri downloadUri, String column) { + final String[] columns = new String[] {column}; + Cursor cursor = mResolver.query(downloadUri, columns, null, null, null); + try { + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + return cursor.getString(0); + } finally { + cursor.close(); + } + } + + /** + * Request a download from the Download Manager. + */ + 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); + return mResolver.insert(Downloads.Impl.CONTENT_URI, values); + } + + /** + * Update one field of a download in the provider. + */ + private void updateDownload(Uri downloadUri, String column, String value) { + ContentValues values = new ContentValues(); + values.put(column, value); + int numChanged = mResolver.update(downloadUri, values, null, null); + assertEquals(1, numChanged); + } +} 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 headers = new HashMap(); - 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 getHeaders() { - List headerStrings = new ArrayList(); - 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 requestQueue - = new LinkedBlockingQueue(); - private final BlockingQueue responseQueue - = new LinkedBlockingQueue(); - 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> futures = new LinkedList>(); - - 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 drainRequests() { - List requests = new ArrayList(); - 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() { - 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() { - 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 headers = new ArrayList(); - 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 chunkSizes = new ArrayList(); - 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 headers; - private final List chunkSizes; - private final int bodySize; - private final byte[] body; - private final int sequenceNumber; - - RecordedRequest(String requestLine, List headers, List 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 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 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]; - } -} -- cgit v1.2.3