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