am cc167f03: (-s ours) am bb611587: (-s ours) Import translations. DO NOT MERGE
[android/platform/packages/providers/DownloadProvider.git] / tests / src / com / android / providers / downloads / StorageTest.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.providers.downloads;
18
19 import static android.app.DownloadManager.COLUMN_REASON;
20 import static android.app.DownloadManager.ERROR_INSUFFICIENT_SPACE;
21 import static android.app.DownloadManager.STATUS_FAILED;
22 import static android.app.DownloadManager.STATUS_SUCCESSFUL;
23 import static android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION;
24 import static android.provider.Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION;
25
26 import android.app.DownloadManager;
27 import android.content.pm.PackageManager;
28 import android.os.Environment;
29 import android.os.StatFs;
30 import android.provider.Downloads.Impl;
31 import android.system.ErrnoException;
32 import android.system.Os;
33 import android.system.StructStatVfs;
34 import android.test.MoreAsserts;
35 import android.util.Log;
36
37 import com.android.providers.downloads.StorageUtils.ObserverLatch;
38 import com.google.mockwebserver.MockResponse;
39 import com.google.mockwebserver.SocketPolicy;
40
41 import libcore.io.ForwardingOs;
42 import libcore.io.IoUtils;
43
44 import java.io.File;
45 import java.io.FileDescriptor;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.util.concurrent.TimeUnit;
49
50 public class StorageTest extends AbstractPublicApiTest {
51     private static final String TAG = "StorageTest";
52
53     private static final int DOWNLOAD_SIZE = 512 * 1024;
54     private static final byte[] DOWNLOAD_BODY;
55
56     static {
57         DOWNLOAD_BODY = new byte[DOWNLOAD_SIZE];
58         for (int i = 0; i < DOWNLOAD_SIZE; i++) {
59             DOWNLOAD_BODY[i] = (byte) (i % 32);
60         }
61     }
62
63     private libcore.io.Os mOriginal;
64     private long mStealBytes;
65
66     public StorageTest() {
67         super(new FakeSystemFacade());
68     }
69
70     @Override
71     protected void setUp() throws Exception {
72         super.setUp();
73
74         StorageUtils.sForceFullEviction = true;
75         mStealBytes = 0;
76
77         mOriginal = libcore.io.Libcore.os;
78         libcore.io.Libcore.os = new ForwardingOs(mOriginal) {
79             @Override
80             public StructStatVfs statvfs(String path) throws ErrnoException {
81                 return stealBytes(os.statvfs(path));
82             }
83
84             @Override
85             public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException {
86                 return stealBytes(os.fstatvfs(fd));
87             }
88
89             private StructStatVfs stealBytes(StructStatVfs s) {
90                 final long stealBlocks = (mStealBytes + (s.f_bsize - 1)) / s.f_bsize;
91                 final long f_bavail = s.f_bavail - stealBlocks;
92                 return new StructStatVfs(s.f_bsize, s.f_frsize, s.f_blocks, s.f_bfree, f_bavail,
93                         s.f_files, s.f_ffree, s.f_favail, s.f_fsid, s.f_flag, s.f_namemax);
94             }
95         };
96     }
97
98     @Override
99     protected void tearDown() throws Exception {
100         super.tearDown();
101
102         StorageUtils.sForceFullEviction = false;
103         mStealBytes = 0;
104
105         if (mOriginal != null) {
106             libcore.io.Libcore.os = mOriginal;
107         }
108     }
109
110     private enum CacheStatus { CLEAN, DIRTY }
111     private enum BodyType { COMPLETE, CHUNKED }
112
113     public void testDataDirtyComplete() throws Exception {
114         prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
115                 CacheStatus.DIRTY, BodyType.COMPLETE,
116                 STATUS_SUCCESSFUL, -1);
117     }
118
119     public void testDataDirtyChunked() throws Exception {
120         prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
121                 CacheStatus.DIRTY, BodyType.CHUNKED,
122                 STATUS_SUCCESSFUL, -1);
123     }
124
125     public void testDataCleanComplete() throws Exception {
126         prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
127                 CacheStatus.CLEAN, BodyType.COMPLETE,
128                 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
129     }
130
131     public void testDataCleanChunked() throws Exception {
132         prepareAndRunDownload(DESTINATION_CACHE_PARTITION,
133                 CacheStatus.CLEAN, BodyType.CHUNKED,
134                 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
135     }
136
137     public void testCacheDirtyComplete() throws Exception {
138         prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
139                 CacheStatus.DIRTY, BodyType.COMPLETE,
140                 STATUS_SUCCESSFUL, -1);
141     }
142
143     public void testCacheDirtyChunked() throws Exception {
144         prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
145                 CacheStatus.DIRTY, BodyType.CHUNKED,
146                 STATUS_SUCCESSFUL, -1);
147     }
148
149     public void testCacheCleanComplete() throws Exception {
150         prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
151                 CacheStatus.CLEAN, BodyType.COMPLETE,
152                 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
153     }
154
155     public void testCacheCleanChunked() throws Exception {
156         prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION,
157                 CacheStatus.CLEAN, BodyType.CHUNKED,
158                 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE);
159     }
160
161     private void prepareAndRunDownload(
162             int dest, CacheStatus cache, BodyType body, int expectedStatus, int expectedReason)
163             throws Exception {
164
165         // Ensure that we've purged everything possible for destination
166         final File dirtyDir;
167         if (dest == DESTINATION_CACHE_PARTITION) {
168             final PackageManager pm = getContext().getPackageManager();
169             final ObserverLatch observer = new ObserverLatch();
170             pm.freeStorageAndNotify(Long.MAX_VALUE, observer);
171
172             try {
173                 if (!observer.latch.await(30, TimeUnit.SECONDS)) {
174                     throw new IOException("Timeout while freeing disk space");
175                 }
176             } catch (InterruptedException e) {
177                 Thread.currentThread().interrupt();
178             }
179
180             dirtyDir = getContext().getCacheDir();
181
182         } else if (dest == DESTINATION_SYSTEMCACHE_PARTITION) {
183             IoUtils.deleteContents(Environment.getDownloadCacheDirectory());
184             dirtyDir = Environment.getDownloadCacheDirectory();
185
186         } else {
187             throw new IllegalArgumentException("Unknown destination");
188         }
189
190         // Allocate a cache file, if requested, making it large enough and old
191         // enough to clear.
192         final File dirtyFile;
193         if (cache == CacheStatus.DIRTY) {
194             dirtyFile = new File(dirtyDir, "cache_file.bin");
195             assertTrue(dirtyFile.createNewFile());
196             final FileOutputStream os = new FileOutputStream(dirtyFile);
197             final int dirtySize = (DOWNLOAD_SIZE * 3) / 2;
198             Os.posix_fallocate(os.getFD(), 0, dirtySize);
199             IoUtils.closeQuietly(os);
200
201             dirtyFile.setLastModified(
202                     System.currentTimeMillis() - (StorageUtils.MIN_DELETE_AGE * 2));
203         } else {
204             dirtyFile = null;
205         }
206
207         // At this point, hide all other disk space to make the download fail;
208         // if we have a dirty cache file it can be cleared to let us proceed.
209         final long targetFree = StorageUtils.RESERVED_BYTES + (DOWNLOAD_SIZE / 2);
210
211         final StatFs stat = new StatFs(dirtyDir.getAbsolutePath());
212         Log.d(TAG, "Available bytes (before steal): " + stat.getAvailableBytes());
213         mStealBytes = stat.getAvailableBytes() - targetFree;
214
215         stat.restat(dirtyDir.getAbsolutePath());
216         Log.d(TAG, "Available bytes (after steal): " + stat.getAvailableBytes());
217
218         final MockResponse resp = new MockResponse().setResponseCode(200)
219                 .setHeader("Content-type", "text/plain")
220                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
221         if (body == BodyType.CHUNKED) {
222             resp.setChunkedBody(DOWNLOAD_BODY, 1021);
223         } else {
224             resp.setBody(DOWNLOAD_BODY);
225         }
226         enqueueResponse(resp);
227
228         final DownloadManager.Request req = getRequest();
229         if (dest == Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
230             req.setDestinationToSystemCache();
231         }
232         final Download download = enqueueRequest(req);
233         download.runUntilStatus(expectedStatus);
234
235         if (expectedStatus == STATUS_SUCCESSFUL) {
236             MoreAsserts.assertEquals(DOWNLOAD_BODY, download.getRawContents());
237         }
238
239         if (expectedReason != -1) {
240             assertEquals(expectedReason, download.getLongField(COLUMN_REASON));
241         }
242
243         if (dirtyFile != null) {
244             assertFalse(dirtyFile.exists());
245         }
246     }
247 }