b6fd611ef916ad925876ed060461da0b8d8e8647
[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 static android.app.DownloadManager.STATUS_FAILED;
20 import static android.app.DownloadManager.STATUS_PAUSED;
21 import static android.net.TrafficStats.GB_IN_BYTES;
22 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
23 import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
24 import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
25 import static java.net.HttpURLConnection.HTTP_OK;
26 import static java.net.HttpURLConnection.HTTP_PARTIAL;
27 import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
28 import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
29 import static org.mockito.Matchers.anyInt;
30 import static org.mockito.Matchers.anyString;
31 import static org.mockito.Matchers.isA;
32 import static org.mockito.Mockito.atLeastOnce;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36
37 import android.app.DownloadManager;
38 import android.app.Notification;
39 import android.app.NotificationManager;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.database.Cursor;
43 import android.net.ConnectivityManager;
44 import android.net.Uri;
45 import android.os.Environment;
46 import android.os.SystemClock;
47 import android.provider.Downloads;
48 import android.test.suitebuilder.annotation.LargeTest;
49 import android.test.suitebuilder.annotation.Suppress;
50 import android.text.format.DateUtils;
51
52 import com.google.mockwebserver.MockResponse;
53 import com.google.mockwebserver.MockStreamResponse;
54 import com.google.mockwebserver.RecordedRequest;
55 import com.google.mockwebserver.SocketPolicy;
56
57 import java.io.File;
58 import java.io.FileInputStream;
59 import java.io.FileNotFoundException;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.util.List;
63 import java.util.concurrent.TimeoutException;
64
65 @LargeTest
66 public class PublicApiFunctionalTest extends AbstractPublicApiTest {
67     private static final String REDIRECTED_PATH = "/other_path";
68     private static final String ETAG = "my_etag";
69
70     protected File mTestDirectory;
71     private NotificationManager mNotifManager;
72
73     public PublicApiFunctionalTest() {
74         super(new FakeSystemFacade());
75     }
76
77     @Override
78     protected void setUp() throws Exception {
79         super.setUp();
80
81         mNotifManager = (NotificationManager) getContext()
82                 .getSystemService(Context.NOTIFICATION_SERVICE);
83
84         mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
85                                   + "download_manager_functional_test");
86         if (mTestDirectory.exists()) {
87             for (File file : mTestDirectory.listFiles()) {
88                 file.delete();
89             }
90         } else {
91             mTestDirectory.mkdir();
92         }
93     }
94
95     @Override
96     protected void tearDown() throws Exception {
97         if (mTestDirectory != null && mTestDirectory.exists()) {
98             for (File file : mTestDirectory.listFiles()) {
99                 file.delete();
100             }
101             mTestDirectory.delete();
102         }
103         super.tearDown();
104     }
105
106     public void testBasicRequest() throws Exception {
107         enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
108
109         Download download = enqueueRequest(getRequest());
110         assertEquals(DownloadManager.STATUS_PENDING,
111                      download.getLongField(DownloadManager.COLUMN_STATUS));
112         assertEquals(getServerUri(REQUEST_PATH),
113                      download.getStringField(DownloadManager.COLUMN_URI));
114         assertEquals(download.mId, download.getLongField(DownloadManager.COLUMN_ID));
115         assertEquals(mSystemFacade.currentTimeMillis(),
116                      download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP));
117
118         mSystemFacade.incrementTimeMillis(10);
119         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
120         RecordedRequest request = takeRequest();
121         assertEquals("GET", request.getMethod());
122         assertEquals(REQUEST_PATH, request.getPath());
123
124         Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI));
125         assertEquals("content", localUri.getScheme());
126         checkUriContent(localUri);
127         assertEquals("text/plain", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE));
128
129         int size = FILE_CONTENT.length();
130         assertEquals(size, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
131         assertEquals(size, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
132         assertEquals(mSystemFacade.currentTimeMillis(),
133                      download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP));
134
135         checkCompleteDownload(download);
136     }
137
138     @Suppress
139     public void testExtremelyLarge() throws Exception {
140         // NOTE: suppressed since this takes several minutes to run
141         final long length = 3 * GB_IN_BYTES;
142         final InputStream body = new FakeInputStream(length);
143
144         enqueueResponse(new MockStreamResponse().setResponseCode(HTTP_OK).setBody(body, length)
145                 .setHeader("Content-type", "text/plain")
146                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
147
148         final Download download = enqueueRequest(getRequest()
149                 .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "extreme.bin"));
150         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL, 10 * DateUtils.MINUTE_IN_MILLIS);
151
152         assertEquals(length, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
153         assertEquals(length, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
154     }
155
156     private void checkUriContent(Uri uri) throws FileNotFoundException, IOException {
157         InputStream inputStream = mResolver.openInputStream(uri);
158         try {
159             assertEquals(FILE_CONTENT, readStream(inputStream));
160         } finally {
161             inputStream.close();
162         }
163     }
164
165     public void testTitleAndDescription() throws Exception {
166         Download download = enqueueRequest(getRequest()
167                                            .setTitle("my title")
168                                            .setDescription("my description"));
169         assertEquals("my title", download.getStringField(DownloadManager.COLUMN_TITLE));
170         assertEquals("my description",
171                      download.getStringField(DownloadManager.COLUMN_DESCRIPTION));
172     }
173
174     public void testDownloadError() throws Exception {
175         enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND));
176         runSimpleFailureTest(HTTP_NOT_FOUND);
177     }
178
179     public void testUnhandledHttpStatus() throws Exception {
180         enqueueResponse(buildEmptyResponse(1234)); // some invalid HTTP status
181         runSimpleFailureTest(DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
182     }
183
184     public void testInterruptedDownload() throws Exception {
185         int initialLength = 5;
186         enqueueInterruptedDownloadResponses(initialLength);
187
188         Download download = enqueueRequest(getRequest());
189         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
190         assertEquals(initialLength,
191                      download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
192         assertEquals(FILE_CONTENT.length(),
193                      download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
194         takeRequest(); // get the first request out of the queue
195
196         mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
197         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
198         checkCompleteDownload(download);
199
200         List<String> headers = takeRequest().getHeaders();
201         assertTrue("No Range header: " + headers,
202                    headers.contains("Range: bytes=" + initialLength + "-"));
203         assertTrue("No ETag header: " + headers, headers.contains("If-Match: " + ETAG));
204     }
205
206     public void testInterruptedExternalDownload() throws Exception {
207         enqueueInterruptedDownloadResponses(5);
208         Download download = enqueueRequest(getRequest().setDestinationUri(getExternalUri()));
209         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
210         mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
211         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
212         checkCompleteDownload(download);
213     }
214
215     private void enqueueInterruptedDownloadResponses(int initialLength) {
216         // the first response has normal headers but unexpectedly closes after initialLength bytes
217         enqueueResponse(buildPartialResponse(0, initialLength));
218         // the second response returns partial content for the rest of the data
219         enqueueResponse(buildPartialResponse(initialLength, FILE_CONTENT.length()));
220     }
221
222     private MockResponse buildPartialResponse(int start, int end) {
223         int totalLength = FILE_CONTENT.length();
224         boolean isFirstResponse = (start == 0);
225         int status = isFirstResponse ? HTTP_OK : HTTP_PARTIAL;
226         MockResponse response = buildResponse(status, FILE_CONTENT.substring(start, end))
227                 .setHeader("Content-length", totalLength)
228                 .setHeader("Etag", ETAG);
229         if (!isFirstResponse) {
230             response.setHeader(
231                     "Content-range", "bytes " + start + "-" + totalLength + "/" + totalLength);
232         }
233         return response;
234     }
235
236     // enqueue a huge response to keep the receiveing thread in DownloadThread.java busy for a while
237     // give enough time to do something (cancel/remove etc) on that downloadrequest
238     // while it is in progress
239     private MockResponse buildContinuingResponse() {
240         int numPackets = 100;
241         int contentLength = STRING_1K.length() * numPackets;
242         return buildResponse(HTTP_OK, STRING_1K)
243                .setHeader("Content-length", contentLength)
244                .setHeader("Etag", ETAG)
245                .setBytesPerSecond(1024);
246     }
247
248     public void testFiltering() throws Exception {
249         enqueueResponse(buildEmptyResponse(HTTP_OK));
250         enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND));
251
252         Download download1 = enqueueRequest(getRequest());
253         download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
254
255         mSystemFacade.incrementTimeMillis(1); // ensure downloads are correctly ordered by time
256         Download download2 = enqueueRequest(getRequest());
257         download2.runUntilStatus(DownloadManager.STATUS_FAILED);
258
259         mSystemFacade.incrementTimeMillis(1);
260         Download download3 = enqueueRequest(getRequest());
261
262         Cursor cursor = mManager.query(new DownloadManager.Query());
263         checkAndCloseCursor(cursor, download3, download2, download1);
264
265         cursor = mManager.query(new DownloadManager.Query().setFilterById(download2.mId));
266         checkAndCloseCursor(cursor, download2);
267
268         cursor = mManager.query(new DownloadManager.Query()
269                                 .setFilterByStatus(DownloadManager.STATUS_PENDING));
270         checkAndCloseCursor(cursor, download3);
271
272         cursor = mManager.query(new DownloadManager.Query()
273                                 .setFilterByStatus(DownloadManager.STATUS_FAILED
274                                               | DownloadManager.STATUS_SUCCESSFUL));
275         checkAndCloseCursor(cursor, download2, download1);
276
277         cursor = mManager.query(new DownloadManager.Query()
278                                 .setFilterByStatus(DownloadManager.STATUS_RUNNING));
279         checkAndCloseCursor(cursor);
280
281         mSystemFacade.incrementTimeMillis(1);
282         Download invisibleDownload = enqueueRequest(getRequest().setVisibleInDownloadsUi(false));
283         cursor = mManager.query(new DownloadManager.Query());
284         checkAndCloseCursor(cursor, invisibleDownload, download3, download2, download1);
285         cursor = mManager.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true));
286         checkAndCloseCursor(cursor, download3, download2, download1);
287     }
288
289     public void testOrdering() throws Exception {
290         enqueueResponse(buildResponse(HTTP_OK, "small contents"));
291         enqueueResponse(buildResponse(HTTP_OK, "large contents large contents"));
292         enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND));
293
294         Download download1 = enqueueRequest(getRequest());
295         download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
296
297         mSystemFacade.incrementTimeMillis(1);
298         Download download2 = enqueueRequest(getRequest());
299         download2.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
300
301         mSystemFacade.incrementTimeMillis(1);
302         Download download3 = enqueueRequest(getRequest());
303         download3.runUntilStatus(DownloadManager.STATUS_FAILED);
304
305         // default ordering -- by timestamp descending
306         Cursor cursor = mManager.query(new DownloadManager.Query());
307         checkAndCloseCursor(cursor, download3, download2, download1);
308
309         cursor = mManager.query(new DownloadManager.Query()
310                 .orderBy(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP,
311                         DownloadManager.Query.ORDER_ASCENDING));
312         checkAndCloseCursor(cursor, download1, download2, download3);
313
314         cursor = mManager.query(new DownloadManager.Query()
315                 .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES,
316                         DownloadManager.Query.ORDER_DESCENDING));
317         checkAndCloseCursor(cursor, download2, download1, download3);
318
319         cursor = mManager.query(new DownloadManager.Query()
320                 .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES,
321                         DownloadManager.Query.ORDER_ASCENDING));
322         checkAndCloseCursor(cursor, download3, download1, download2);
323     }
324
325     private void checkAndCloseCursor(Cursor cursor, Download... downloads) {
326         try {
327             int idIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID);
328             assertEquals(downloads.length, cursor.getCount());
329             cursor.moveToFirst();
330             for (Download download : downloads) {
331                 assertEquals(download.mId, cursor.getLong(idIndex));
332                 cursor.moveToNext();
333             }
334         } finally {
335             cursor.close();
336         }
337     }
338
339     public void testInvalidUri() throws Exception {
340         try {
341             enqueueRequest(getRequest("/no_host"));
342         } catch (IllegalArgumentException exc) { // expected
343             return;
344         }
345
346         fail("No exception thrown for invalid URI");
347     }
348
349     public void testDestination() throws Exception {
350         enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
351         Uri destination = getExternalUri();
352         Download download = enqueueRequest(getRequest().setDestinationUri(destination));
353         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
354
355         Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI));
356         assertEquals(destination, localUri);
357
358         InputStream stream = new FileInputStream(destination.getPath());
359         try {
360             assertEquals(FILE_CONTENT, readStream(stream));
361         } finally {
362             stream.close();
363         }
364     }
365
366     private Uri getExternalUri() {
367         return Uri.fromFile(mTestDirectory).buildUpon().appendPath("testfile.txt").build();
368     }
369
370     public void testRequestHeaders() throws Exception {
371         enqueueResponse(buildEmptyResponse(HTTP_OK));
372         Download download = enqueueRequest(getRequest().addRequestHeader("Header1", "value1")
373                                            .addRequestHeader("Header2", "value2"));
374         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
375
376         List<String> headers = takeRequest().getHeaders();
377         assertTrue(headers.contains("Header1: value1"));
378         assertTrue(headers.contains("Header2: value2"));
379     }
380
381     public void testDelete() throws Exception {
382         Download download = enqueueRequest(getRequest().addRequestHeader("header", "value"));
383         mManager.remove(download.mId);
384         Cursor cursor = mManager.query(new DownloadManager.Query());
385         try {
386             assertEquals(0, cursor.getCount());
387         } finally {
388             cursor.close();
389         }
390     }
391
392     public void testSizeLimitOverMobile() throws Exception {
393         enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
394         enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
395
396         mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1;
397         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE;
398         Download download = enqueueRequest(getRequest());
399         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
400
401         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
402         // first response was read, but aborted after the DL manager processed the Content-Length
403         // header, so we need to enqueue a second one
404         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
405     }
406
407     public void testRedirect301() throws Exception {
408         RecordedRequest lastRequest = runRedirectionTest(301);
409         // for 301, upon retry/resume, we reuse the redirected URI
410         assertEquals(REDIRECTED_PATH, lastRequest.getPath());
411     }
412
413     public void testRedirect302() throws Exception {
414         RecordedRequest lastRequest = runRedirectionTest(302);
415         // for 302, upon retry/resume, we use the original URI
416         assertEquals(REQUEST_PATH, lastRequest.getPath());
417     }
418
419     public void testRunawayRedirect() throws Exception {
420         for (int i = 0; i < 16; i++) {
421             enqueueResponse(buildEmptyResponse(HTTP_MOVED_TEMP)
422                     .setHeader("Location", mServer.getUrl("/" + i).toString()));
423         }
424
425         final Download download = enqueueRequest(getRequest());
426
427         // Ensure that we arrive at failed download, instead of spinning forever
428         download.runUntilStatus(DownloadManager.STATUS_FAILED);
429         assertEquals(DownloadManager.ERROR_TOO_MANY_REDIRECTS, download.getReason());
430     }
431
432     public void testRunawayUnavailable() throws Exception {
433         final int RETRY_DELAY = 120;
434         for (int i = 0; i < 16; i++) {
435             enqueueResponse(
436                     buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", RETRY_DELAY));
437         }
438
439         final Download download = enqueueRequest(getRequest());
440         for (int i = 0; i < Constants.MAX_RETRIES - 1; i++) {
441             download.runUntilStatus(DownloadManager.STATUS_PAUSED);
442             mSystemFacade.incrementTimeMillis((RETRY_DELAY + 60) * SECOND_IN_MILLIS);
443         }
444
445         // Ensure that we arrive at failed download, instead of spinning forever
446         download.runUntilStatus(DownloadManager.STATUS_FAILED);
447     }
448
449     public void testNoEtag() throws Exception {
450         enqueueResponse(buildPartialResponse(0, 5).removeHeader("Etag"));
451         runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME);
452     }
453
454     public void testEtagChanged() throws Exception {
455         final String A = "kittenz";
456         final String B = "puppiez";
457
458         // 1. Try downloading A, but partial result
459         enqueueResponse(buildResponse(HTTP_OK, A.substring(0, 2))
460                 .setHeader("Content-length", A.length())
461                 .setHeader("Etag", A));
462
463         // 2. Try resuming A, but fail ETag check
464         enqueueResponse(buildEmptyResponse(HTTP_PRECON_FAILED));
465
466         final Download download = enqueueRequest(getRequest());
467         RecordedRequest req;
468
469         // 1. Try downloading A, but partial result
470         download.runUntilStatus(STATUS_PAUSED);
471         assertEquals(DownloadManager.PAUSED_WAITING_TO_RETRY, download.getReason());
472         req = takeRequest();
473         assertNull(getHeaderValue(req, "Range"));
474         assertNull(getHeaderValue(req, "If-Match"));
475
476         // 2. Try resuming A, but fail ETag check
477         mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
478         download.runUntilStatus(STATUS_FAILED);
479         assertEquals(HTTP_PRECON_FAILED, download.getReason());
480         req = takeRequest();
481         assertEquals("bytes=2-", getHeaderValue(req, "Range"));
482         assertEquals(A, getHeaderValue(req, "If-Match"));
483     }
484
485     public void testSanitizeMediaType() throws Exception {
486         enqueueResponse(buildEmptyResponse(HTTP_OK)
487                 .setHeader("Content-Type", "text/html; charset=ISO-8859-4"));
488         Download download = enqueueRequest(getRequest());
489         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
490         assertEquals("text/html", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE));
491     }
492
493     public void testNoContentLength() throws Exception {
494         enqueueResponse(buildEmptyResponse(HTTP_OK).removeHeader("Content-length"));
495         runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME);
496     }
497
498     public void testInsufficientSpace() throws Exception {
499         // this would be better done by stubbing the system API to check available space, but in the
500         // meantime, just use an absurdly large header value
501         enqueueResponse(buildEmptyResponse(HTTP_OK)
502                 .setHeader("Content-Length", 1024L * 1024 * 1024 * 1024 * 1024));
503         runSimpleFailureTest(DownloadManager.ERROR_INSUFFICIENT_SPACE);
504     }
505
506     public void testCancel() throws Exception {
507         // return 'real time' from FakeSystemFacade so that DownloadThread will report progress
508         mSystemFacade.setReturnActualTime(true);
509         enqueueResponse(buildContinuingResponse());
510         Download download = enqueueRequest(getRequest());
511         // give the download time to get started and progress to 1% completion
512         // before cancelling it.
513         boolean rslt = download.runUntilProgress(1);
514         assertTrue(rslt);
515         mManager.remove(download.mId);
516
517         // Verify that row is removed from database
518         final long timeout = SystemClock.elapsedRealtime() + (15 * SECOND_IN_MILLIS);
519         while (download.getStatusIfExists() != -1) {
520             if (SystemClock.elapsedRealtime() > timeout) {
521                 throw new TimeoutException("Row wasn't removed");
522             }
523             SystemClock.sleep(100);
524         }
525     }
526
527     public void testDownloadCompleteBroadcast() throws Exception {
528         enqueueResponse(buildEmptyResponse(HTTP_OK));
529         Download download = enqueueRequest(getRequest());
530         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
531
532         assertEquals(1, mSystemFacade.mBroadcastsSent.size());
533         Intent broadcast = mSystemFacade.mBroadcastsSent.get(0);
534         assertEquals(DownloadManager.ACTION_DOWNLOAD_COMPLETE, broadcast.getAction());
535         assertEquals(PACKAGE_NAME, broadcast.getPackage());
536         long intentId = broadcast.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
537         assertEquals(download.mId, intentId);
538     }
539
540     public void testNotificationClickedBroadcast() throws Exception {
541         Download download = enqueueRequest(getRequest());
542
543         DownloadReceiver receiver = new DownloadReceiver();
544         receiver.mSystemFacade = mSystemFacade;
545         Intent intent = new Intent(Constants.ACTION_LIST);
546         intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId));
547         intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
548                 new long[] { download.mId });
549         receiver.onReceive(mContext, intent);
550
551         assertEquals(1, mSystemFacade.mBroadcastsSent.size());
552         Intent broadcast = mSystemFacade.mBroadcastsSent.get(0);
553         assertEquals(DownloadManager.ACTION_NOTIFICATION_CLICKED, broadcast.getAction());
554         assertEquals(PACKAGE_NAME, broadcast.getPackage());
555     }
556
557     public void testBasicConnectivityChanges() throws Exception {
558         enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
559
560         // without connectivity, download immediately pauses
561         mSystemFacade.mActiveNetworkType = null;
562         Download download = enqueueRequest(getRequest());
563         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
564
565         // connecting should start the download
566         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
567         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
568     }
569
570     public void testAllowedNetworkTypes() throws Exception {
571         enqueueResponse(buildEmptyResponse(HTTP_OK));
572         enqueueResponse(buildEmptyResponse(HTTP_OK));
573
574         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE;
575
576         // by default, use any connection
577         Download download = enqueueRequest(getRequest());
578         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
579
580         // restrict a download to wifi...
581         download = enqueueRequest(getRequest()
582                                   .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI));
583         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
584         // ...then enable wifi
585         mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
586         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
587     }
588
589     public void testRoaming() throws Exception {
590         enqueueResponse(buildEmptyResponse(HTTP_OK));
591         enqueueResponse(buildEmptyResponse(HTTP_OK));
592
593         mSystemFacade.mIsRoaming = true;
594
595         // by default, allow roaming
596         Download download = enqueueRequest(getRequest());
597         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
598
599         // disallow roaming for a download...
600         download = enqueueRequest(getRequest().setAllowedOverRoaming(false));
601         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
602         // ...then turn off roaming
603         mSystemFacade.mIsRoaming = false;
604         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
605     }
606
607     public void testContentObserver() throws Exception {
608         enqueueResponse(buildEmptyResponse(HTTP_OK));
609         mResolver.resetNotified();
610         final Download download = enqueueRequest(getRequest());
611         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
612         assertTrue(mResolver.mNotifyWasCalled);
613     }
614
615     public void testNotificationNever() throws Exception {
616         enqueueResponse(buildEmptyResponse(HTTP_OK));
617
618         final Download download = enqueueRequest(
619                 getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN));
620         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
621
622         verify(mNotifManager, times(1)).cancelAll();
623         verify(mNotifManager, never()).notify(anyString(), anyInt(), isA(Notification.class));
624     }
625
626     public void testNotificationVisible() throws Exception {
627         enqueueResponse(buildEmptyResponse(HTTP_OK));
628
629         // only shows in-progress notifications
630         final Download download = enqueueRequest(getRequest());
631         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
632
633         // TODO: verify different notif types with tags
634         verify(mNotifManager, times(1)).cancelAll();
635         verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class));
636     }
637
638     public void testNotificationVisibleComplete() throws Exception {
639         enqueueResponse(buildEmptyResponse(HTTP_OK));
640
641         final Download download = enqueueRequest(getRequest().setNotificationVisibility(
642                 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED));
643         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
644
645         // TODO: verify different notif types with tags
646         verify(mNotifManager, times(1)).cancelAll();
647         verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class));
648     }
649
650     public void testRetryAfter() throws Exception {
651         final int delay = 120;
652         enqueueResponse(
653                 buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", delay));
654         enqueueResponse(buildEmptyResponse(HTTP_OK));
655
656         Download download = enqueueRequest(getRequest());
657         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
658
659         // download manager adds random 0-30s offset
660         mSystemFacade.incrementTimeMillis((delay + 31) * 1000);
661         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
662     }
663
664     public void testManyInterruptions() throws Exception {
665         final int length = FILE_CONTENT.length();
666         for (int i = 0; i < length; i++) {
667             enqueueResponse(buildPartialResponse(i, i + 1));
668         }
669
670         Download download = enqueueRequest(getRequest());
671         for (int i = 0; i < length - 1; i++) {
672             download.runUntilStatus(DownloadManager.STATUS_PAUSED);
673             mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
674         }
675
676         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
677         checkCompleteDownload(download);
678     }
679
680     public void testExistingFile() throws Exception {
681         enqueueResponse(buildEmptyResponse(HTTP_OK));
682
683         // download a file which already exists.
684         // downloadservice should simply create filename with "-" and a number attached
685         // at the end; i.e., download shouldnot fail.
686         Uri destination = getExternalUri();
687         new File(destination.getPath()).createNewFile();
688
689         Download download = enqueueRequest(getRequest().setDestinationUri(destination));
690         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
691     }
692
693     public void testEmptyFields() throws Exception {
694         Download download = enqueueRequest(getRequest());
695         assertEquals("", download.getStringField(DownloadManager.COLUMN_TITLE));
696         assertEquals("", download.getStringField(DownloadManager.COLUMN_DESCRIPTION));
697         assertNull(download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE));
698         assertEquals(0, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
699         assertEquals(-1, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
700         // just ensure no exception is thrown
701         download.getLongField(DownloadManager.COLUMN_REASON);
702     }
703
704     public void testRestart() throws Exception {
705         enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND));
706         enqueueResponse(buildEmptyResponse(HTTP_OK));
707
708         Download download = enqueueRequest(getRequest());
709         download.runUntilStatus(DownloadManager.STATUS_FAILED);
710
711         mManager.restartDownload(download.mId);
712         assertEquals(DownloadManager.STATUS_PENDING,
713                 download.getLongField(DownloadManager.COLUMN_STATUS));
714         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
715     }
716
717     private void checkCompleteDownload(Download download) throws Exception {
718         assertEquals(FILE_CONTENT.length(),
719                      download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
720         assertEquals(FILE_CONTENT, download.getContents());
721     }
722
723     private void runSimpleFailureTest(int expectedErrorCode) throws Exception {
724         Download download = enqueueRequest(getRequest());
725         download.runUntilStatus(DownloadManager.STATUS_FAILED);
726         assertEquals(expectedErrorCode,
727                      download.getLongField(DownloadManager.COLUMN_REASON));
728     }
729
730     /**
731      * Run a redirection test consisting of
732      * 1) Request to REQUEST_PATH with 3xx response redirecting to another URI
733      * 2) Request to REDIRECTED_PATH with interrupted partial response
734      * 3) Resume request to complete download
735      * @return the last request sent to the server, resuming after the interruption
736      */
737     private RecordedRequest runRedirectionTest(int status) throws Exception {
738         enqueueResponse(buildEmptyResponse(status)
739                 .setHeader("Location", mServer.getUrl(REDIRECTED_PATH).toString()));
740         enqueueInterruptedDownloadResponses(5);
741
742         final Download download = enqueueRequest(getRequest());
743         download.runUntilStatus(DownloadManager.STATUS_PAUSED);
744         mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS);
745         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
746
747         assertEquals(REQUEST_PATH, takeRequest().getPath());
748         assertEquals(REDIRECTED_PATH, takeRequest().getPath());
749
750         return takeRequest();
751     }
752
753     /**
754      * Return value of requested HTTP header, if it exists.
755      */
756     private static String getHeaderValue(RecordedRequest req, String header) {
757         header = header.toLowerCase() + ":";
758         for (String h : req.getHeaders()) {
759             if (h.toLowerCase().startsWith(header)) {
760                 return h.substring(header.length()).trim();
761             }
762         }
763         return null;
764     }
765 }