summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/providers/downloads/StorageTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/providers/downloads/StorageTest.java')
-rw-r--r--tests/src/com/android/providers/downloads/StorageTest.java248
1 files changed, 248 insertions, 0 deletions
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());
+ }
+ }
+}