e34c66e6c19d86f7bdc9258cae151c893be0257c
[android/platform/packages/providers/DownloadProvider.git] / tests / src / com / android / providers / downloads / PublicApiFunctionalTest.java
1 /*
2  * Copyright (C) 2010 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 android.database.Cursor;
20 import android.net.ConnectivityManager;
21 import android.net.DownloadManager;
22 import android.net.Uri;
23 import android.os.Environment;
24 import android.test.suitebuilder.annotation.LargeTest;
25 import tests.http.RecordedRequest;
26
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.InputStream;
30 import java.net.MalformedURLException;
31
32 @LargeTest
33 public class PublicApiFunctionalTest extends AbstractDownloadManagerFunctionalTest {
34     private static final String REQUEST_PATH = "/path";
35
36     class Download implements StatusReader {
37         final long mId;
38
39         private Download(long downloadId) {
40             this.mId = downloadId;
41         }
42
43         public int getStatus() {
44             return (int) getLongField(DownloadManager.COLUMN_STATUS);
45         }
46
47         public boolean isComplete(int status) {
48             return status != DownloadManager.STATUS_PENDING
49                     && status != DownloadManager.STATUS_RUNNING
50                     && status != DownloadManager.STATUS_PAUSED;
51         }
52
53         String getStringField(String field) {
54             Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
55             try {
56                 assertEquals(1, cursor.getCount());
57                 cursor.moveToFirst();
58                 return cursor.getString(cursor.getColumnIndexOrThrow(field));
59             } finally {
60                 cursor.close();
61             }
62         }
63
64         long getLongField(String field) {
65             Cursor cursor = mManager.query(new DownloadManager.Query().setFilterById(mId));
66             try {
67                 assertEquals(1, cursor.getCount());
68                 cursor.moveToFirst();
69                 return cursor.getLong(cursor.getColumnIndexOrThrow(field));
70             } finally {
71                 cursor.close();
72             }
73         }
74
75         String getContents() throws Exception {
76             InputStream stream = new FileInputStream(
77                     mManager.openDownloadedFile(mId).getFileDescriptor());
78             try {
79                 return readStream(stream);
80             } finally {
81                 stream.close();
82             }
83         }
84
85         RecordedRequest runUntilStatus(int status) throws Exception {
86             return PublicApiFunctionalTest.this.runUntilStatus(this, status);
87         }
88     }
89
90     private DownloadManager mManager;
91     private File mTestDirectory;
92
93     @Override
94     protected void setUp() throws Exception {
95         super.setUp();
96         mManager = new DownloadManager(mResolver);
97
98         mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
99                                   + "download_manager_functional_test");
100         if (mTestDirectory.exists()) {
101             throw new RuntimeException(
102                     "Test directory on external storage already exists, cannot run");
103         }
104         if (!mTestDirectory.mkdir()) {
105             throw new RuntimeException("Couldn't create test directory: "
106                                        + mTestDirectory.getPath());
107         }
108     }
109
110     @Override
111     protected void tearDown() throws Exception {
112         if (mTestDirectory != null) {
113             for (File file : mTestDirectory.listFiles()) {
114                 file.delete();
115             }
116             mTestDirectory.delete();
117         }
118         super.tearDown();
119     }
120
121     public void testBasicRequest() throws Exception {
122         enqueueResponse(HTTP_OK, FILE_CONTENT);
123
124         Download download = enqueueRequest(getRequest());
125         assertEquals(DownloadManager.STATUS_PENDING,
126                      download.getLongField(DownloadManager.COLUMN_STATUS));
127         assertEquals(getServerUri(REQUEST_PATH),
128                      download.getStringField(DownloadManager.COLUMN_URI));
129         assertEquals(download.mId, download.getLongField(DownloadManager.COLUMN_ID));
130         assertEquals(mSystemFacade.currentTimeMillis(),
131                      download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP));
132
133         mSystemFacade.incrementTimeMillis(10);
134         RecordedRequest request = download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
135         assertEquals("GET", request.getMethod());
136         assertEquals(REQUEST_PATH, request.getPath());
137
138         Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI));
139         assertEquals("file", localUri.getScheme());
140         assertStartsWith("//" + Environment.getDownloadCacheDirectory().getPath(),
141                          localUri.getSchemeSpecificPart());
142         assertEquals("text/plain", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE));
143
144         int size = FILE_CONTENT.length();
145         assertEquals(size, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
146         assertEquals(size, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
147         assertEquals(mSystemFacade.currentTimeMillis(),
148                      download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP));
149
150         assertEquals(FILE_CONTENT, download.getContents());
151     }
152
153     public void testTitleAndDescription() throws Exception {
154         Download download = enqueueRequest(getRequest()
155                                            .setTitle("my title")
156                                            .setDescription("my description"));
157         assertEquals("my title", download.getStringField(DownloadManager.COLUMN_TITLE));
158         assertEquals("my description",
159                      download.getStringField(DownloadManager.COLUMN_DESCRIPTION));
160     }
161
162     public void testDownloadError() throws Exception {
163         enqueueEmptyResponse(HTTP_NOT_FOUND);
164         Download download = enqueueRequest(getRequest());
165         download.runUntilStatus(DownloadManager.STATUS_FAILED);
166         assertEquals(HTTP_NOT_FOUND, download.getLongField(DownloadManager.COLUMN_ERROR_CODE));
167     }
168
169     public void testUnhandledHttpStatus() throws Exception {
170         enqueueEmptyResponse(1234); // some invalid HTTP status
171         Download download = enqueueRequest(getRequest());
172         download.runUntilStatus(DownloadManager.STATUS_FAILED);
173         assertEquals(DownloadManager.ERROR_UNHANDLED_HTTP_CODE,
174                      download.getLongField(DownloadManager.COLUMN_ERROR_CODE));
175     }
176
177     public void testInterruptedDownload() throws Exception {
178         int initialLength = 5;
179         String etag = "my_etag";
180         int totalLength = FILE_CONTENT.length();
181         // the first response has normal headers but unexpectedly closes after initialLength bytes
182         enqueueResponse(HTTP_OK, FILE_CONTENT.substring(0, initialLength))
183                 .addHeader("Content-length", totalLength)
184                 .addHeader("Etag", etag)
185                 .setCloseConnectionAfter(true);
186         Download download = enqueueRequest(getRequest());
187
188         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
189         assertEquals(initialLength,
190                      download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
191
192         mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
193         // the second response returns partial content for the rest of the data
194         enqueueResponse(HTTP_PARTIAL_CONTENT, FILE_CONTENT.substring(initialLength))
195                 .addHeader("Content-range",
196                            "bytes " + initialLength + "-" + totalLength + "/" + totalLength)
197                 .addHeader("Etag", etag);
198         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
199         assertEquals(totalLength,
200                      download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
201     }
202
203     public void testFiltering() throws Exception {
204         enqueueEmptyResponse(HTTP_OK);
205         Download download1 = enqueueRequest(getRequest());
206         download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
207         enqueueEmptyResponse(HTTP_NOT_FOUND);
208
209         mSystemFacade.incrementTimeMillis(1); // ensure downloads are correctly ordered by time
210         Download download2 = enqueueRequest(getRequest());
211         download2.runUntilStatus(DownloadManager.STATUS_FAILED);
212
213         mSystemFacade.incrementTimeMillis(1);
214         Download download3 = enqueueRequest(getRequest());
215
216         Cursor cursor = mManager.query(new DownloadManager.Query());
217         checkAndCloseCursor(cursor, download3, download2, download1);
218
219         cursor = mManager.query(new DownloadManager.Query().setFilterById(download2.mId));
220         checkAndCloseCursor(cursor, download2);
221
222         cursor = mManager.query(new DownloadManager.Query()
223                                 .setFilterByStatus(DownloadManager.STATUS_PENDING));
224         checkAndCloseCursor(cursor, download3);
225
226         cursor = mManager.query(new DownloadManager.Query()
227                                 .setFilterByStatus(DownloadManager.STATUS_FAILED
228                                               | DownloadManager.STATUS_SUCCESSFUL));
229         checkAndCloseCursor(cursor, download2, download1);
230
231         cursor = mManager.query(new DownloadManager.Query()
232                                 .setFilterByStatus(DownloadManager.STATUS_RUNNING));
233         checkAndCloseCursor(cursor);
234     }
235
236     private void checkAndCloseCursor(Cursor cursor, Download... downloads) {
237         try {
238             int idIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID);
239             assertEquals(downloads.length, cursor.getCount());
240             cursor.moveToFirst();
241             for (Download download : downloads) {
242                 assertEquals(download.mId, cursor.getLong(idIndex));
243                 cursor.moveToNext();
244             }
245         } finally {
246             cursor.close();
247         }
248     }
249
250     public void testInvalidUri() throws Exception {
251         try {
252             enqueueRequest(getRequest("/no_host"));
253         } catch (IllegalArgumentException exc) { // expected
254             return;
255         }
256
257         fail("No exception thrown for invalid URI");
258     }
259
260     public void testDestination() throws Exception {
261         enqueueResponse(HTTP_OK, FILE_CONTENT);
262         Uri destination = Uri.fromFile(mTestDirectory).buildUpon().appendPath("testfile").build();
263         Download download = enqueueRequest(getRequest().setDestinationUri(destination));
264         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
265
266         Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI));
267         assertEquals(destination, localUri);
268
269         InputStream stream = new FileInputStream(destination.getSchemeSpecificPart());
270         try {
271             assertEquals(FILE_CONTENT, readStream(stream));
272         } finally {
273             stream.close();
274         }
275     }
276
277     public void testRequestHeaders() throws Exception {
278         enqueueEmptyResponse(HTTP_OK);
279         Download download = enqueueRequest(getRequest().setRequestHeader("Header1", "value1")
280                                            .setRequestHeader("Header2", "value2"));
281         RecordedRequest request = download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
282
283         assertTrue(request.getHeaders().contains("Header1: value1"));
284         assertTrue(request.getHeaders().contains("Header2: value2"));
285     }
286
287     public void testDelete() throws Exception {
288         Download download = enqueueRequest(getRequest().setRequestHeader("header", "value"));
289         mManager.remove(download.mId);
290         Cursor cursor = mManager.query(new DownloadManager.Query());
291         try {
292             assertEquals(0, cursor.getCount());
293         } finally {
294             cursor.close();
295         }
296     }
297
298     public void testSizeLimitOverMobile() throws Exception {
299         mSystemFacade.mMaxBytesOverMobile = FILE_CONTENT.length() - 1;
300
301         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE;
302         enqueueResponse(HTTP_OK, FILE_CONTENT);
303         Download download = enqueueRequest(getRequest());
304         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
305
306         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
307         // first response was read, but aborted after the DL manager processed the Content-Length
308         // header, so we need to enqueue a second one
309         enqueueResponse(HTTP_OK, FILE_CONTENT);
310         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
311     }
312     
313     /**
314      * Test for race conditions when the service is flooded with startService() calls while running
315      * a download.
316      */
317     public void testFloodServiceWithStarts() throws Exception {
318         enqueueResponse(HTTP_OK, FILE_CONTENT);
319         Download download = enqueueRequest(getRequest());
320         while (download.getStatus() != DownloadManager.STATUS_SUCCESSFUL) {
321             startService(null);
322             Thread.sleep(10);
323         }
324     }
325
326     private DownloadManager.Request getRequest() throws MalformedURLException {
327         return getRequest(getServerUri(REQUEST_PATH));
328     }
329
330     private DownloadManager.Request getRequest(String path) {
331         return new DownloadManager.Request(Uri.parse(path));
332     }
333
334     private Download enqueueRequest(DownloadManager.Request request) {
335         return new Download(mManager.enqueue(request));
336     }
337 }