summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-01-30 15:01:39 -0800
committerJeff Sharkey <jsharkey@android.com>2014-02-06 10:42:46 -0800
commitdffbb9c4567e9d29d19964a83129e38dceab7055 (patch)
tree773bb59bc04f75e19e3a39acba06de574f75a385 /tests
parent9b731a5521f569c91aeb419d43fa098a34cf78cb (diff)
downloadandroid_packages_providers_DownloadProvider-dffbb9c4567e9d29d19964a83129e38dceab7055.tar.gz
android_packages_providers_DownloadProvider-dffbb9c4567e9d29d19964a83129e38dceab7055.tar.bz2
android_packages_providers_DownloadProvider-dffbb9c4567e9d29d19964a83129e38dceab7055.zip
Many improvements to download storage management.
Change all data transfer to occur through FileDescriptors instead of relying on local files. This paves the way for downloading directly to content:// Uris in the future. Rewrite storage management logic to preflight download when size is known. If enough space is found, immediately reserve the space with fallocate(), advising the kernel block allocator to try giving us a contiguous block regions to reduce fragmentation. When preflighting on internal storage or emulated external storage, ask PackageManager to clear private app caches to free up space. Since we fallocate() the entire file, use the database as the source of truth for resume locations, which requires that we fsync() before each database update. Store in-progress downloads in separate directories to keep the OS from deleting out from under us. Clean up filename generation logic to break ties in this new dual-directory case. Clearer enforcement of successful download preconditions around content lengths and ETags. Move all database field mutations to clearer DownloadInfoDelta object, and write back through single code path. Catch and log uncaught exceptions from DownloadThread. Tests to verify new storage behaviors. Fixed existing test to reflect correct RFC behavior. Bug: 5287571, 3213677, 12663412 Change-Id: I6bb905eca7c7d1a6bc88df3db28b65d70f660221
Diffstat (limited to 'tests')
-rw-r--r--tests/AndroidManifest.xml2
-rw-r--r--tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java36
-rw-r--r--tests/src/com/android/providers/downloads/AbstractPublicApiTest.java15
-rw-r--r--tests/src/com/android/providers/downloads/FakeSystemFacade.java4
-rw-r--r--tests/src/com/android/providers/downloads/HelpersTest.java68
-rw-r--r--tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java14
-rw-r--r--tests/src/com/android/providers/downloads/StorageTest.java248
-rw-r--r--tests/src/com/android/providers/downloads/ThreadingTest.java11
8 files changed, 352 insertions, 46 deletions
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index d520123f..ec73ca2e 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -19,6 +19,8 @@
package="com.android.providers.downloads.tests"
android:sharedUserId="android.media">
+ <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
index 3b937389..28c5dc7d 100644
--- a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
@@ -74,15 +74,18 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
static class MockContentResolverWithNotify extends MockContentResolver {
public boolean mNotifyWasCalled = false;
+ public MockContentResolverWithNotify(Context context) {
+ super(context);
+ }
+
public synchronized void resetNotified() {
mNotifyWasCalled = false;
}
@Override
- public synchronized void notifyChange(Uri uri, ContentObserver observer,
- boolean syncToNetwork) {
+ public synchronized void notifyChange(
+ Uri uri, ContentObserver observer, boolean syncToNetwork) {
mNotifyWasCalled = true;
- notifyAll();
}
}
@@ -94,20 +97,17 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
static class TestContext extends RenamingDelegatingContext {
private static final String FILENAME_PREFIX = "test.";
- private ContentResolver mResolver;
+ private final ContentResolver mResolver;
private final NotificationManager mNotifManager;
boolean mHasServiceBeenStarted = false;
public TestContext(Context realContext) {
super(realContext, FILENAME_PREFIX);
+ mResolver = new MockContentResolverWithNotify(this);
mNotifManager = mock(NotificationManager.class);
}
- public void setResolver(ContentResolver resolver) {
- mResolver = resolver;
- }
-
/**
* Direct DownloadService to our test instance of DownloadProvider.
*/
@@ -156,12 +156,20 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
final Context realContext = getContext();
+
mTestContext = new TestContext(realContext);
- setupProviderAndResolver();
- mTestContext.setResolver(mResolver);
+ mResolver = (MockContentResolverWithNotify) mTestContext.getContentResolver();
+
+ final DownloadProvider provider = new DownloadProvider();
+ provider.mSystemFacade = mSystemFacade;
+ provider.attachInfo(mTestContext, null);
+
+ mResolver.addProvider(PROVIDER_AUTHORITY, provider);
+
setContext(mTestContext);
setupService();
getService().mSystemFacade = mSystemFacade;
+
mSystemFacade.setUp();
assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data
mServer = new MockWebServer();
@@ -186,14 +194,6 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
}
}
- 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.
*/
diff --git a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
index 348dbd1b..2846c7af 100644
--- a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
@@ -28,6 +28,9 @@ import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.util.Log;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
@@ -91,19 +94,23 @@ public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunc
}
}
- String getContents() throws Exception {
+ byte[] getRawContents() throws Exception {
ParcelFileDescriptor downloadedFile = mManager.openDownloadedFile(mId);
assertTrue("Invalid file descriptor: " + downloadedFile,
downloadedFile.getFileDescriptor().valid());
- final InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
+ final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
downloadedFile);
try {
- return readStream(stream);
+ return Streams.readFully(is);
} finally {
- stream.close();
+ IoUtils.closeQuietly(is);
}
}
+ String getContents() throws Exception {
+ return new String(getRawContents());
+ }
+
void runUntilStatus(int status) throws TimeoutException {
final long startMillis = mSystemFacade.currentTimeMillis();
startService(null);
diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
index d54c1224..5a15d399 100644
--- a/tests/src/com/android/providers/downloads/FakeSystemFacade.java
+++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
@@ -64,12 +64,12 @@ public class FakeSystemFacade implements SystemFacade {
@Override
public Long getMaxBytesOverMobile() {
- return mMaxBytesOverMobile ;
+ return mMaxBytesOverMobile;
}
@Override
public Long getRecommendedMaxBytesOverMobile() {
- return mRecommendedMaxBytesOverMobile ;
+ return mRecommendedMaxBytesOverMobile;
}
@Override
diff --git a/tests/src/com/android/providers/downloads/HelpersTest.java b/tests/src/com/android/providers/downloads/HelpersTest.java
index 50f4c44c..121b7cda 100644
--- a/tests/src/com/android/providers/downloads/HelpersTest.java
+++ b/tests/src/com/android/providers/downloads/HelpersTest.java
@@ -16,29 +16,73 @@
package com.android.providers.downloads;
+import android.net.Uri;
import android.provider.Downloads;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
/**
* This test exercises methods in the {@Helpers} utility class.
*/
-@LargeTest
+@SmallTest
public class HelpersTest extends AndroidTestCase {
- public HelpersTest() {
+ @Override
+ protected void tearDown() throws Exception {
+ IoUtils.deleteContents(getContext().getFilesDir());
+ IoUtils.deleteContents(getContext().getCacheDir());
+
+ super.tearDown();
+ }
+
+ public void testGenerateSaveFile() throws Exception {
+ final File expected = new File(getContext().getFilesDir(), "file.mp4");
+ final String actual = Helpers.generateSaveFile(getContext(),
+ "http://example.com/file.txt", null, null, null,
+ "video/mp4", Downloads.Impl.DESTINATION_CACHE_PARTITION);
+ assertEquals(expected.getAbsolutePath(), actual);
+ }
+
+ public void testGenerateSaveFileDupes() throws Exception {
+ final File expected1 = new File(getContext().getFilesDir(), "file.txt");
+ final String actual1 = Helpers.generateSaveFile(getContext(), "http://example.com/file.txt",
+ null, null, null, null, Downloads.Impl.DESTINATION_CACHE_PARTITION);
+
+ final File expected2 = new File(getContext().getFilesDir(), "file-1.txt");
+ final String actual2 = Helpers.generateSaveFile(getContext(), "http://example.com/file.txt",
+ null, null, null, null, Downloads.Impl.DESTINATION_CACHE_PARTITION);
+
+ assertEquals(expected1.getAbsolutePath(), actual1);
+ assertEquals(expected2.getAbsolutePath(), actual2);
}
- public void testGetFullPath() throws Exception {
- String hint = "file:///com.android.providers.downloads/test";
+ public void testGenerateSaveFileNoExtension() throws Exception {
+ final File expected = new File(getContext().getFilesDir(), "file.mp4");
+ final String actual = Helpers.generateSaveFile(getContext(),
+ "http://example.com/file", null, null, null,
+ "video/mp4", Downloads.Impl.DESTINATION_CACHE_PARTITION);
+ assertEquals(expected.getAbsolutePath(), actual);
+ }
+
+ public void testGenerateSaveFileHint() throws Exception {
+ final File expected = new File(getContext().getFilesDir(), "meow");
+ final String hint = Uri.fromFile(expected).toString();
- // Test that we never change requested filename.
- String fileName = Helpers.getFullPath(
- hint,
- "video/mp4", // MIME type corresponding to file extension .mp4
- Downloads.Impl.DESTINATION_FILE_URI,
- null);
- assertEquals(hint, fileName);
+ // Test that we never change requested filename.
+ final String actual = Helpers.generateSaveFile(getContext(), "url", hint,
+ "dispo", "locat", "video/mp4", Downloads.Impl.DESTINATION_FILE_URI);
+ assertEquals(expected.getAbsolutePath(), actual);
}
+ public void testGenerateSaveFileDisposition() throws Exception {
+ final File expected = new File(getContext().getFilesDir(), "real.mp4");
+ final String actual = Helpers.generateSaveFile(getContext(),
+ "http://example.com/file.txt", null, "attachment; filename=\"subdir/real.pdf\"",
+ null, "video/mp4", Downloads.Impl.DESTINATION_CACHE_PARTITION);
+ assertEquals(expected.getAbsolutePath(), actual);
+ }
}
diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
index bde95815..d7b389c5 100644
--- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
@@ -53,6 +53,8 @@ import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.RecordedRequest;
import com.google.mockwebserver.SocketPolicy;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -83,9 +85,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
+ "download_manager_functional_test");
if (mTestDirectory.exists()) {
- for (File file : mTestDirectory.listFiles()) {
- file.delete();
- }
+ IoUtils.deleteContents(mTestDirectory);
} else {
mTestDirectory.mkdir();
}
@@ -94,9 +94,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
@Override
protected void tearDown() throws Exception {
if (mTestDirectory != null && mTestDirectory.exists()) {
- for (File file : mTestDirectory.listFiles()) {
- file.delete();
- }
+ IoUtils.deleteContents(mTestDirectory);
mTestDirectory.delete();
}
super.tearDown();
@@ -223,7 +221,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
boolean isFirstResponse = (start == 0);
int status = isFirstResponse ? HTTP_OK : HTTP_PARTIAL;
MockResponse response = buildResponse(status, FILE_CONTENT.substring(start, end))
- .setHeader("Content-length", totalLength)
+ .setHeader("Content-length", isFirstResponse ? totalLength : (end - start))
.setHeader("Etag", ETAG);
if (!isFirstResponse) {
response.setHeader(
@@ -475,7 +473,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
// 2. Try resuming A, but fail ETag check
mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
download.runUntilStatus(STATUS_FAILED);
- assertEquals(HTTP_PRECON_FAILED, download.getReason());
+ assertEquals(DownloadManager.ERROR_CANNOT_RESUME, download.getReason());
req = takeRequest();
assertEquals("bytes=2-", getHeaderValue(req, "Range"));
assertEquals(A, getHeaderValue(req, "If-Match"));
diff --git a/tests/src/com/android/providers/downloads/StorageTest.java b/tests/src/com/android/providers/downloads/StorageTest.java
new file mode 100644
index 00000000..eaac3bdc
--- /dev/null
+++ b/tests/src/com/android/providers/downloads/StorageTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 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 static android.app.DownloadManager.COLUMN_REASON;
+import static android.app.DownloadManager.ERROR_INSUFFICIENT_SPACE;
+import static android.app.DownloadManager.STATUS_FAILED;
+import static android.app.DownloadManager.STATUS_SUCCESSFUL;
+import static android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION;
+import static android.provider.Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION;
+
+import android.app.DownloadManager;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.StatFs;
+import android.provider.Downloads.Impl;
+import android.test.MoreAsserts;
+import android.util.Log;
+
+import com.android.providers.downloads.StorageUtils.ObserverLatch;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.SocketPolicy;
+
+import libcore.io.ErrnoException;
+import libcore.io.ForwardingOs;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.Os;
+import libcore.io.StructStatVfs;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+public class StorageTest extends AbstractPublicApiTest {
+ private static final String TAG = "StorageTest";
+
+ private static final int DOWNLOAD_SIZE = 512 * 1024;
+ private static final byte[] DOWNLOAD_BODY;
+
+ static {
+ DOWNLOAD_BODY = new byte[DOWNLOAD_SIZE];
+ for (int i = 0; i < DOWNLOAD_SIZE; i++) {
+ DOWNLOAD_BODY[i] = (byte) (i % 32);
+ }
+ }
+
+ private Os mOriginal;
+ private long mStealBytes;
+
+ public StorageTest() {
+ super(new FakeSystemFacade());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ StorageUtils.sForceFullEviction = true;
+ mStealBytes = 0;
+
+ mOriginal = Libcore.os;
+ Libcore.os = new ForwardingOs(mOriginal) {
+ @Override
+ public StructStatVfs statvfs(String path) throws ErrnoException {
+ return stealBytes(os.statvfs(path));
+ }
+
+ @Override
+ public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException {
+ return stealBytes(os.fstatvfs(fd));
+ }
+
+ private StructStatVfs stealBytes(StructStatVfs s) {
+ final long stealBlocks = (mStealBytes + (s.f_bsize - 1)) / s.f_bsize;
+ final long f_bavail = s.f_bavail - stealBlocks;
+ return new StructStatVfs(s.f_bsize, s.f_frsize, s.f_blocks, s.f_bfree, f_bavail,
+ s.f_files, s.f_ffree, s.f_favail, s.f_fsid, s.f_flag, s.f_namemax);
+ }
+ };
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ StorageUtils.sForceFullEviction = false;
+ mStealBytes = 0;
+
+ if (mOriginal != null) {
+ Libcore.os = mOriginal;
+ }
+ }
+
+ private enum CacheStatus { CLEAN, DIRTY }
+ private enum BodyType { COMPLETE, CHUNKED }
+
+ public void testDataDirtyComplete() throws Exception {
+ prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
+ CacheStatus.DIRTY, BodyType.COMPLETE,
+ STATUS_SUCCESSFUL, -1);
+ }
+
+ public void testDataDirtyChunked() throws Exception {
+ prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
+ CacheStatus.DIRTY, BodyType.CHUNKED,
+ STATUS_SUCCESSFUL, -1);
+ }
+
+ public void testDataCleanComplete() throws Exception {
+ prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
+ CacheStatus.CLEAN, BodyType.COMPLETE,
+ STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
+ }
+
+ public void testDataCleanChunked() throws Exception {
+ prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
+ CacheStatus.CLEAN, BodyType.CHUNKED,
+ STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
+ }
+
+ public void testCacheDirtyComplete() throws Exception {
+ prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
+ CacheStatus.DIRTY, BodyType.COMPLETE,
+ STATUS_SUCCESSFUL, -1);
+ }
+
+ public void testCacheDirtyChunked() throws Exception {
+ prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
+ CacheStatus.DIRTY, BodyType.CHUNKED,
+ STATUS_SUCCESSFUL, -1);
+ }
+
+ public void testCacheCleanComplete() throws Exception {
+ prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
+ CacheStatus.CLEAN, BodyType.COMPLETE,
+ STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
+ }
+
+ public void testCacheCleanChunked() throws Exception {
+ prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
+ CacheStatus.CLEAN, BodyType.CHUNKED,
+ STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
+ }
+
+ private void prepareAndRunDownload(
+ int dest, CacheStatus cache, BodyType body, int expectedStatus, int expectedReason)
+ throws Exception {
+
+ // Ensure that we've purged everything possible for destination
+ final File dirtyDir;
+ if (dest == DESTINATION_CACHE_PARTITION) {
+ final PackageManager pm = getContext().getPackageManager();
+ final ObserverLatch observer = new ObserverLatch();
+ pm.freeStorageAndNotify(Long.MAX_VALUE, observer);
+
+ try {
+ if (!observer.latch.await(30, TimeUnit.SECONDS)) {
+ throw new IOException("Timeout while freeing disk space");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ dirtyDir = getContext().getCacheDir();
+
+ } else if (dest == DESTINATION_SYSTEMCACHE_PARTITION) {
+ IoUtils.deleteContents(Environment.getDownloadCacheDirectory());
+ dirtyDir = Environment.getDownloadCacheDirectory();
+
+ } else {
+ throw new IllegalArgumentException("Unknown destination");
+ }
+
+ // Allocate a cache file, if requested, making it large enough and old
+ // enough to clear.
+ final File dirtyFile;
+ if (cache == CacheStatus.DIRTY) {
+ dirtyFile = new File(dirtyDir, "cache_file.bin");
+ assertTrue(dirtyFile.createNewFile());
+ final FileOutputStream os = new FileOutputStream(dirtyFile);
+ final int dirtySize = (DOWNLOAD_SIZE * 3) / 2;
+ Libcore.os.posix_fallocate(os.getFD(), 0, dirtySize);
+ IoUtils.closeQuietly(os);
+
+ dirtyFile.setLastModified(
+ System.currentTimeMillis() - (StorageUtils.MIN_DELETE_AGE * 2));
+ } else {
+ dirtyFile = null;
+ }
+
+ // At this point, hide all other disk space to make the download fail;
+ // if we have a dirty cache file it can be cleared to let us proceed.
+ final long targetFree = StorageUtils.RESERVED_BYTES + (DOWNLOAD_SIZE / 2);
+
+ final StatFs stat = new StatFs(dirtyDir.getAbsolutePath());
+ Log.d(TAG, "Available bytes (before steal): " + stat.getAvailableBytes());
+ mStealBytes = stat.getAvailableBytes() - targetFree;
+
+ stat.restat(dirtyDir.getAbsolutePath());
+ Log.d(TAG, "Available bytes (after steal): " + stat.getAvailableBytes());
+
+ final MockResponse resp = new MockResponse().setResponseCode(200)
+ .setHeader("Content-type", "text/plain")
+ .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
+ if (body == BodyType.CHUNKED) {
+ resp.setChunkedBody(DOWNLOAD_BODY, 1021);
+ } else {
+ resp.setBody(DOWNLOAD_BODY);
+ }
+ enqueueResponse(resp);
+
+ final DownloadManager.Request req = getRequest();
+ if (dest == Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
+ req.setDestinationToSystemCache();
+ }
+ final Download download = enqueueRequest(req);
+ download.runUntilStatus(expectedStatus);
+
+ if (expectedStatus == STATUS_SUCCESSFUL) {
+ MoreAsserts.assertEquals(DOWNLOAD_BODY, download.getRawContents());
+ }
+
+ if (expectedReason != -1) {
+ assertEquals(expectedReason, download.getLongField(COLUMN_REASON));
+ }
+
+ if (dirtyFile != null) {
+ assertFalse(dirtyFile.exists());
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/downloads/ThreadingTest.java b/tests/src/com/android/providers/downloads/ThreadingTest.java
index 920f703b..1e501444 100644
--- a/tests/src/com/android/providers/downloads/ThreadingTest.java
+++ b/tests/src/com/android/providers/downloads/ThreadingTest.java
@@ -27,6 +27,7 @@ import com.google.android.collect.Sets;
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.SocketPolicy;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -60,9 +61,10 @@ public class ThreadingTest extends AbstractPublicApiTest {
public void testFilenameRace() throws Exception {
final List<Pair<Download, String>> downloads = Lists.newArrayList();
+ final HashSet<String> expectedBodies = Sets.newHashSet();
// Request dozen files at once with same name
- for (int i = 0; i < 12; i++) {
+ for (int i = 0; i < 32; i++) {
final String body = "DOWNLOAD " + i + " CONTENTS";
enqueueResponse(new MockResponse().setResponseCode(HTTP_OK).setBody(body)
.setHeader("Content-type", "text/plain")
@@ -70,6 +72,7 @@ public class ThreadingTest extends AbstractPublicApiTest {
final Download d = enqueueRequest(getRequest());
downloads.add(Pair.create(d, body));
+ expectedBodies.add(body);
}
// Kick off downloads in parallel
@@ -82,6 +85,7 @@ public class ThreadingTest extends AbstractPublicApiTest {
// Ensure that contents are clean and filenames unique
final Set<String> seenFiles = Sets.newHashSet();
+ final HashSet<String> actualBodies = Sets.newHashSet();
for (Pair<Download, String> d : downloads) {
final String file = d.first.getStringField(DownloadManager.COLUMN_LOCAL_FILENAME);
@@ -91,7 +95,10 @@ public class ThreadingTest extends AbstractPublicApiTest {
final String expected = d.second;
final String actual = d.first.getContents();
- assertEquals(expected, actual);
+
+ actualBodies.add(actual);
}
+
+ assertEquals(expectedBodies, actualBodies);
}
}