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