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