| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.providers.downloads; |
| |
| import static android.app.DownloadManager.STATUS_FAILED; |
| import static android.app.DownloadManager.STATUS_PAUSED; |
| import static android.net.TrafficStats.GB_IN_BYTES; |
| import static android.text.format.DateUtils.SECOND_IN_MILLIS; |
| |
| import static org.mockito.Matchers.anyInt; |
| import static org.mockito.Matchers.anyString; |
| import static org.mockito.Matchers.isA; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| |
| import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; |
| import static java.net.HttpURLConnection.HTTP_NOT_FOUND; |
| import static java.net.HttpURLConnection.HTTP_OK; |
| import static java.net.HttpURLConnection.HTTP_PARTIAL; |
| import static java.net.HttpURLConnection.HTTP_PRECON_FAILED; |
| import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; |
| |
| import android.app.BroadcastOptions; |
| import android.app.DownloadManager; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.net.ConnectivityManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.os.SystemClock; |
| import android.provider.Downloads; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.test.suitebuilder.annotation.Suppress; |
| import android.text.format.DateUtils; |
| |
| import libcore.io.IoUtils; |
| |
| import com.google.mockwebserver.MockResponse; |
| import com.google.mockwebserver.RecordedRequest; |
| import com.google.mockwebserver.SocketPolicy; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| @LargeTest |
| public class PublicApiFunctionalTest extends AbstractPublicApiTest { |
| private static final String REDIRECTED_PATH = "/other_path"; |
| private static final String ETAG = "my_etag"; |
| |
| protected File mTestDirectory; |
| private NotificationManager mNotifManager; |
| private DownloadManager mDownloadManager; |
| |
| public PublicApiFunctionalTest() { |
| super(new FakeSystemFacade()); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| mNotifManager = getContext().getSystemService(NotificationManager.class); |
| mDownloadManager = getContext().getSystemService(DownloadManager.class); |
| |
| mTestDirectory = new File(Environment.getExternalStoragePublicDirectory( |
| Environment.DIRECTORY_DOWNLOADS) |
| + File.separator + "download_manager_functional_test"); |
| if (mTestDirectory.exists()) { |
| IoUtils.deleteContents(mTestDirectory); |
| } else { |
| mTestDirectory.mkdir(); |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (mTestDirectory != null && mTestDirectory.exists()) { |
| IoUtils.deleteContents(mTestDirectory); |
| mTestDirectory.delete(); |
| } |
| super.tearDown(); |
| } |
| |
| public void testBasicRequest() throws Exception { |
| enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |
| |
| Download download = enqueueRequest(getRequest()); |
| assertEquals(DownloadManager.STATUS_PENDING, |
| download.getLongField(DownloadManager.COLUMN_STATUS)); |
| assertEquals(getServerUri(REQUEST_PATH), |
| download.getStringField(DownloadManager.COLUMN_URI)); |
| assertEquals(download.mId, download.getLongField(DownloadManager.COLUMN_ID)); |
| assertEquals(mSystemFacade.currentTimeMillis(), |
| download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); |
| |
| mSystemFacade.incrementTimeMillis(10); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| RecordedRequest request = takeRequest(); |
| assertEquals("GET", request.getMethod()); |
| assertEquals(REQUEST_PATH, request.getPath()); |
| |
| Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI)); |
| assertEquals("content", localUri.getScheme()); |
| checkUriContent(localUri); |
| assertEquals("text/plain", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); |
| |
| int size = FILE_CONTENT.length(); |
| assertEquals(size, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); |
| assertEquals(size, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); |
| assertEquals(mSystemFacade.currentTimeMillis(), |
| download.getLongField(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); |
| |
| checkCompleteDownload(download); |
| } |
| |
| @Suppress |
| public void testExtremelyLarge() throws Exception { |
| // NOTE: suppressed since this takes several minutes to run |
| final long length = 3 * GB_IN_BYTES; |
| final InputStream body = new FakeInputStream(length); |
| |
| enqueueResponse(new MockResponse().setResponseCode(HTTP_OK).setBody(body, length) |
| .setHeader("Content-type", "text/plain") |
| .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); |
| |
| final Download download = enqueueRequest(getRequest() |
| .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "extreme.bin")); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL, 10 * DateUtils.MINUTE_IN_MILLIS); |
| |
| assertEquals(length, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); |
| assertEquals(length, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); |
| } |
| |
| private void checkUriContent(Uri uri) throws FileNotFoundException, IOException { |
| InputStream inputStream = mResolver.openInputStream(uri); |
| try { |
| assertEquals(FILE_CONTENT, readStream(inputStream)); |
| } finally { |
| inputStream.close(); |
| } |
| } |
| |
| public void testTitleAndDescription() throws Exception { |
| Download download = enqueueRequest(getRequest() |
| .setTitle("my title") |
| .setDescription("my description")); |
| assertEquals("my title", download.getStringField(DownloadManager.COLUMN_TITLE)); |
| assertEquals("my description", |
| download.getStringField(DownloadManager.COLUMN_DESCRIPTION)); |
| } |
| |
| public void testDownloadError() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); |
| runSimpleFailureTest(HTTP_NOT_FOUND); |
| } |
| |
| public void testUnhandledHttpStatus() throws Exception { |
| enqueueResponse(buildEmptyResponse(1234)); // some invalid HTTP status |
| runSimpleFailureTest(DownloadManager.ERROR_UNHANDLED_HTTP_CODE); |
| } |
| |
| public void testInterruptedDownload() throws Exception { |
| int initialLength = 5; |
| enqueueInterruptedDownloadResponses(initialLength); |
| |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| assertEquals(initialLength, |
| download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); |
| assertEquals(FILE_CONTENT.length(), |
| download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); |
| takeRequest(); // get the first request out of the queue |
| |
| mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| checkCompleteDownload(download); |
| |
| List<String> headers = takeRequest().getHeaders(); |
| assertTrue("No Range header: " + headers, |
| headers.contains("Range: bytes=" + initialLength + "-")); |
| assertTrue("No ETag header: " + headers, headers.contains("If-Match: " + ETAG)); |
| } |
| |
| public void testInterruptedExternalDownload() throws Exception { |
| enqueueInterruptedDownloadResponses(5); |
| Download download = enqueueRequest(getRequest().setDestinationUri(getExternalUri())); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| checkCompleteDownload(download); |
| } |
| |
| private void enqueueInterruptedDownloadResponses(int initialLength) { |
| // the first response has normal headers but unexpectedly closes after initialLength bytes |
| enqueueResponse(buildPartialResponse(0, initialLength)); |
| // the second response returns partial content for the rest of the data |
| enqueueResponse(buildPartialResponse(initialLength, FILE_CONTENT.length())); |
| } |
| |
| private MockResponse buildPartialResponse(int start, int end) { |
| int totalLength = FILE_CONTENT.length(); |
| boolean isFirstResponse = (start == 0); |
| int status = isFirstResponse ? HTTP_OK : HTTP_PARTIAL; |
| MockResponse response = buildResponse(status, FILE_CONTENT.substring(start, end)) |
| .setHeader("Content-length", isFirstResponse ? totalLength : (end - start)) |
| .setHeader("Etag", ETAG); |
| if (!isFirstResponse) { |
| response.setHeader( |
| "Content-range", "bytes " + start + "-" + totalLength + "/" + totalLength); |
| } |
| return response; |
| } |
| |
| // enqueue a huge response to keep the receiveing thread in DownloadThread.java busy for a while |
| // give enough time to do something (cancel/remove etc) on that downloadrequest |
| // while it is in progress |
| private MockResponse buildContinuingResponse() { |
| int numPackets = 100; |
| int contentLength = STRING_1K.length() * numPackets; |
| return buildResponse(HTTP_OK, STRING_1K) |
| .setHeader("Content-length", contentLength) |
| .setHeader("Etag", ETAG) |
| .throttleBody(1024, 1, TimeUnit.SECONDS); |
| } |
| |
| public void testFiltering() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); |
| |
| Download download1 = enqueueRequest(getRequestWithDestinationDownloadsDir()); |
| download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| mSystemFacade.incrementTimeMillis(1); // ensure downloads are correctly ordered by time |
| Download download2 = enqueueRequest(getRequestWithDestinationDownloadsDir()); |
| download2.runUntilStatus(DownloadManager.STATUS_FAILED); |
| |
| mSystemFacade.incrementTimeMillis(1); |
| Download download3 = enqueueRequest(getRequestWithDestinationDownloadsDir()); |
| |
| Cursor cursor = mManager.query(new DownloadManager.Query()); |
| checkAndCloseCursor(cursor, download3, download2, download1); |
| |
| cursor = mManager.query(new DownloadManager.Query().setFilterById(download2.mId)); |
| checkAndCloseCursor(cursor, download2); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .setFilterByStatus(DownloadManager.STATUS_PENDING)); |
| checkAndCloseCursor(cursor, download3); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .setFilterByStatus(DownloadManager.STATUS_FAILED |
| | DownloadManager.STATUS_SUCCESSFUL)); |
| checkAndCloseCursor(cursor, download2, download1); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .setFilterByStatus(DownloadManager.STATUS_RUNNING)); |
| checkAndCloseCursor(cursor); |
| |
| mSystemFacade.incrementTimeMillis(1); |
| Download invisibleDownload = enqueueRequest(getRequest().setVisibleInDownloadsUi(false)); |
| cursor = mManager.query(new DownloadManager.Query()); |
| checkAndCloseCursor(cursor, invisibleDownload, download3, download2, download1); |
| cursor = mManager.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true)); |
| checkAndCloseCursor(cursor, download3, download2, download1); |
| } |
| |
| public void testOrdering() throws Exception { |
| enqueueResponse(buildResponse(HTTP_OK, "small contents")); |
| enqueueResponse(buildResponse(HTTP_OK, "large contents large contents")); |
| enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); |
| |
| Download download1 = enqueueRequest(getRequest()); |
| download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| mSystemFacade.incrementTimeMillis(1); |
| Download download2 = enqueueRequest(getRequest()); |
| download2.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| mSystemFacade.incrementTimeMillis(1); |
| Download download3 = enqueueRequest(getRequest()); |
| download3.runUntilStatus(DownloadManager.STATUS_FAILED); |
| |
| // default ordering -- by timestamp descending |
| Cursor cursor = mManager.query(new DownloadManager.Query()); |
| checkAndCloseCursor(cursor, download3, download2, download1); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .orderBy(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP, |
| DownloadManager.Query.ORDER_ASCENDING)); |
| checkAndCloseCursor(cursor, download1, download2, download3); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, |
| DownloadManager.Query.ORDER_DESCENDING)); |
| checkAndCloseCursor(cursor, download2, download1, download3); |
| |
| cursor = mManager.query(new DownloadManager.Query() |
| .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, |
| DownloadManager.Query.ORDER_ASCENDING)); |
| checkAndCloseCursor(cursor, download3, download1, download2); |
| } |
| |
| private void checkAndCloseCursor(Cursor cursor, Download... downloads) { |
| try { |
| int idIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); |
| assertEquals(downloads.length, cursor.getCount()); |
| cursor.moveToFirst(); |
| for (Download download : downloads) { |
| assertEquals(download.mId, cursor.getLong(idIndex)); |
| cursor.moveToNext(); |
| } |
| } finally { |
| cursor.close(); |
| } |
| } |
| |
| public void testInvalidUri() throws Exception { |
| try { |
| enqueueRequest(getRequest("/no_host")); |
| } catch (IllegalArgumentException exc) { // expected |
| return; |
| } |
| |
| fail("No exception thrown for invalid URI"); |
| } |
| |
| public void testDestination() throws Exception { |
| enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |
| Uri destination = getExternalUri(); |
| Download download = enqueueRequest(getRequest().setDestinationUri(destination)); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| Uri localUri = Uri.parse(download.getStringField(DownloadManager.COLUMN_LOCAL_URI)); |
| assertEquals(destination, localUri); |
| |
| InputStream stream = new FileInputStream(destination.getPath()); |
| try { |
| assertEquals(FILE_CONTENT, readStream(stream)); |
| } finally { |
| stream.close(); |
| } |
| } |
| |
| private Uri getExternalUri() { |
| return Uri.fromFile(mTestDirectory).buildUpon().appendPath("testfile.txt").build(); |
| } |
| |
| public void testRequestHeaders() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| Download download = enqueueRequest(getRequest().addRequestHeader("Header1", "value1") |
| .addRequestHeader("Header2", "value2")); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| List<String> headers = takeRequest().getHeaders(); |
| assertTrue(headers.contains("Header1: value1")); |
| assertTrue(headers.contains("Header2: value2")); |
| } |
| |
| public void testDelete() throws Exception { |
| Download download = enqueueRequest(getRequest().addRequestHeader("header", "value")); |
| mManager.remove(download.mId); |
| Cursor cursor = mManager.query(new DownloadManager.Query()); |
| try { |
| assertEquals(0, cursor.getCount()); |
| } finally { |
| cursor.close(); |
| } |
| } |
| |
| public void testSizeLimitOverMobile() throws Exception { |
| enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |
| enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |
| |
| mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1; |
| mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; |
| mSystemFacade.mIsMetered = true; |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| |
| mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; |
| mSystemFacade.mIsMetered = false; |
| // first response was read, but aborted after the DL manager processed the Content-Length |
| // header, so we need to enqueue a second one |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testRedirect301() throws Exception { |
| RecordedRequest lastRequest = runRedirectionTest(301); |
| // for 301, upon retry/resume, we reuse the redirected URI |
| assertEquals(REDIRECTED_PATH, lastRequest.getPath()); |
| } |
| |
| public void testRedirect302() throws Exception { |
| RecordedRequest lastRequest = runRedirectionTest(302); |
| // for 302, upon retry/resume, we use the original URI |
| assertEquals(REQUEST_PATH, lastRequest.getPath()); |
| } |
| |
| public void testRunawayRedirect() throws Exception { |
| for (int i = 0; i < 16; i++) { |
| enqueueResponse(buildEmptyResponse(HTTP_MOVED_TEMP) |
| .setHeader("Location", mServer.getUrl("/" + i).toString())); |
| } |
| |
| final Download download = enqueueRequest(getRequest()); |
| |
| // Ensure that we arrive at failed download, instead of spinning forever |
| download.runUntilStatus(DownloadManager.STATUS_FAILED); |
| assertEquals(DownloadManager.ERROR_TOO_MANY_REDIRECTS, download.getReason()); |
| } |
| |
| public void testRunawayUnavailable() throws Exception { |
| final int RETRY_DELAY = 120; |
| for (int i = 0; i < 16; i++) { |
| enqueueResponse( |
| buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", RETRY_DELAY)); |
| } |
| |
| final Download download = enqueueRequest(getRequest()); |
| for (int i = 0; i < Constants.MAX_RETRIES - 1; i++) { |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| mSystemFacade.incrementTimeMillis((RETRY_DELAY + 60) * SECOND_IN_MILLIS); |
| } |
| |
| // Ensure that we arrive at failed download, instead of spinning forever |
| download.runUntilStatus(DownloadManager.STATUS_FAILED); |
| } |
| |
| public void testNoEtag() throws Exception { |
| enqueueResponse(buildPartialResponse(0, 5).removeHeader("Etag")); |
| runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME); |
| } |
| |
| public void testEtagChanged() throws Exception { |
| final String A = "kittenz"; |
| final String B = "puppiez"; |
| |
| // 1. Try downloading A, but partial result |
| enqueueResponse(buildResponse(HTTP_OK, A.substring(0, 2)) |
| .setHeader("Content-length", A.length()) |
| .setHeader("Etag", A)); |
| |
| // 2. Try resuming A, but fail ETag check |
| enqueueResponse(buildEmptyResponse(HTTP_PRECON_FAILED)); |
| |
| final Download download = enqueueRequest(getRequest()); |
| RecordedRequest req; |
| |
| // 1. Try downloading A, but partial result |
| download.runUntilStatus(STATUS_PAUSED); |
| assertEquals(DownloadManager.PAUSED_WAITING_TO_RETRY, download.getReason()); |
| req = takeRequest(); |
| assertNull(getHeaderValue(req, "Range")); |
| assertNull(getHeaderValue(req, "If-Match")); |
| |
| // 2. Try resuming A, but fail ETag check |
| mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); |
| download.runUntilStatus(STATUS_FAILED); |
| assertEquals(DownloadManager.ERROR_CANNOT_RESUME, download.getReason()); |
| req = takeRequest(); |
| assertEquals("bytes=2-", getHeaderValue(req, "Range")); |
| assertEquals(A, getHeaderValue(req, "If-Match")); |
| } |
| |
| public void testSanitizeMediaType() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK) |
| .setHeader("Content-Type", "text/html; charset=ISO-8859-4")); |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| assertEquals("text/html", download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); |
| } |
| |
| public void testNoContentLength() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK).removeHeader("Content-length")); |
| runSimpleFailureTest(DownloadManager.ERROR_CANNOT_RESUME); |
| } |
| |
| public void testInsufficientSpace() throws Exception { |
| // this would be better done by stubbing the system API to check available space, but in the |
| // meantime, just use an absurdly large header value |
| enqueueResponse(buildEmptyResponse(HTTP_OK) |
| .setHeader("Content-Length", 1024L * 1024 * 1024 * 1024 * 1024)); |
| runSimpleFailureTest(DownloadManager.ERROR_INSUFFICIENT_SPACE); |
| } |
| |
| public void testCancel() throws Exception { |
| // return 'real time' from FakeSystemFacade so that DownloadThread will report progress |
| mSystemFacade.setReturnActualTime(true); |
| enqueueResponse(buildContinuingResponse()); |
| Download download = enqueueRequest(getRequest()); |
| // give the download time to get started and progress to 1% completion |
| // before cancelling it. |
| boolean rslt = download.runUntilProgress(1); |
| assertTrue(rslt); |
| mManager.remove(download.mId); |
| |
| // Verify that row is removed from database |
| final long timeout = SystemClock.elapsedRealtime() + (15 * SECOND_IN_MILLIS); |
| while (download.getStatusIfExists() != -1) { |
| if (SystemClock.elapsedRealtime() > timeout) { |
| throw new TimeoutException("Row wasn't removed"); |
| } |
| SystemClock.sleep(100); |
| } |
| } |
| |
| public void testDownloadCompleteBroadcast() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| assertEquals(1, mSystemFacade.mBroadcastsSent.size()); |
| Intent broadcast = mSystemFacade.mBroadcastsSent.get(0); |
| assertEquals(DownloadManager.ACTION_DOWNLOAD_COMPLETE, broadcast.getAction()); |
| assertEquals(PACKAGE_NAME, broadcast.getPackage()); |
| long intentId = broadcast.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID); |
| assertEquals(download.mId, intentId); |
| } |
| |
| public void testNotificationClickedBroadcast() throws Exception { |
| Download download = enqueueRequest(getRequest()); |
| |
| DownloadReceiver receiver = new DownloadReceiver(); |
| Helpers.setSystemFacade(mSystemFacade); |
| Intent intent = new Intent(Constants.ACTION_LIST); |
| intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId)); |
| intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, |
| new long[] { download.mId }); |
| receiver.onReceive(mContext, intent); |
| |
| assertEquals(1, mSystemFacade.mBroadcastsSent.size()); |
| Intent broadcast = mSystemFacade.mBroadcastsSent.get(0); |
| assertEquals(DownloadManager.ACTION_NOTIFICATION_CLICKED, broadcast.getAction()); |
| assertEquals(PACKAGE_NAME, broadcast.getPackage()); |
| |
| Bundle bOptions = mSystemFacade.mLastBroadcastOptions; |
| assertNotNull(bOptions); |
| BroadcastOptions brOptions = new BroadcastOptions(bOptions); |
| assertTrue(brOptions.allowsBackgroundActivityStarts()); |
| } |
| |
| public void testNotificationCancelDownloadClicked() throws Exception { |
| Download download = enqueueRequest(getRequest()); |
| |
| DownloadReceiver receiver = new DownloadReceiver(); |
| Helpers.setSystemFacade(mSystemFacade); |
| Intent intent = new Intent(Constants.ACTION_CANCEL); |
| intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId)); |
| |
| long[] downloadIds = {download.mId}; |
| intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds); |
| intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, "tag"); |
| receiver.onReceive(mContext, intent); |
| |
| verify(mNotifManager, times(1)).cancel("tag", 0); |
| verify(mDownloadManager, times(1)).remove(downloadIds); |
| } |
| |
| public void testBasicConnectivityChanges() throws Exception { |
| enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |
| |
| // without connectivity, download immediately pauses |
| mSystemFacade.mActiveNetworkType = null; |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| |
| // connecting should start the download |
| mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testAllowedNetworkTypes() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE; |
| mSystemFacade.mIsMetered = true; |
| |
| // by default, use any connection |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| // restrict a download to wifi... |
| download = enqueueRequest(getRequest() |
| .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| // ...then enable wifi |
| mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI; |
| mSystemFacade.mIsMetered = false; |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testRoaming() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| mSystemFacade.mIsRoaming = true; |
| |
| // by default, allow roaming |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| // disallow roaming for a download... |
| download = enqueueRequest(getRequest().setAllowedOverRoaming(false)); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| // ...then turn off roaming |
| mSystemFacade.mIsRoaming = false; |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testContentObserver() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| mResolver.resetNotified(); |
| final Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| assertTrue(mResolver.mNotifyWasCalled); |
| } |
| |
| @Suppress |
| public void testNotificationNever() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| final Download download = enqueueRequest( |
| getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| // TODO: verify different notif types with tags |
| verify(mNotifManager, never()).notify(anyString(), anyInt(), isA(Notification.class)); |
| } |
| |
| @Suppress |
| public void testNotificationVisible() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| // only shows in-progress notifications |
| final Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| // TODO: verify different notif types with tags |
| verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class)); |
| } |
| |
| @Suppress |
| public void testNotificationVisibleComplete() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| final Download download = enqueueRequest(getRequest().setNotificationVisibility( |
| DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| // TODO: verify different notif types with tags |
| verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class)); |
| } |
| |
| public void testRetryAfter() throws Exception { |
| final int delay = 120; |
| enqueueResponse( |
| buildEmptyResponse(HTTP_UNAVAILABLE).setHeader("Retry-after", delay)); |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| |
| // download manager adds random 0-30s offset |
| mSystemFacade.incrementTimeMillis((delay + 31) * 1000); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testManyInterruptions() throws Exception { |
| final int length = FILE_CONTENT.length(); |
| for (int i = 0; i < length; i++) { |
| enqueueResponse(buildPartialResponse(i, i + 1)); |
| } |
| |
| Download download = enqueueRequest(getRequest()); |
| for (int i = 0; i < length - 1; i++) { |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); |
| } |
| |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| checkCompleteDownload(download); |
| } |
| |
| public void testExistingFile() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| // download a file which already exists. |
| // downloadservice should simply create filename with "-" and a number attached |
| // at the end; i.e., download shouldnot fail. |
| Uri destination = getExternalUri(); |
| new File(destination.getPath()).createNewFile(); |
| |
| Download download = enqueueRequest(getRequest().setDestinationUri(destination)); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| public void testEmptyFields() throws Exception { |
| Download download = enqueueRequest(getRequest()); |
| assertEquals("", download.getStringField(DownloadManager.COLUMN_TITLE)); |
| assertEquals("", download.getStringField(DownloadManager.COLUMN_DESCRIPTION)); |
| assertNull(download.getStringField(DownloadManager.COLUMN_MEDIA_TYPE)); |
| assertEquals(0, download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); |
| assertEquals(-1, download.getLongField(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); |
| // just ensure no exception is thrown |
| download.getLongField(DownloadManager.COLUMN_REASON); |
| } |
| |
| public void testRestart() throws Exception { |
| enqueueResponse(buildEmptyResponse(HTTP_NOT_FOUND)); |
| enqueueResponse(buildEmptyResponse(HTTP_OK)); |
| |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_FAILED); |
| |
| mManager.restartDownload(download.mId); |
| assertEquals(DownloadManager.STATUS_PENDING, |
| download.getLongField(DownloadManager.COLUMN_STATUS)); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| } |
| |
| private void checkCompleteDownload(Download download) throws Exception { |
| assertEquals(FILE_CONTENT.length(), |
| download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); |
| assertEquals(FILE_CONTENT, download.getContents()); |
| } |
| |
| private void runSimpleFailureTest(int expectedErrorCode) throws Exception { |
| Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_FAILED); |
| assertEquals(expectedErrorCode, |
| download.getLongField(DownloadManager.COLUMN_REASON)); |
| } |
| |
| /** |
| * Run a redirection test consisting of |
| * 1) Request to REQUEST_PATH with 3xx response redirecting to another URI |
| * 2) Request to REDIRECTED_PATH with interrupted partial response |
| * 3) Resume request to complete download |
| * @return the last request sent to the server, resuming after the interruption |
| */ |
| private RecordedRequest runRedirectionTest(int status) throws Exception { |
| enqueueResponse(buildEmptyResponse(status) |
| .setHeader("Location", mServer.getUrl(REDIRECTED_PATH).toString())); |
| enqueueInterruptedDownloadResponses(5); |
| |
| final Download download = enqueueRequest(getRequest()); |
| download.runUntilStatus(DownloadManager.STATUS_PAUSED); |
| mSystemFacade.incrementTimeMillis(RETRY_DELAY_MILLIS); |
| download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); |
| |
| assertEquals(REQUEST_PATH, takeRequest().getPath()); |
| assertEquals(REDIRECTED_PATH, takeRequest().getPath()); |
| |
| return takeRequest(); |
| } |
| |
| /** |
| * Return value of requested HTTP header, if it exists. |
| */ |
| private static String getHeaderValue(RecordedRequest req, String header) { |
| header = header.toLowerCase() + ":"; |
| for (String h : req.getHeaders()) { |
| if (h.toLowerCase().startsWith(header)) { |
| return h.substring(header.length()).trim(); |
| } |
| } |
| return null; |
| } |
| } |