From 23357198c440e6872d3aef3e608295db7f8273bc Mon Sep 17 00:00:00 2001 From: Steve Howard Date: Tue, 22 Jun 2010 18:20:55 -0700 Subject: Stub out the system clock in the download manager, add tests Introduce SystemFacade, an interface that allows us to stub out the system clock for testing the download manager. This allows us to test retrying a failed download without having the test wait 60 seconds. This interface can include other dependencies in the future as well. I've also used this to add tests for 503 (retry-after) and 301 (redirect), and I've added a test for download to the cache partition. Other changes: * made MockWebServer capable of checking + rethrowing exceptions from child threads * refactoring + cleanup of DownloadManagerFunctionalTest --- .../downloads/DownloadManagerFunctionalTest.java | 150 ++++++++++++++------- .../providers/downloads/FakeSystemFacade.java | 13 ++ tests/src/tests/http/MockWebServer.java | 33 ++++- 3 files changed, 142 insertions(+), 54 deletions(-) create mode 100644 tests/src/com/android/providers/downloads/FakeSystemFacade.java (limited to 'tests/src') diff --git a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java index 76b3d589..8d4655bb 100644 --- a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java @@ -26,7 +26,9 @@ import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.Environment; import android.provider.Downloads; +import android.test.MoreAsserts; import android.test.RenamingDelegatingContext; import android.test.ServiceTestCase; import android.test.mock.MockContentResolver; @@ -55,18 +57,23 @@ import java.util.Set; */ @LargeTest public class DownloadManagerFunctionalTest extends ServiceTestCase { - private static final int HTTP_PARTIAL_CONTENT = 206; + private static final String LOG_TAG = "DownloadManagerFunctionalTest"; + private static final String PROVIDER_AUTHORITY = "downloads"; + private static final int RETRY_DELAY_MILLIS = 61 * 1000; + private static final long REQUEST_TIMEOUT_MILLIS = 10 * 1000; + private static final String FILE_CONTENT = "hello world"; + private static final int HTTP_OK = 200; - private static final String LOG_TAG = "DownloadManagerFunctionalTest"; + private static final int HTTP_PARTIAL_CONTENT = 206; private static final int HTTP_NOT_FOUND = 404; - private static final String FILE_CONTENT = "hello world"; - private static final long REQUEST_TIMEOUT_MILLIS = 5000; + private static final int HTTP_SERVICE_UNAVAILABLE = 503; private MockWebServer mServer; // resolves requests to the DownloadProvider we set up private MockContentResolver mResolver; private TestContext mTestContext; + private FakeSystemFacade mSystemFacade; /** * Context passed to the provider and the service. Allows most methods to pass through to the @@ -148,6 +155,9 @@ public class DownloadManagerFunctionalTest extends ServiceTestCase headers = request.getHeaders(); assertTrue("No Range header: " + headers, headers.contains("Range: bytes=" + initialLength + "-")); assertTrue("No ETag header: " + headers, headers.contains("If-Match: " + etag)); - assertEquals(FILE_CONTENT, getDownloadContents(downloadUri)); - checkForUnexpectedRequests(); + } + + private void assertStartsWith(String expectedPrefix, String actual) { + String regex = "^" + expectedPrefix + ".*"; + MoreAsserts.assertMatchesRegex(regex, actual); } /** @@ -295,8 +327,19 @@ public class DownloadManagerFunctionalTest extends ServiceTestCase startTimeMillis + REQUEST_TIMEOUT_MILLIS) { fail("Download timed out with status " + status); } - try { - Thread.sleep(100); - } catch (InterruptedException exc) { - // no problem - } + Thread.sleep(100); + mServer.checkForExceptions(); status = getDownloadStatus(downloadUri); } @@ -348,12 +388,20 @@ public class DownloadManagerFunctionalTest extends ServiceTestCase extraRequests = mServer.drainRequests(); - assertEquals("Invalid requests: " + extraRequests.toString(), 0, extraRequests.size()); - } } diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java new file mode 100644 index 00000000..b75e663a --- /dev/null +++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java @@ -0,0 +1,13 @@ +package com.android.providers.downloads; + +public class FakeSystemFacade implements SystemFacade { + private long mTimeMillis = 0; + + void incrementTimeMillis(long delta) { + mTimeMillis += delta; + } + + public long currentTimeMillis() { + return mTimeMillis; + } +} diff --git a/tests/src/tests/http/MockWebServer.java b/tests/src/tests/http/MockWebServer.java index b2cb8d7b..11c8063e 100644 --- a/tests/src/tests/http/MockWebServer.java +++ b/tests/src/tests/http/MockWebServer.java @@ -27,13 +27,18 @@ 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 @@ -50,6 +55,8 @@ public final class MockWebServer { = 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; @@ -107,7 +114,7 @@ public final class MockWebServer { final ServerSocket ss = new ServerSocket(0); ss.setReuseAddress(true); port = ss.getLocalPort(); - executor.submit(new Callable() { + submitCallable(new Callable() { public Void call() throws Exception { int count = 0; while (true) { @@ -125,7 +132,7 @@ public final class MockWebServer { } private void serveConnection(final Socket s) { - executor.submit(new Callable() { + submitCallable(new Callable() { public Void call() throws Exception { InputStream in = new BufferedInputStream(s.getInputStream()); OutputStream out = new BufferedOutputStream(s.getOutputStream()); @@ -156,6 +163,28 @@ public final class MockWebServer { }); } + 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. */ -- cgit v1.2.3