toolkit/components/jsdownloads/test/unit/common_test_Download.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* Any copyright is dedicated to the Public Domain.
michael@0 4 * http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 5
michael@0 6 /**
michael@0 7 * This script is loaded by "test_DownloadCore.js" and "test_DownloadLegacy.js"
michael@0 8 * with different values of the gUseLegacySaver variable, to apply tests to both
michael@0 9 * the "copy" and "legacy" saver implementations.
michael@0 10 */
michael@0 11
michael@0 12 "use strict";
michael@0 13
michael@0 14 ////////////////////////////////////////////////////////////////////////////////
michael@0 15 //// Globals
michael@0 16
michael@0 17 /**
michael@0 18 * Creates and starts a new download, using either DownloadCopySaver or
michael@0 19 * DownloadLegacySaver based on the current test run.
michael@0 20 *
michael@0 21 * @return {Promise}
michael@0 22 * @resolves The newly created Download object. The download may be in progress
michael@0 23 * or already finished. The promiseDownloadStopped function can be
michael@0 24 * used to wait for completion.
michael@0 25 * @rejects JavaScript exception.
michael@0 26 */
michael@0 27 function promiseStartDownload(aSourceUrl) {
michael@0 28 if (gUseLegacySaver) {
michael@0 29 return promiseStartLegacyDownload(aSourceUrl);
michael@0 30 }
michael@0 31
michael@0 32 return promiseNewDownload(aSourceUrl).then(download => {
michael@0 33 download.start();
michael@0 34 return download;
michael@0 35 });
michael@0 36 }
michael@0 37
michael@0 38 /**
michael@0 39 * Creates and starts a new download, configured to keep partial data, and
michael@0 40 * returns only when the first part of "interruptible_resumable.txt" has been
michael@0 41 * saved to disk. You must call "continueResponses" to allow the interruptible
michael@0 42 * request to continue.
michael@0 43 *
michael@0 44 * This function uses either DownloadCopySaver or DownloadLegacySaver based on
michael@0 45 * the current test run.
michael@0 46 *
michael@0 47 * @return {Promise}
michael@0 48 * @resolves The newly created Download object, still in progress.
michael@0 49 * @rejects JavaScript exception.
michael@0 50 */
michael@0 51 function promiseStartDownload_tryToKeepPartialData() {
michael@0 52 return Task.spawn(function () {
michael@0 53 mustInterruptResponses();
michael@0 54
michael@0 55 // Start a new download and configure it to keep partially downloaded data.
michael@0 56 let download;
michael@0 57 if (!gUseLegacySaver) {
michael@0 58 let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 59 download = yield Downloads.createDownload({
michael@0 60 source: httpUrl("interruptible_resumable.txt"),
michael@0 61 target: { path: targetFilePath,
michael@0 62 partFilePath: targetFilePath + ".part" },
michael@0 63 });
michael@0 64 download.tryToKeepPartialData = true;
michael@0 65 download.start();
michael@0 66 } else {
michael@0 67 // Start a download using nsIExternalHelperAppService, that is configured
michael@0 68 // to keep partially downloaded data by default.
michael@0 69 download = yield promiseStartExternalHelperAppServiceDownload();
michael@0 70 }
michael@0 71
michael@0 72 yield promiseDownloadMidway(download);
michael@0 73 yield promisePartFileReady(download);
michael@0 74
michael@0 75 throw new Task.Result(download);
michael@0 76 });
michael@0 77 }
michael@0 78
michael@0 79 /**
michael@0 80 * This function should be called after the progress notification for a download
michael@0 81 * is received, and waits for the worker thread of BackgroundFileSaver to
michael@0 82 * receive the data to be written to the ".part" file on disk.
michael@0 83 *
michael@0 84 * @return {Promise}
michael@0 85 * @resolves When the ".part" file has been written to disk.
michael@0 86 * @rejects JavaScript exception.
michael@0 87 */
michael@0 88 function promisePartFileReady(aDownload) {
michael@0 89 return Task.spawn(function () {
michael@0 90 // We don't have control over the file output code in BackgroundFileSaver.
michael@0 91 // After we receive the download progress notification, we may only check
michael@0 92 // that the ".part" file has been created, while its size cannot be
michael@0 93 // determined because the file is currently open.
michael@0 94 try {
michael@0 95 do {
michael@0 96 yield promiseTimeout(50);
michael@0 97 } while (!(yield OS.File.exists(aDownload.target.partFilePath)));
michael@0 98 } catch (ex if ex instanceof OS.File.Error) {
michael@0 99 // This indicates that the file has been created and cannot be accessed.
michael@0 100 // The specific error might vary with the platform.
michael@0 101 do_print("Expected exception while checking existence: " + ex.toString());
michael@0 102 // Wait some more time to allow the write to complete.
michael@0 103 yield promiseTimeout(100);
michael@0 104 }
michael@0 105 });
michael@0 106 }
michael@0 107
michael@0 108 ////////////////////////////////////////////////////////////////////////////////
michael@0 109 //// Tests
michael@0 110
michael@0 111 /**
michael@0 112 * Executes a download and checks its basic properties after construction.
michael@0 113 * The download is started by constructing the simplest Download object with
michael@0 114 * the "copy" saver, or using the legacy nsITransfer interface.
michael@0 115 */
michael@0 116 add_task(function test_basic()
michael@0 117 {
michael@0 118 let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
michael@0 119
michael@0 120 let download;
michael@0 121 if (!gUseLegacySaver) {
michael@0 122 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 123 // we can check its basic properties before it starts.
michael@0 124 download = yield Downloads.createDownload({
michael@0 125 source: { url: httpUrl("source.txt") },
michael@0 126 target: { path: targetFile.path },
michael@0 127 saver: { type: "copy" },
michael@0 128 });
michael@0 129
michael@0 130 do_check_eq(download.source.url, httpUrl("source.txt"));
michael@0 131 do_check_eq(download.target.path, targetFile.path);
michael@0 132
michael@0 133 yield download.start();
michael@0 134 } else {
michael@0 135 // When testing DownloadLegacySaver, the download is already started when it
michael@0 136 // is created, thus we must check its basic properties while in progress.
michael@0 137 download = yield promiseStartLegacyDownload(null,
michael@0 138 { targetFile: targetFile });
michael@0 139
michael@0 140 do_check_eq(download.source.url, httpUrl("source.txt"));
michael@0 141 do_check_eq(download.target.path, targetFile.path);
michael@0 142
michael@0 143 yield promiseDownloadStopped(download);
michael@0 144 }
michael@0 145
michael@0 146 // Check additional properties on the finished download.
michael@0 147 do_check_true(download.source.referrer === null);
michael@0 148
michael@0 149 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 150 });
michael@0 151
michael@0 152 /**
michael@0 153 * Executes a download with the tryToKeepPartialData property set, and ensures
michael@0 154 * that the file is saved correctly. When testing DownloadLegacySaver, the
michael@0 155 * download is executed using the nsIExternalHelperAppService component.
michael@0 156 */
michael@0 157 add_task(function test_basic_tryToKeepPartialData()
michael@0 158 {
michael@0 159 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 160 continueResponses();
michael@0 161 yield promiseDownloadStopped(download);
michael@0 162
michael@0 163 // The target file should now have been created, and the ".part" file deleted.
michael@0 164 yield promiseVerifyContents(download.target.path,
michael@0 165 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 166 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 167 do_check_eq(32, download.saver.getSha256Hash().length);
michael@0 168 });
michael@0 169
michael@0 170 /**
michael@0 171 * Checks the referrer for downloads.
michael@0 172 */
michael@0 173 add_task(function test_referrer()
michael@0 174 {
michael@0 175 let sourcePath = "/test_referrer.txt";
michael@0 176 let sourceUrl = httpUrl("test_referrer.txt");
michael@0 177 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 178
michael@0 179 function cleanup() {
michael@0 180 gHttpServer.registerPathHandler(sourcePath, null);
michael@0 181 }
michael@0 182 do_register_cleanup(cleanup);
michael@0 183
michael@0 184 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
michael@0 185 aResponse.setHeader("Content-Type", "text/plain", false);
michael@0 186
michael@0 187 do_check_true(aRequest.hasHeader("Referer"));
michael@0 188 do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL);
michael@0 189 });
michael@0 190 let download = yield Downloads.createDownload({
michael@0 191 source: { url: sourceUrl, referrer: TEST_REFERRER_URL },
michael@0 192 target: targetPath,
michael@0 193 });
michael@0 194 do_check_eq(download.source.referrer, TEST_REFERRER_URL);
michael@0 195 yield download.start();
michael@0 196
michael@0 197 download = yield Downloads.createDownload({
michael@0 198 source: { url: sourceUrl, referrer: TEST_REFERRER_URL,
michael@0 199 isPrivate: true },
michael@0 200 target: targetPath,
michael@0 201 });
michael@0 202 do_check_eq(download.source.referrer, TEST_REFERRER_URL);
michael@0 203 yield download.start();
michael@0 204
michael@0 205 // Test the download still works for non-HTTP channel with referrer.
michael@0 206 sourceUrl = "data:text/html,<html><body></body></html>";
michael@0 207 download = yield Downloads.createDownload({
michael@0 208 source: { url: sourceUrl, referrer: TEST_REFERRER_URL },
michael@0 209 target: targetPath,
michael@0 210 });
michael@0 211 do_check_eq(download.source.referrer, TEST_REFERRER_URL);
michael@0 212 yield download.start();
michael@0 213
michael@0 214 cleanup();
michael@0 215 });
michael@0 216
michael@0 217 /**
michael@0 218 * Checks initial and final state and progress for a successful download.
michael@0 219 */
michael@0 220 add_task(function test_initial_final_state()
michael@0 221 {
michael@0 222 let download;
michael@0 223 if (!gUseLegacySaver) {
michael@0 224 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 225 // we can check its state before it starts.
michael@0 226 download = yield promiseNewDownload();
michael@0 227
michael@0 228 do_check_true(download.stopped);
michael@0 229 do_check_false(download.succeeded);
michael@0 230 do_check_false(download.canceled);
michael@0 231 do_check_true(download.error === null);
michael@0 232 do_check_eq(download.progress, 0);
michael@0 233 do_check_true(download.startTime === null);
michael@0 234
michael@0 235 yield download.start();
michael@0 236 } else {
michael@0 237 // When testing DownloadLegacySaver, the download is already started when it
michael@0 238 // is created, thus we cannot check its initial state.
michael@0 239 download = yield promiseStartLegacyDownload();
michael@0 240 yield promiseDownloadStopped(download);
michael@0 241 }
michael@0 242
michael@0 243 do_check_true(download.stopped);
michael@0 244 do_check_true(download.succeeded);
michael@0 245 do_check_false(download.canceled);
michael@0 246 do_check_true(download.error === null);
michael@0 247 do_check_eq(download.progress, 100);
michael@0 248 do_check_true(isValidDate(download.startTime));
michael@0 249 });
michael@0 250
michael@0 251 /**
michael@0 252 * Checks the notification of the final download state.
michael@0 253 */
michael@0 254 add_task(function test_final_state_notified()
michael@0 255 {
michael@0 256 mustInterruptResponses();
michael@0 257
michael@0 258 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 259
michael@0 260 let onchangeNotified = false;
michael@0 261 let lastNotifiedStopped;
michael@0 262 let lastNotifiedProgress;
michael@0 263 download.onchange = function () {
michael@0 264 onchangeNotified = true;
michael@0 265 lastNotifiedStopped = download.stopped;
michael@0 266 lastNotifiedProgress = download.progress;
michael@0 267 };
michael@0 268
michael@0 269 // Allow the download to complete.
michael@0 270 let promiseAttempt = download.start();
michael@0 271 continueResponses();
michael@0 272 yield promiseAttempt;
michael@0 273
michael@0 274 // The view should have been notified before the download completes.
michael@0 275 do_check_true(onchangeNotified);
michael@0 276 do_check_true(lastNotifiedStopped);
michael@0 277 do_check_eq(lastNotifiedProgress, 100);
michael@0 278 });
michael@0 279
michael@0 280 /**
michael@0 281 * Checks intermediate progress for a successful download.
michael@0 282 */
michael@0 283 add_task(function test_intermediate_progress()
michael@0 284 {
michael@0 285 mustInterruptResponses();
michael@0 286
michael@0 287 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 288
michael@0 289 yield promiseDownloadMidway(download);
michael@0 290
michael@0 291 do_check_true(download.hasProgress);
michael@0 292 do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
michael@0 293 do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
michael@0 294
michael@0 295 // Continue after the first chunk of data is fully received.
michael@0 296 continueResponses();
michael@0 297 yield promiseDownloadStopped(download);
michael@0 298
michael@0 299 do_check_true(download.stopped);
michael@0 300 do_check_eq(download.progress, 100);
michael@0 301
michael@0 302 yield promiseVerifyContents(download.target.path,
michael@0 303 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 304 });
michael@0 305
michael@0 306 /**
michael@0 307 * Downloads a file with a "Content-Length" of 0 and checks the progress.
michael@0 308 */
michael@0 309 add_task(function test_empty_progress()
michael@0 310 {
michael@0 311 let download = yield promiseStartDownload(httpUrl("empty.txt"));
michael@0 312 yield promiseDownloadStopped(download);
michael@0 313
michael@0 314 do_check_true(download.stopped);
michael@0 315 do_check_true(download.hasProgress);
michael@0 316 do_check_eq(download.progress, 100);
michael@0 317 do_check_eq(download.currentBytes, 0);
michael@0 318 do_check_eq(download.totalBytes, 0);
michael@0 319
michael@0 320 // We should have received the content type even for an empty file.
michael@0 321 do_check_eq(download.contentType, "text/plain");
michael@0 322
michael@0 323 do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
michael@0 324 });
michael@0 325
michael@0 326 /**
michael@0 327 * Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData
michael@0 328 * property set, and ensures that the file is saved correctly.
michael@0 329 */
michael@0 330 add_task(function test_empty_progress_tryToKeepPartialData()
michael@0 331 {
michael@0 332 // Start a new download and configure it to keep partially downloaded data.
michael@0 333 let download;
michael@0 334 if (!gUseLegacySaver) {
michael@0 335 let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 336 download = yield Downloads.createDownload({
michael@0 337 source: httpUrl("empty.txt"),
michael@0 338 target: { path: targetFilePath,
michael@0 339 partFilePath: targetFilePath + ".part" },
michael@0 340 });
michael@0 341 download.tryToKeepPartialData = true;
michael@0 342 download.start();
michael@0 343 } else {
michael@0 344 // Start a download using nsIExternalHelperAppService, that is configured
michael@0 345 // to keep partially downloaded data by default.
michael@0 346 download = yield promiseStartExternalHelperAppServiceDownload(
michael@0 347 httpUrl("empty.txt"));
michael@0 348 }
michael@0 349 yield promiseDownloadStopped(download);
michael@0 350
michael@0 351 // The target file should now have been created, and the ".part" file deleted.
michael@0 352 do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
michael@0 353 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 354 do_check_eq(32, download.saver.getSha256Hash().length);
michael@0 355 });
michael@0 356
michael@0 357 /**
michael@0 358 * Downloads an empty file with no "Content-Length" and checks the progress.
michael@0 359 */
michael@0 360 add_task(function test_empty_noprogress()
michael@0 361 {
michael@0 362 let sourcePath = "/test_empty_noprogress.txt";
michael@0 363 let sourceUrl = httpUrl("test_empty_noprogress.txt");
michael@0 364 let deferRequestReceived = Promise.defer();
michael@0 365
michael@0 366 // Register an interruptible handler that notifies us when the request occurs.
michael@0 367 function cleanup() {
michael@0 368 gHttpServer.registerPathHandler(sourcePath, null);
michael@0 369 }
michael@0 370 do_register_cleanup(cleanup);
michael@0 371
michael@0 372 registerInterruptibleHandler(sourcePath,
michael@0 373 function firstPart(aRequest, aResponse) {
michael@0 374 aResponse.setHeader("Content-Type", "text/plain", false);
michael@0 375 deferRequestReceived.resolve();
michael@0 376 }, function secondPart(aRequest, aResponse) { });
michael@0 377
michael@0 378 // Start the download, without allowing the request to finish.
michael@0 379 mustInterruptResponses();
michael@0 380 let download;
michael@0 381 if (!gUseLegacySaver) {
michael@0 382 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 383 // we can hook its onchange callback that will be notified when the
michael@0 384 // download starts.
michael@0 385 download = yield promiseNewDownload(sourceUrl);
michael@0 386
michael@0 387 download.onchange = function () {
michael@0 388 if (!download.stopped) {
michael@0 389 do_check_false(download.hasProgress);
michael@0 390 do_check_eq(download.currentBytes, 0);
michael@0 391 do_check_eq(download.totalBytes, 0);
michael@0 392 }
michael@0 393 };
michael@0 394
michael@0 395 download.start();
michael@0 396 } else {
michael@0 397 // When testing DownloadLegacySaver, the download is already started when it
michael@0 398 // is created, and it may have already made all needed property change
michael@0 399 // notifications, thus there is no point in checking the onchange callback.
michael@0 400 download = yield promiseStartLegacyDownload(sourceUrl);
michael@0 401 }
michael@0 402
michael@0 403 // Wait for the request to be received by the HTTP server, but don't allow the
michael@0 404 // request to finish yet. Before checking the download state, wait for the
michael@0 405 // events to be processed by the client.
michael@0 406 yield deferRequestReceived.promise;
michael@0 407 yield promiseExecuteSoon();
michael@0 408
michael@0 409 // Check that this download has no progress report.
michael@0 410 do_check_false(download.stopped);
michael@0 411 do_check_false(download.hasProgress);
michael@0 412 do_check_eq(download.currentBytes, 0);
michael@0 413 do_check_eq(download.totalBytes, 0);
michael@0 414
michael@0 415 // Now allow the response to finish.
michael@0 416 continueResponses();
michael@0 417 yield promiseDownloadStopped(download);
michael@0 418
michael@0 419 // We should have received the content type even if no progress is reported.
michael@0 420 do_check_eq(download.contentType, "text/plain");
michael@0 421
michael@0 422 // Verify the state of the completed download.
michael@0 423 do_check_true(download.stopped);
michael@0 424 do_check_false(download.hasProgress);
michael@0 425 do_check_eq(download.progress, 100);
michael@0 426 do_check_eq(download.currentBytes, 0);
michael@0 427 do_check_eq(download.totalBytes, 0);
michael@0 428
michael@0 429 do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
michael@0 430 });
michael@0 431
michael@0 432 /**
michael@0 433 * Calls the "start" method two times before the download is finished.
michael@0 434 */
michael@0 435 add_task(function test_start_twice()
michael@0 436 {
michael@0 437 mustInterruptResponses();
michael@0 438
michael@0 439 let download;
michael@0 440 if (!gUseLegacySaver) {
michael@0 441 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 442 // we can start the download later during the test.
michael@0 443 download = yield promiseNewDownload(httpUrl("interruptible.txt"));
michael@0 444 } else {
michael@0 445 // When testing DownloadLegacySaver, the download is already started when it
michael@0 446 // is created. Effectively, we are starting the download three times.
michael@0 447 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"));
michael@0 448 }
michael@0 449
michael@0 450 // Call the start method two times.
michael@0 451 let promiseAttempt1 = download.start();
michael@0 452 let promiseAttempt2 = download.start();
michael@0 453
michael@0 454 // Allow the download to finish.
michael@0 455 continueResponses();
michael@0 456
michael@0 457 // Both promises should now be resolved.
michael@0 458 yield promiseAttempt1;
michael@0 459 yield promiseAttempt2;
michael@0 460
michael@0 461 do_check_true(download.stopped);
michael@0 462 do_check_true(download.succeeded);
michael@0 463 do_check_false(download.canceled);
michael@0 464 do_check_true(download.error === null);
michael@0 465
michael@0 466 yield promiseVerifyContents(download.target.path,
michael@0 467 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 468 });
michael@0 469
michael@0 470 /**
michael@0 471 * Cancels a download and verifies that its state is reported correctly.
michael@0 472 */
michael@0 473 add_task(function test_cancel_midway()
michael@0 474 {
michael@0 475 mustInterruptResponses();
michael@0 476
michael@0 477 // In this test case, we execute different checks that are only possible with
michael@0 478 // DownloadCopySaver or DownloadLegacySaver respectively.
michael@0 479 let download;
michael@0 480 let options = {};
michael@0 481 if (!gUseLegacySaver) {
michael@0 482 download = yield promiseNewDownload(httpUrl("interruptible.txt"));
michael@0 483 } else {
michael@0 484 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"),
michael@0 485 options);
michael@0 486 }
michael@0 487
michael@0 488 // Cancel the download after receiving the first part of the response.
michael@0 489 let deferCancel = Promise.defer();
michael@0 490 let onchange = function () {
michael@0 491 if (!download.stopped && !download.canceled && download.progress == 50) {
michael@0 492 // Cancel the download immediately during the notification.
michael@0 493 deferCancel.resolve(download.cancel());
michael@0 494
michael@0 495 // The state change happens immediately after calling "cancel", but
michael@0 496 // temporary files or part files may still exist at this point.
michael@0 497 do_check_true(download.canceled);
michael@0 498 }
michael@0 499 };
michael@0 500
michael@0 501 // Register for the notification, but also call the function directly in
michael@0 502 // case the download already reached the expected progress. This may happen
michael@0 503 // when using DownloadLegacySaver.
michael@0 504 download.onchange = onchange;
michael@0 505 onchange();
michael@0 506
michael@0 507 let promiseAttempt;
michael@0 508 if (!gUseLegacySaver) {
michael@0 509 promiseAttempt = download.start();
michael@0 510 }
michael@0 511
michael@0 512 // Wait on the promise returned by the "cancel" method to ensure that the
michael@0 513 // cancellation process finished and temporary files were removed.
michael@0 514 yield deferCancel.promise;
michael@0 515
michael@0 516 if (gUseLegacySaver) {
michael@0 517 // The nsIWebBrowserPersist instance should have been canceled now.
michael@0 518 do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT);
michael@0 519 }
michael@0 520
michael@0 521 do_check_true(download.stopped);
michael@0 522 do_check_true(download.canceled);
michael@0 523 do_check_true(download.error === null);
michael@0 524
michael@0 525 do_check_false(yield OS.File.exists(download.target.path));
michael@0 526
michael@0 527 // Progress properties are not reset by canceling.
michael@0 528 do_check_eq(download.progress, 50);
michael@0 529 do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
michael@0 530 do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
michael@0 531
michael@0 532 if (!gUseLegacySaver) {
michael@0 533 // The promise returned by "start" should have been rejected meanwhile.
michael@0 534 try {
michael@0 535 yield promiseAttempt;
michael@0 536 do_throw("The download should have been canceled.");
michael@0 537 } catch (ex if ex instanceof Downloads.Error) {
michael@0 538 do_check_false(ex.becauseSourceFailed);
michael@0 539 do_check_false(ex.becauseTargetFailed);
michael@0 540 }
michael@0 541 }
michael@0 542 });
michael@0 543
michael@0 544 /**
michael@0 545 * Cancels a download while keeping partially downloaded data, and verifies that
michael@0 546 * both the target file and the ".part" file are deleted.
michael@0 547 */
michael@0 548 add_task(function test_cancel_midway_tryToKeepPartialData()
michael@0 549 {
michael@0 550 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 551
michael@0 552 do_check_true(yield OS.File.exists(download.target.path));
michael@0 553 do_check_true(yield OS.File.exists(download.target.partFilePath));
michael@0 554
michael@0 555 yield download.cancel();
michael@0 556 yield download.removePartialData();
michael@0 557
michael@0 558 do_check_true(download.stopped);
michael@0 559 do_check_true(download.canceled);
michael@0 560 do_check_true(download.error === null);
michael@0 561
michael@0 562 do_check_false(yield OS.File.exists(download.target.path));
michael@0 563 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 564 });
michael@0 565
michael@0 566 /**
michael@0 567 * Cancels a download right after starting it.
michael@0 568 */
michael@0 569 add_task(function test_cancel_immediately()
michael@0 570 {
michael@0 571 mustInterruptResponses();
michael@0 572
michael@0 573 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 574
michael@0 575 let promiseAttempt = download.start();
michael@0 576 do_check_false(download.stopped);
michael@0 577
michael@0 578 let promiseCancel = download.cancel();
michael@0 579 do_check_true(download.canceled);
michael@0 580
michael@0 581 // At this point, we don't know whether the download has already stopped or
michael@0 582 // is still waiting for cancellation. We can wait on the promise returned
michael@0 583 // by the "start" method to know for sure.
michael@0 584 try {
michael@0 585 yield promiseAttempt;
michael@0 586 do_throw("The download should have been canceled.");
michael@0 587 } catch (ex if ex instanceof Downloads.Error) {
michael@0 588 do_check_false(ex.becauseSourceFailed);
michael@0 589 do_check_false(ex.becauseTargetFailed);
michael@0 590 }
michael@0 591
michael@0 592 do_check_true(download.stopped);
michael@0 593 do_check_true(download.canceled);
michael@0 594 do_check_true(download.error === null);
michael@0 595
michael@0 596 do_check_false(yield OS.File.exists(download.target.path));
michael@0 597
michael@0 598 // Check that the promise returned by the "cancel" method has been resolved.
michael@0 599 yield promiseCancel;
michael@0 600 });
michael@0 601
michael@0 602 /**
michael@0 603 * Cancels and restarts a download sequentially.
michael@0 604 */
michael@0 605 add_task(function test_cancel_midway_restart()
michael@0 606 {
michael@0 607 mustInterruptResponses();
michael@0 608
michael@0 609 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 610
michael@0 611 // The first time, cancel the download midway.
michael@0 612 yield promiseDownloadMidway(download);
michael@0 613 yield download.cancel();
michael@0 614
michael@0 615 do_check_true(download.stopped);
michael@0 616
michael@0 617 // The second time, we'll provide the entire interruptible response.
michael@0 618 continueResponses();
michael@0 619 download.onchange = null;
michael@0 620 let promiseAttempt = download.start();
michael@0 621
michael@0 622 // Download state should have already been reset.
michael@0 623 do_check_false(download.stopped);
michael@0 624 do_check_false(download.canceled);
michael@0 625 do_check_true(download.error === null);
michael@0 626
michael@0 627 // For the following test, we rely on the network layer reporting its progress
michael@0 628 // asynchronously. Otherwise, there is nothing stopping the restarted
michael@0 629 // download from reaching the same progress as the first request already.
michael@0 630 do_check_eq(download.progress, 0);
michael@0 631 do_check_eq(download.totalBytes, 0);
michael@0 632 do_check_eq(download.currentBytes, 0);
michael@0 633
michael@0 634 yield promiseAttempt;
michael@0 635
michael@0 636 do_check_true(download.stopped);
michael@0 637 do_check_true(download.succeeded);
michael@0 638 do_check_false(download.canceled);
michael@0 639 do_check_true(download.error === null);
michael@0 640
michael@0 641 yield promiseVerifyContents(download.target.path,
michael@0 642 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 643 });
michael@0 644
michael@0 645 /**
michael@0 646 * Cancels a download and restarts it from where it stopped.
michael@0 647 */
michael@0 648 add_task(function test_cancel_midway_restart_tryToKeepPartialData()
michael@0 649 {
michael@0 650 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 651 yield download.cancel();
michael@0 652
michael@0 653 do_check_true(download.stopped);
michael@0 654 do_check_true(download.hasPartialData);
michael@0 655
michael@0 656 // The target file should not exist, but we should have kept the partial data.
michael@0 657 do_check_false(yield OS.File.exists(download.target.path));
michael@0 658 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
michael@0 659
michael@0 660 // Verify that the server sent the response from the start.
michael@0 661 do_check_eq(gMostRecentFirstBytePos, 0);
michael@0 662
michael@0 663 // The second time, we'll request and obtain the second part of the response,
michael@0 664 // but we still stop when half of the remaining progress is reached.
michael@0 665 let deferMidway = Promise.defer();
michael@0 666 download.onchange = function () {
michael@0 667 if (!download.stopped && !download.canceled &&
michael@0 668 download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) {
michael@0 669 download.onchange = null;
michael@0 670 deferMidway.resolve();
michael@0 671 }
michael@0 672 };
michael@0 673
michael@0 674 mustInterruptResponses();
michael@0 675 let promiseAttempt = download.start();
michael@0 676
michael@0 677 // Continue when the number of bytes we received is correct, then check that
michael@0 678 // progress is at about 75 percent. The exact figure may vary because of
michael@0 679 // rounding issues, since the total number of bytes in the response might not
michael@0 680 // be a multiple of four.
michael@0 681 yield deferMidway.promise;
michael@0 682 do_check_true(download.progress > 72 && download.progress < 78);
michael@0 683
michael@0 684 // Now we allow the download to finish.
michael@0 685 continueResponses();
michael@0 686 yield promiseAttempt;
michael@0 687
michael@0 688 // Check that the server now sent the second part only.
michael@0 689 do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length);
michael@0 690
michael@0 691 // The target file should now have been created, and the ".part" file deleted.
michael@0 692 yield promiseVerifyContents(download.target.path,
michael@0 693 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 694 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 695 });
michael@0 696
michael@0 697 /**
michael@0 698 * Cancels a download while keeping partially downloaded data, then removes the
michael@0 699 * data and restarts the download from the beginning.
michael@0 700 */
michael@0 701 add_task(function test_cancel_midway_restart_removePartialData()
michael@0 702 {
michael@0 703 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 704 yield download.cancel();
michael@0 705
michael@0 706 do_check_true(download.hasPartialData);
michael@0 707 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
michael@0 708
michael@0 709 yield download.removePartialData();
michael@0 710
michael@0 711 do_check_false(download.hasPartialData);
michael@0 712 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 713
michael@0 714 // The second time, we'll request and obtain the entire response again.
michael@0 715 continueResponses();
michael@0 716 yield download.start();
michael@0 717
michael@0 718 // Verify that the server sent the response from the start.
michael@0 719 do_check_eq(gMostRecentFirstBytePos, 0);
michael@0 720
michael@0 721 // The target file should now have been created, and the ".part" file deleted.
michael@0 722 yield promiseVerifyContents(download.target.path,
michael@0 723 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 724 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 725 });
michael@0 726
michael@0 727 /**
michael@0 728 * Cancels a download while keeping partially downloaded data, then removes the
michael@0 729 * data and restarts the download from the beginning without keeping the partial
michael@0 730 * data anymore.
michael@0 731 */
michael@0 732 add_task(function test_cancel_midway_restart_tryToKeepPartialData_false()
michael@0 733 {
michael@0 734 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 735 yield download.cancel();
michael@0 736
michael@0 737 download.tryToKeepPartialData = false;
michael@0 738
michael@0 739 // The above property change does not affect existing partial data.
michael@0 740 do_check_true(download.hasPartialData);
michael@0 741 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
michael@0 742
michael@0 743 yield download.removePartialData();
michael@0 744 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 745
michael@0 746 // Restart the download from the beginning.
michael@0 747 mustInterruptResponses();
michael@0 748 download.start();
michael@0 749
michael@0 750 yield promiseDownloadMidway(download);
michael@0 751 yield promisePartFileReady(download);
michael@0 752
michael@0 753 // While the download is in progress, we should still have a ".part" file.
michael@0 754 do_check_false(download.hasPartialData);
michael@0 755 do_check_true(yield OS.File.exists(download.target.partFilePath));
michael@0 756
michael@0 757 yield download.cancel();
michael@0 758
michael@0 759 // The ".part" file should be deleted now that the download is canceled.
michael@0 760 do_check_false(download.hasPartialData);
michael@0 761 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 762
michael@0 763 // The third time, we'll request and obtain the entire response again.
michael@0 764 continueResponses();
michael@0 765 yield download.start();
michael@0 766
michael@0 767 // Verify that the server sent the response from the start.
michael@0 768 do_check_eq(gMostRecentFirstBytePos, 0);
michael@0 769
michael@0 770 // The target file should now have been created, and the ".part" file deleted.
michael@0 771 yield promiseVerifyContents(download.target.path,
michael@0 772 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 773 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 774 });
michael@0 775
michael@0 776 /**
michael@0 777 * Cancels a download right after starting it, then restarts it immediately.
michael@0 778 */
michael@0 779 add_task(function test_cancel_immediately_restart_immediately()
michael@0 780 {
michael@0 781 mustInterruptResponses();
michael@0 782
michael@0 783 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 784 let promiseAttempt = download.start();
michael@0 785
michael@0 786 do_check_false(download.stopped);
michael@0 787
michael@0 788 download.cancel();
michael@0 789 do_check_true(download.canceled);
michael@0 790
michael@0 791 let promiseRestarted = download.start();
michael@0 792 do_check_false(download.stopped);
michael@0 793 do_check_false(download.canceled);
michael@0 794 do_check_true(download.error === null);
michael@0 795
michael@0 796 // For the following test, we rely on the network layer reporting its progress
michael@0 797 // asynchronously. Otherwise, there is nothing stopping the restarted
michael@0 798 // download from reaching the same progress as the first request already.
michael@0 799 do_check_eq(download.hasProgress, false);
michael@0 800 do_check_eq(download.progress, 0);
michael@0 801 do_check_eq(download.totalBytes, 0);
michael@0 802 do_check_eq(download.currentBytes, 0);
michael@0 803
michael@0 804 // Ensure the next request is now allowed to complete, regardless of whether
michael@0 805 // the canceled request was received by the server or not.
michael@0 806 continueResponses();
michael@0 807 try {
michael@0 808 yield promiseAttempt;
michael@0 809 // If we get here, it means that the first attempt actually succeeded. In
michael@0 810 // fact, this could be a valid outcome, because the cancellation request may
michael@0 811 // not have been processed in time before the download finished.
michael@0 812 do_print("The download should have been canceled.");
michael@0 813 } catch (ex if ex instanceof Downloads.Error) {
michael@0 814 do_check_false(ex.becauseSourceFailed);
michael@0 815 do_check_false(ex.becauseTargetFailed);
michael@0 816 }
michael@0 817
michael@0 818 yield promiseRestarted;
michael@0 819
michael@0 820 do_check_true(download.stopped);
michael@0 821 do_check_true(download.succeeded);
michael@0 822 do_check_false(download.canceled);
michael@0 823 do_check_true(download.error === null);
michael@0 824
michael@0 825 yield promiseVerifyContents(download.target.path,
michael@0 826 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 827 });
michael@0 828
michael@0 829 /**
michael@0 830 * Cancels a download midway, then restarts it immediately.
michael@0 831 */
michael@0 832 add_task(function test_cancel_midway_restart_immediately()
michael@0 833 {
michael@0 834 mustInterruptResponses();
michael@0 835
michael@0 836 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 837 let promiseAttempt = download.start();
michael@0 838
michael@0 839 // The first time, cancel the download midway.
michael@0 840 yield promiseDownloadMidway(download);
michael@0 841 download.cancel();
michael@0 842 do_check_true(download.canceled);
michael@0 843
michael@0 844 let promiseRestarted = download.start();
michael@0 845 do_check_false(download.stopped);
michael@0 846 do_check_false(download.canceled);
michael@0 847 do_check_true(download.error === null);
michael@0 848
michael@0 849 // For the following test, we rely on the network layer reporting its progress
michael@0 850 // asynchronously. Otherwise, there is nothing stopping the restarted
michael@0 851 // download from reaching the same progress as the first request already.
michael@0 852 do_check_eq(download.hasProgress, false);
michael@0 853 do_check_eq(download.progress, 0);
michael@0 854 do_check_eq(download.totalBytes, 0);
michael@0 855 do_check_eq(download.currentBytes, 0);
michael@0 856
michael@0 857 // The second request is allowed to complete.
michael@0 858 continueResponses();
michael@0 859 try {
michael@0 860 yield promiseAttempt;
michael@0 861 do_throw("The download should have been canceled.");
michael@0 862 } catch (ex if ex instanceof Downloads.Error) {
michael@0 863 do_check_false(ex.becauseSourceFailed);
michael@0 864 do_check_false(ex.becauseTargetFailed);
michael@0 865 }
michael@0 866
michael@0 867 yield promiseRestarted;
michael@0 868
michael@0 869 do_check_true(download.stopped);
michael@0 870 do_check_true(download.succeeded);
michael@0 871 do_check_false(download.canceled);
michael@0 872 do_check_true(download.error === null);
michael@0 873
michael@0 874 yield promiseVerifyContents(download.target.path,
michael@0 875 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 876 });
michael@0 877
michael@0 878 /**
michael@0 879 * Calls the "cancel" method on a successful download.
michael@0 880 */
michael@0 881 add_task(function test_cancel_successful()
michael@0 882 {
michael@0 883 let download = yield promiseStartDownload();
michael@0 884 yield promiseDownloadStopped(download);
michael@0 885
michael@0 886 // The cancel method should succeed with no effect.
michael@0 887 yield download.cancel();
michael@0 888
michael@0 889 do_check_true(download.stopped);
michael@0 890 do_check_true(download.succeeded);
michael@0 891 do_check_false(download.canceled);
michael@0 892 do_check_true(download.error === null);
michael@0 893
michael@0 894 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 895 });
michael@0 896
michael@0 897 /**
michael@0 898 * Calls the "cancel" method two times in a row.
michael@0 899 */
michael@0 900 add_task(function test_cancel_twice()
michael@0 901 {
michael@0 902 mustInterruptResponses();
michael@0 903
michael@0 904 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 905
michael@0 906 let promiseAttempt = download.start();
michael@0 907 do_check_false(download.stopped);
michael@0 908
michael@0 909 let promiseCancel1 = download.cancel();
michael@0 910 do_check_true(download.canceled);
michael@0 911 let promiseCancel2 = download.cancel();
michael@0 912
michael@0 913 try {
michael@0 914 yield promiseAttempt;
michael@0 915 do_throw("The download should have been canceled.");
michael@0 916 } catch (ex if ex instanceof Downloads.Error) {
michael@0 917 do_check_false(ex.becauseSourceFailed);
michael@0 918 do_check_false(ex.becauseTargetFailed);
michael@0 919 }
michael@0 920
michael@0 921 // Both promises should now be resolved.
michael@0 922 yield promiseCancel1;
michael@0 923 yield promiseCancel2;
michael@0 924
michael@0 925 do_check_true(download.stopped);
michael@0 926 do_check_false(download.succeeded);
michael@0 927 do_check_true(download.canceled);
michael@0 928 do_check_true(download.error === null);
michael@0 929
michael@0 930 do_check_false(yield OS.File.exists(download.target.path));
michael@0 931 });
michael@0 932
michael@0 933 /**
michael@0 934 * Checks that a download cannot be restarted after the "finalize" method.
michael@0 935 */
michael@0 936 add_task(function test_finalize()
michael@0 937 {
michael@0 938 mustInterruptResponses();
michael@0 939
michael@0 940 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 941
michael@0 942 let promiseFinalized = download.finalize();
michael@0 943
michael@0 944 try {
michael@0 945 yield download.start();
michael@0 946 do_throw("It should not be possible to restart after finalization.");
michael@0 947 } catch (ex) { }
michael@0 948
michael@0 949 yield promiseFinalized;
michael@0 950
michael@0 951 do_check_true(download.stopped);
michael@0 952 do_check_false(download.succeeded);
michael@0 953 do_check_true(download.canceled);
michael@0 954 do_check_true(download.error === null);
michael@0 955
michael@0 956 do_check_false(yield OS.File.exists(download.target.path));
michael@0 957 });
michael@0 958
michael@0 959 /**
michael@0 960 * Checks that the "finalize" method can remove partially downloaded data.
michael@0 961 */
michael@0 962 add_task(function test_finalize_tryToKeepPartialData()
michael@0 963 {
michael@0 964 // Check finalization without removing partial data.
michael@0 965 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 966 yield download.finalize();
michael@0 967
michael@0 968 do_check_true(download.hasPartialData);
michael@0 969 do_check_true(yield OS.File.exists(download.target.partFilePath));
michael@0 970
michael@0 971 // Clean up.
michael@0 972 yield download.removePartialData();
michael@0 973
michael@0 974 // Check finalization while removing partial data.
michael@0 975 download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 976 yield download.finalize(true);
michael@0 977
michael@0 978 do_check_false(download.hasPartialData);
michael@0 979 do_check_false(yield OS.File.exists(download.target.partFilePath));
michael@0 980 });
michael@0 981
michael@0 982 /**
michael@0 983 * Checks that whenSucceeded returns a promise that is resolved after a restart.
michael@0 984 */
michael@0 985 add_task(function test_whenSucceeded_after_restart()
michael@0 986 {
michael@0 987 mustInterruptResponses();
michael@0 988
michael@0 989 let promiseSucceeded;
michael@0 990
michael@0 991 let download;
michael@0 992 if (!gUseLegacySaver) {
michael@0 993 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 994 // we can verify getting a reference before the first download attempt.
michael@0 995 download = yield promiseNewDownload(httpUrl("interruptible.txt"));
michael@0 996 promiseSucceeded = download.whenSucceeded();
michael@0 997 download.start();
michael@0 998 } else {
michael@0 999 // When testing DownloadLegacySaver, the download is already started when it
michael@0 1000 // is created, thus we cannot get the reference before the first attempt.
michael@0 1001 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"));
michael@0 1002 promiseSucceeded = download.whenSucceeded();
michael@0 1003 }
michael@0 1004
michael@0 1005 // Cancel the first download attempt.
michael@0 1006 yield download.cancel();
michael@0 1007
michael@0 1008 // The second request is allowed to complete.
michael@0 1009 continueResponses();
michael@0 1010 download.start();
michael@0 1011
michael@0 1012 // Wait for the download to finish by waiting on the whenSucceeded promise.
michael@0 1013 yield promiseSucceeded;
michael@0 1014
michael@0 1015 do_check_true(download.stopped);
michael@0 1016 do_check_true(download.succeeded);
michael@0 1017 do_check_false(download.canceled);
michael@0 1018 do_check_true(download.error === null);
michael@0 1019
michael@0 1020 yield promiseVerifyContents(download.target.path,
michael@0 1021 TEST_DATA_SHORT + TEST_DATA_SHORT);
michael@0 1022 });
michael@0 1023
michael@0 1024 /**
michael@0 1025 * Ensures download error details are reported on network failures.
michael@0 1026 */
michael@0 1027 add_task(function test_error_source()
michael@0 1028 {
michael@0 1029 let serverSocket = startFakeServer();
michael@0 1030 try {
michael@0 1031 let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt";
michael@0 1032
michael@0 1033 let download;
michael@0 1034 try {
michael@0 1035 if (!gUseLegacySaver) {
michael@0 1036 // When testing DownloadCopySaver, we want to check that the promise
michael@0 1037 // returned by the "start" method is rejected.
michael@0 1038 download = yield promiseNewDownload(sourceUrl);
michael@0 1039
michael@0 1040 do_check_true(download.error === null);
michael@0 1041
michael@0 1042 yield download.start();
michael@0 1043 } else {
michael@0 1044 // When testing DownloadLegacySaver, we cannot be sure whether we are
michael@0 1045 // testing the promise returned by the "start" method or we are testing
michael@0 1046 // the "error" property checked by promiseDownloadStopped. This happens
michael@0 1047 // because we don't have control over when the download is started.
michael@0 1048 download = yield promiseStartLegacyDownload(sourceUrl);
michael@0 1049 yield promiseDownloadStopped(download);
michael@0 1050 }
michael@0 1051 do_throw("The download should have failed.");
michael@0 1052 } catch (ex if ex instanceof Downloads.Error && ex.becauseSourceFailed) {
michael@0 1053 // A specific error object is thrown when reading from the source fails.
michael@0 1054 }
michael@0 1055
michael@0 1056 // Check the properties now that the download stopped.
michael@0 1057 do_check_true(download.stopped);
michael@0 1058 do_check_false(download.canceled);
michael@0 1059 do_check_true(download.error !== null);
michael@0 1060 do_check_true(download.error.becauseSourceFailed);
michael@0 1061 do_check_false(download.error.becauseTargetFailed);
michael@0 1062
michael@0 1063 do_check_false(yield OS.File.exists(download.target.path));
michael@0 1064 } finally {
michael@0 1065 serverSocket.close();
michael@0 1066 }
michael@0 1067 });
michael@0 1068
michael@0 1069 /**
michael@0 1070 * Ensures download error details are reported on local writing failures.
michael@0 1071 */
michael@0 1072 add_task(function test_error_target()
michael@0 1073 {
michael@0 1074 // Create a file without write access permissions before downloading.
michael@0 1075 let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
michael@0 1076 targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0);
michael@0 1077 try {
michael@0 1078 let download;
michael@0 1079 try {
michael@0 1080 if (!gUseLegacySaver) {
michael@0 1081 // When testing DownloadCopySaver, we want to check that the promise
michael@0 1082 // returned by the "start" method is rejected.
michael@0 1083 download = yield Downloads.createDownload({
michael@0 1084 source: httpUrl("source.txt"),
michael@0 1085 target: targetFile,
michael@0 1086 });
michael@0 1087 yield download.start();
michael@0 1088 } else {
michael@0 1089 // When testing DownloadLegacySaver, we cannot be sure whether we are
michael@0 1090 // testing the promise returned by the "start" method or we are testing
michael@0 1091 // the "error" property checked by promiseDownloadStopped. This happens
michael@0 1092 // because we don't have control over when the download is started.
michael@0 1093 download = yield promiseStartLegacyDownload(null,
michael@0 1094 { targetFile: targetFile });
michael@0 1095 yield promiseDownloadStopped(download);
michael@0 1096 }
michael@0 1097 do_throw("The download should have failed.");
michael@0 1098 } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) {
michael@0 1099 // A specific error object is thrown when writing to the target fails.
michael@0 1100 }
michael@0 1101
michael@0 1102 // Check the properties now that the download stopped.
michael@0 1103 do_check_true(download.stopped);
michael@0 1104 do_check_false(download.canceled);
michael@0 1105 do_check_true(download.error !== null);
michael@0 1106 do_check_true(download.error.becauseTargetFailed);
michael@0 1107 do_check_false(download.error.becauseSourceFailed);
michael@0 1108 } finally {
michael@0 1109 // Restore the default permissions to allow deleting the file on Windows.
michael@0 1110 if (targetFile.exists()) {
michael@0 1111 targetFile.permissions = FileUtils.PERMS_FILE;
michael@0 1112 targetFile.remove(false);
michael@0 1113 }
michael@0 1114 }
michael@0 1115 });
michael@0 1116
michael@0 1117 /**
michael@0 1118 * Restarts a failed download.
michael@0 1119 */
michael@0 1120 add_task(function test_error_restart()
michael@0 1121 {
michael@0 1122 let download;
michael@0 1123
michael@0 1124 // Create a file without write access permissions before downloading.
michael@0 1125 let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
michael@0 1126 targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0);
michael@0 1127 try {
michael@0 1128 // Use DownloadCopySaver or DownloadLegacySaver based on the test run,
michael@0 1129 // specifying the target file we created.
michael@0 1130 if (!gUseLegacySaver) {
michael@0 1131 download = yield Downloads.createDownload({
michael@0 1132 source: httpUrl("source.txt"),
michael@0 1133 target: targetFile,
michael@0 1134 });
michael@0 1135 download.start();
michael@0 1136 } else {
michael@0 1137 download = yield promiseStartLegacyDownload(null,
michael@0 1138 { targetFile: targetFile });
michael@0 1139 }
michael@0 1140 yield promiseDownloadStopped(download);
michael@0 1141 do_throw("The download should have failed.");
michael@0 1142 } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) {
michael@0 1143 // A specific error object is thrown when writing to the target fails.
michael@0 1144 } finally {
michael@0 1145 // Restore the default permissions to allow deleting the file on Windows.
michael@0 1146 if (targetFile.exists()) {
michael@0 1147 targetFile.permissions = FileUtils.PERMS_FILE;
michael@0 1148
michael@0 1149 // Also for Windows, rename the file before deleting. This makes the
michael@0 1150 // current file name available immediately for a new file, while deleting
michael@0 1151 // in place prevents creation of a file with the same name for some time.
michael@0 1152 targetFile.moveTo(null, targetFile.leafName + ".delete.tmp");
michael@0 1153 targetFile.remove(false);
michael@0 1154 }
michael@0 1155 }
michael@0 1156
michael@0 1157 // Restart the download and wait for completion.
michael@0 1158 yield download.start();
michael@0 1159
michael@0 1160 do_check_true(download.stopped);
michael@0 1161 do_check_true(download.succeeded);
michael@0 1162 do_check_false(download.canceled);
michael@0 1163 do_check_true(download.error === null);
michael@0 1164 do_check_eq(download.progress, 100);
michael@0 1165
michael@0 1166 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 1167 });
michael@0 1168
michael@0 1169 /**
michael@0 1170 * Executes download in both public and private modes.
michael@0 1171 */
michael@0 1172 add_task(function test_public_and_private()
michael@0 1173 {
michael@0 1174 let sourcePath = "/test_public_and_private.txt";
michael@0 1175 let sourceUrl = httpUrl("test_public_and_private.txt");
michael@0 1176 let testCount = 0;
michael@0 1177
michael@0 1178 // Apply pref to allow all cookies.
michael@0 1179 Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
michael@0 1180
michael@0 1181 function cleanup() {
michael@0 1182 Services.prefs.clearUserPref("network.cookie.cookieBehavior");
michael@0 1183 Services.cookies.removeAll();
michael@0 1184 gHttpServer.registerPathHandler(sourcePath, null);
michael@0 1185 }
michael@0 1186 do_register_cleanup(cleanup);
michael@0 1187
michael@0 1188 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
michael@0 1189 aResponse.setHeader("Content-Type", "text/plain", false);
michael@0 1190
michael@0 1191 if (testCount == 0) {
michael@0 1192 // No cookies should exist for first public download.
michael@0 1193 do_check_false(aRequest.hasHeader("Cookie"));
michael@0 1194 aResponse.setHeader("Set-Cookie", "foobar=1", false);
michael@0 1195 testCount++;
michael@0 1196 } else if (testCount == 1) {
michael@0 1197 // The cookie should exists for second public download.
michael@0 1198 do_check_true(aRequest.hasHeader("Cookie"));
michael@0 1199 do_check_eq(aRequest.getHeader("Cookie"), "foobar=1");
michael@0 1200 testCount++;
michael@0 1201 } else if (testCount == 2) {
michael@0 1202 // No cookies should exist for first private download.
michael@0 1203 do_check_false(aRequest.hasHeader("Cookie"));
michael@0 1204 }
michael@0 1205 });
michael@0 1206
michael@0 1207 let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
michael@0 1208 yield Downloads.fetch(sourceUrl, targetFile);
michael@0 1209 yield Downloads.fetch(sourceUrl, targetFile);
michael@0 1210
michael@0 1211 if (!gUseLegacySaver) {
michael@0 1212 let download = yield Downloads.createDownload({
michael@0 1213 source: { url: sourceUrl, isPrivate: true },
michael@0 1214 target: targetFile,
michael@0 1215 });
michael@0 1216 yield download.start();
michael@0 1217 } else {
michael@0 1218 let download = yield promiseStartLegacyDownload(sourceUrl,
michael@0 1219 { isPrivate: true });
michael@0 1220 yield promiseDownloadStopped(download);
michael@0 1221 }
michael@0 1222
michael@0 1223 cleanup();
michael@0 1224 });
michael@0 1225
michael@0 1226 /**
michael@0 1227 * Checks the startTime gets updated even after a restart.
michael@0 1228 */
michael@0 1229 add_task(function test_cancel_immediately_restart_and_check_startTime()
michael@0 1230 {
michael@0 1231 let download = yield promiseStartDownload();
michael@0 1232
michael@0 1233 let startTime = download.startTime;
michael@0 1234 do_check_true(isValidDate(download.startTime));
michael@0 1235
michael@0 1236 yield download.cancel();
michael@0 1237 do_check_eq(download.startTime.getTime(), startTime.getTime());
michael@0 1238
michael@0 1239 // Wait for a timeout.
michael@0 1240 yield promiseTimeout(10);
michael@0 1241
michael@0 1242 yield download.start();
michael@0 1243 do_check_true(download.startTime.getTime() > startTime.getTime());
michael@0 1244 });
michael@0 1245
michael@0 1246 /**
michael@0 1247 * Executes download with content-encoding.
michael@0 1248 */
michael@0 1249 add_task(function test_with_content_encoding()
michael@0 1250 {
michael@0 1251 let sourcePath = "/test_with_content_encoding.txt";
michael@0 1252 let sourceUrl = httpUrl("test_with_content_encoding.txt");
michael@0 1253
michael@0 1254 function cleanup() {
michael@0 1255 gHttpServer.registerPathHandler(sourcePath, null);
michael@0 1256 }
michael@0 1257 do_register_cleanup(cleanup);
michael@0 1258
michael@0 1259 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
michael@0 1260 aResponse.setHeader("Content-Type", "text/plain", false);
michael@0 1261 aResponse.setHeader("Content-Encoding", "gzip", false);
michael@0 1262 aResponse.setHeader("Content-Length",
michael@0 1263 "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false);
michael@0 1264
michael@0 1265 let bos = new BinaryOutputStream(aResponse.bodyOutputStream);
michael@0 1266 bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED,
michael@0 1267 TEST_DATA_SHORT_GZIP_ENCODED.length);
michael@0 1268 });
michael@0 1269
michael@0 1270 let download = yield promiseStartDownload(sourceUrl);
michael@0 1271 yield promiseDownloadStopped(download);
michael@0 1272
michael@0 1273 do_check_eq(download.progress, 100);
michael@0 1274 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
michael@0 1275
michael@0 1276 // Ensure the content matches the decoded test data.
michael@0 1277 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 1278
michael@0 1279 cleanup();
michael@0 1280 });
michael@0 1281
michael@0 1282 /**
michael@0 1283 * Checks that the file is not decoded if the extension matches the encoding.
michael@0 1284 */
michael@0 1285 add_task(function test_with_content_encoding_ignore_extension()
michael@0 1286 {
michael@0 1287 let sourcePath = "/test_with_content_encoding_ignore_extension.gz";
michael@0 1288 let sourceUrl = httpUrl("test_with_content_encoding_ignore_extension.gz");
michael@0 1289
michael@0 1290 function cleanup() {
michael@0 1291 gHttpServer.registerPathHandler(sourcePath, null);
michael@0 1292 }
michael@0 1293 do_register_cleanup(cleanup);
michael@0 1294
michael@0 1295 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
michael@0 1296 aResponse.setHeader("Content-Type", "text/plain", false);
michael@0 1297 aResponse.setHeader("Content-Encoding", "gzip", false);
michael@0 1298 aResponse.setHeader("Content-Length",
michael@0 1299 "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false);
michael@0 1300
michael@0 1301 let bos = new BinaryOutputStream(aResponse.bodyOutputStream);
michael@0 1302 bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED,
michael@0 1303 TEST_DATA_SHORT_GZIP_ENCODED.length);
michael@0 1304 });
michael@0 1305
michael@0 1306 let download = yield promiseStartDownload(sourceUrl);
michael@0 1307 yield promiseDownloadStopped(download);
michael@0 1308
michael@0 1309 do_check_eq(download.progress, 100);
michael@0 1310 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
michael@0 1311
michael@0 1312 // Ensure the content matches the encoded test data. We convert the data to a
michael@0 1313 // string before executing the content check.
michael@0 1314 yield promiseVerifyContents(download.target.path,
michael@0 1315 String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED));
michael@0 1316
michael@0 1317 cleanup();
michael@0 1318 });
michael@0 1319
michael@0 1320 /**
michael@0 1321 * Cancels and restarts a download sequentially with content-encoding.
michael@0 1322 */
michael@0 1323 add_task(function test_cancel_midway_restart_with_content_encoding()
michael@0 1324 {
michael@0 1325 mustInterruptResponses();
michael@0 1326
michael@0 1327 let download = yield promiseStartDownload(httpUrl("interruptible_gzip.txt"));
michael@0 1328
michael@0 1329 // The first time, cancel the download midway.
michael@0 1330 let deferCancel = Promise.defer();
michael@0 1331 let onchange = function () {
michael@0 1332 if (!download.stopped && !download.canceled &&
michael@0 1333 download.currentBytes == TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length) {
michael@0 1334 deferCancel.resolve(download.cancel());
michael@0 1335 }
michael@0 1336 };
michael@0 1337
michael@0 1338 // Register for the notification, but also call the function directly in
michael@0 1339 // case the download already reached the expected progress.
michael@0 1340 download.onchange = onchange;
michael@0 1341 onchange();
michael@0 1342
michael@0 1343 yield deferCancel.promise;
michael@0 1344
michael@0 1345 do_check_true(download.stopped);
michael@0 1346
michael@0 1347 // The second time, we'll provide the entire interruptible response.
michael@0 1348 continueResponses();
michael@0 1349 download.onchange = null;
michael@0 1350 yield download.start();
michael@0 1351
michael@0 1352 do_check_eq(download.progress, 100);
michael@0 1353 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
michael@0 1354
michael@0 1355 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 1356 });
michael@0 1357
michael@0 1358 /**
michael@0 1359 * Download with parental controls enabled.
michael@0 1360 */
michael@0 1361 add_task(function test_blocked_parental_controls()
michael@0 1362 {
michael@0 1363 function cleanup() {
michael@0 1364 DownloadIntegration.shouldBlockInTest = false;
michael@0 1365 }
michael@0 1366 do_register_cleanup(cleanup);
michael@0 1367 DownloadIntegration.shouldBlockInTest = true;
michael@0 1368
michael@0 1369 let download;
michael@0 1370 try {
michael@0 1371 if (!gUseLegacySaver) {
michael@0 1372 // When testing DownloadCopySaver, we want to check that the promise
michael@0 1373 // returned by the "start" method is rejected.
michael@0 1374 download = yield promiseNewDownload();
michael@0 1375 yield download.start();
michael@0 1376 } else {
michael@0 1377 // When testing DownloadLegacySaver, we cannot be sure whether we are
michael@0 1378 // testing the promise returned by the "start" method or we are testing
michael@0 1379 // the "error" property checked by promiseDownloadStopped. This happens
michael@0 1380 // because we don't have control over when the download is started.
michael@0 1381 download = yield promiseStartLegacyDownload();
michael@0 1382 yield promiseDownloadStopped(download);
michael@0 1383 }
michael@0 1384 do_throw("The download should have blocked.");
michael@0 1385 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
michael@0 1386 do_check_true(ex.becauseBlockedByParentalControls);
michael@0 1387 do_check_true(download.error.becauseBlockedByParentalControls);
michael@0 1388 }
michael@0 1389
michael@0 1390 // Now that the download stopped, the target file should not exist.
michael@0 1391 do_check_false(yield OS.File.exists(download.target.path));
michael@0 1392
michael@0 1393 cleanup();
michael@0 1394 });
michael@0 1395
michael@0 1396 /**
michael@0 1397 * Test a download that will be blocked by Windows parental controls by
michael@0 1398 * resulting in an HTTP status code of 450.
michael@0 1399 */
michael@0 1400 add_task(function test_blocked_parental_controls_httpstatus450()
michael@0 1401 {
michael@0 1402 let download;
michael@0 1403 try {
michael@0 1404 if (!gUseLegacySaver) {
michael@0 1405 download = yield promiseNewDownload(httpUrl("parentalblocked.zip"));
michael@0 1406 yield download.start();
michael@0 1407 }
michael@0 1408 else {
michael@0 1409 download = yield promiseStartLegacyDownload(httpUrl("parentalblocked.zip"));
michael@0 1410 yield promiseDownloadStopped(download);
michael@0 1411 }
michael@0 1412 do_throw("The download should have blocked.");
michael@0 1413 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
michael@0 1414 do_check_true(ex.becauseBlockedByParentalControls);
michael@0 1415 do_check_true(download.error.becauseBlockedByParentalControls);
michael@0 1416 do_check_true(download.stopped);
michael@0 1417 }
michael@0 1418
michael@0 1419 do_check_false(yield OS.File.exists(download.target.path));
michael@0 1420 });
michael@0 1421
michael@0 1422 /**
michael@0 1423 * Check that DownloadCopySaver can always retrieve the hash.
michael@0 1424 * DownloadLegacySaver can only retrieve the hash when
michael@0 1425 * nsIExternalHelperAppService is invoked.
michael@0 1426 */
michael@0 1427 add_task(function test_getSha256Hash()
michael@0 1428 {
michael@0 1429 if (!gUseLegacySaver) {
michael@0 1430 let download = yield promiseStartDownload(httpUrl("source.txt"));
michael@0 1431 yield promiseDownloadStopped(download);
michael@0 1432 do_check_true(download.stopped);
michael@0 1433 do_check_eq(32, download.saver.getSha256Hash().length);
michael@0 1434 }
michael@0 1435 });
michael@0 1436
michael@0 1437 /**
michael@0 1438 * Checks that application reputation blocks the download and the target file
michael@0 1439 * does not exist.
michael@0 1440 */
michael@0 1441 add_task(function test_blocked_applicationReputation()
michael@0 1442 {
michael@0 1443 function cleanup() {
michael@0 1444 DownloadIntegration.shouldBlockInTestForApplicationReputation = false;
michael@0 1445 }
michael@0 1446 do_register_cleanup(cleanup);
michael@0 1447 DownloadIntegration.shouldBlockInTestForApplicationReputation = true;
michael@0 1448
michael@0 1449 let download;
michael@0 1450 try {
michael@0 1451 if (!gUseLegacySaver) {
michael@0 1452 // When testing DownloadCopySaver, we want to check that the promise
michael@0 1453 // returned by the "start" method is rejected.
michael@0 1454 download = yield promiseNewDownload();
michael@0 1455 yield download.start();
michael@0 1456 } else {
michael@0 1457 // When testing DownloadLegacySaver, we cannot be sure whether we are
michael@0 1458 // testing the promise returned by the "start" method or we are testing
michael@0 1459 // the "error" property checked by promiseDownloadStopped. This happens
michael@0 1460 // because we don't have control over when the download is started.
michael@0 1461 download = yield promiseStartLegacyDownload();
michael@0 1462 yield promiseDownloadStopped(download);
michael@0 1463 }
michael@0 1464 do_throw("The download should have blocked.");
michael@0 1465 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
michael@0 1466 do_check_true(ex.becauseBlockedByReputationCheck);
michael@0 1467 do_check_true(download.error.becauseBlockedByReputationCheck);
michael@0 1468 }
michael@0 1469
michael@0 1470 // Now that the download is blocked, the target file should not exist.
michael@0 1471 do_check_false(yield OS.File.exists(download.target.path));
michael@0 1472 cleanup();
michael@0 1473 });
michael@0 1474
michael@0 1475 /**
michael@0 1476 * download.showContainingDirectory() action
michael@0 1477 */
michael@0 1478 add_task(function test_showContainingDirectory() {
michael@0 1479 DownloadIntegration._deferTestShowDir = Promise.defer();
michael@0 1480
michael@0 1481 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 1482
michael@0 1483 let download = yield Downloads.createDownload({
michael@0 1484 source: { url: httpUrl("source.txt") },
michael@0 1485 target: ""
michael@0 1486 });
michael@0 1487
michael@0 1488 try {
michael@0 1489 yield download.showContainingDirectory();
michael@0 1490 do_throw("Should have failed because of an invalid path.");
michael@0 1491 } catch (ex if ex instanceof Components.Exception) {
michael@0 1492 // Invalid paths on Windows are reported with NS_ERROR_FAILURE,
michael@0 1493 // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux
michael@0 1494 let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH ||
michael@0 1495 ex.result == Cr.NS_ERROR_FAILURE;
michael@0 1496 do_check_true(validResult);
michael@0 1497 }
michael@0 1498
michael@0 1499 download = yield Downloads.createDownload({
michael@0 1500 source: { url: httpUrl("source.txt") },
michael@0 1501 target: targetPath
michael@0 1502 });
michael@0 1503
michael@0 1504
michael@0 1505 DownloadIntegration._deferTestShowDir = Promise.defer();
michael@0 1506 download.showContainingDirectory();
michael@0 1507 let result = yield DownloadIntegration._deferTestShowDir.promise;
michael@0 1508 do_check_eq(result, "success");
michael@0 1509 });
michael@0 1510
michael@0 1511 /**
michael@0 1512 * download.launch() action
michael@0 1513 */
michael@0 1514 add_task(function test_launch() {
michael@0 1515 let customLauncher = getTempFile("app-launcher");
michael@0 1516
michael@0 1517 // Test both with and without setting a custom application.
michael@0 1518 for (let launcherPath of [null, customLauncher.path]) {
michael@0 1519 let download;
michael@0 1520 if (!gUseLegacySaver) {
michael@0 1521 // When testing DownloadCopySaver, we have control over the download, thus
michael@0 1522 // we can test that file is not launched if download.succeeded is not set.
michael@0 1523 download = yield Downloads.createDownload({
michael@0 1524 source: httpUrl("source.txt"),
michael@0 1525 target: getTempFile(TEST_TARGET_FILE_NAME).path,
michael@0 1526 launcherPath: launcherPath,
michael@0 1527 launchWhenSucceeded: true
michael@0 1528 });
michael@0 1529
michael@0 1530 try {
michael@0 1531 yield download.launch();
michael@0 1532 do_throw("Can't launch download file as it has not completed yet");
michael@0 1533 } catch (ex) {
michael@0 1534 do_check_eq(ex.message,
michael@0 1535 "launch can only be called if the download succeeded");
michael@0 1536 }
michael@0 1537
michael@0 1538 yield download.start();
michael@0 1539 } else {
michael@0 1540 // When testing DownloadLegacySaver, the download is already started when
michael@0 1541 // it is created, thus we don't test calling "launch" before starting.
michael@0 1542 download = yield promiseStartLegacyDownload(
michael@0 1543 httpUrl("source.txt"),
michael@0 1544 { launcherPath: launcherPath,
michael@0 1545 launchWhenSucceeded: true });
michael@0 1546 yield promiseDownloadStopped(download);
michael@0 1547 }
michael@0 1548
michael@0 1549 do_check_true(download.launchWhenSucceeded);
michael@0 1550
michael@0 1551 DownloadIntegration._deferTestOpenFile = Promise.defer();
michael@0 1552 download.launch();
michael@0 1553 let result = yield DownloadIntegration._deferTestOpenFile.promise;
michael@0 1554
michael@0 1555 // Verify that the results match the test case.
michael@0 1556 if (!launcherPath) {
michael@0 1557 // This indicates that the default handler has been chosen.
michael@0 1558 do_check_true(result === null);
michael@0 1559 } else {
michael@0 1560 // Check the nsIMIMEInfo instance that would have been used for launching.
michael@0 1561 do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp);
michael@0 1562 do_check_true(result.preferredApplicationHandler
michael@0 1563 .QueryInterface(Ci.nsILocalHandlerApp)
michael@0 1564 .executable.equals(customLauncher));
michael@0 1565 }
michael@0 1566 }
michael@0 1567 });
michael@0 1568
michael@0 1569 /**
michael@0 1570 * Test passing an invalid path as the launcherPath property.
michael@0 1571 */
michael@0 1572 add_task(function test_launcherPath_invalid() {
michael@0 1573 let download = yield Downloads.createDownload({
michael@0 1574 source: { url: httpUrl("source.txt") },
michael@0 1575 target: { path: getTempFile(TEST_TARGET_FILE_NAME).path },
michael@0 1576 launcherPath: " "
michael@0 1577 });
michael@0 1578
michael@0 1579 DownloadIntegration._deferTestOpenFile = Promise.defer();
michael@0 1580 yield download.start();
michael@0 1581 try {
michael@0 1582 download.launch();
michael@0 1583 result = yield DownloadIntegration._deferTestOpenFile.promise;
michael@0 1584 do_throw("Can't launch file with invalid custom launcher")
michael@0 1585 } catch (ex if ex instanceof Components.Exception) {
michael@0 1586 // Invalid paths on Windows are reported with NS_ERROR_FAILURE,
michael@0 1587 // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux
michael@0 1588 let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH ||
michael@0 1589 ex.result == Cr.NS_ERROR_FAILURE;
michael@0 1590 do_check_true(validResult);
michael@0 1591 }
michael@0 1592 });
michael@0 1593
michael@0 1594 /**
michael@0 1595 * Tests that download.launch() is automatically called after
michael@0 1596 * the download finishes if download.launchWhenSucceeded = true
michael@0 1597 */
michael@0 1598 add_task(function test_launchWhenSucceeded() {
michael@0 1599 let customLauncher = getTempFile("app-launcher");
michael@0 1600
michael@0 1601 // Test both with and without setting a custom application.
michael@0 1602 for (let launcherPath of [null, customLauncher.path]) {
michael@0 1603 DownloadIntegration._deferTestOpenFile = Promise.defer();
michael@0 1604
michael@0 1605 if (!gUseLegacySaver) {
michael@0 1606 let download = yield Downloads.createDownload({
michael@0 1607 source: httpUrl("source.txt"),
michael@0 1608 target: getTempFile(TEST_TARGET_FILE_NAME).path,
michael@0 1609 launchWhenSucceeded: true,
michael@0 1610 launcherPath: launcherPath,
michael@0 1611 });
michael@0 1612 yield download.start();
michael@0 1613 } else {
michael@0 1614 let download = yield promiseStartLegacyDownload(
michael@0 1615 httpUrl("source.txt"),
michael@0 1616 { launcherPath: launcherPath,
michael@0 1617 launchWhenSucceeded: true });
michael@0 1618 yield promiseDownloadStopped(download);
michael@0 1619 }
michael@0 1620
michael@0 1621 let result = yield DownloadIntegration._deferTestOpenFile.promise;
michael@0 1622
michael@0 1623 // Verify that the results match the test case.
michael@0 1624 if (!launcherPath) {
michael@0 1625 // This indicates that the default handler has been chosen.
michael@0 1626 do_check_true(result === null);
michael@0 1627 } else {
michael@0 1628 // Check the nsIMIMEInfo instance that would have been used for launching.
michael@0 1629 do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp);
michael@0 1630 do_check_true(result.preferredApplicationHandler
michael@0 1631 .QueryInterface(Ci.nsILocalHandlerApp)
michael@0 1632 .executable.equals(customLauncher));
michael@0 1633 }
michael@0 1634 }
michael@0 1635 });
michael@0 1636
michael@0 1637 /**
michael@0 1638 * Tests that the proper content type is set for a normal download.
michael@0 1639 */
michael@0 1640 add_task(function test_contentType() {
michael@0 1641 let download = yield promiseStartDownload(httpUrl("source.txt"));
michael@0 1642 yield promiseDownloadStopped(download);
michael@0 1643
michael@0 1644 do_check_eq("text/plain", download.contentType);
michael@0 1645 });
michael@0 1646
michael@0 1647 /**
michael@0 1648 * Tests that the serialization/deserialization of the startTime Date
michael@0 1649 * object works correctly.
michael@0 1650 */
michael@0 1651 add_task(function test_toSerializable_startTime()
michael@0 1652 {
michael@0 1653 let download1 = yield promiseStartDownload(httpUrl("source.txt"));
michael@0 1654 yield promiseDownloadStopped(download1);
michael@0 1655
michael@0 1656 let serializable = download1.toSerializable();
michael@0 1657 let reserialized = JSON.parse(JSON.stringify(serializable));
michael@0 1658
michael@0 1659 let download2 = yield Downloads.createDownload(reserialized);
michael@0 1660
michael@0 1661 do_check_eq(download1.startTime.constructor.name, "Date");
michael@0 1662 do_check_eq(download2.startTime.constructor.name, "Date");
michael@0 1663 do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON());
michael@0 1664 });
michael@0 1665
michael@0 1666 /**
michael@0 1667 * This test will call the platform specific operations within
michael@0 1668 * DownloadPlatform::DownloadDone. While there is no test to verify the
michael@0 1669 * specific behaviours, this at least ensures that there is no error or crash.
michael@0 1670 */
michael@0 1671 add_task(function test_platform_integration()
michael@0 1672 {
michael@0 1673 let downloadFiles = [];
michael@0 1674 function cleanup() {
michael@0 1675 for (let file of downloadFiles) {
michael@0 1676 file.remove(true);
michael@0 1677 }
michael@0 1678 }
michael@0 1679 do_register_cleanup(cleanup);
michael@0 1680
michael@0 1681 for (let isPrivate of [false, true]) {
michael@0 1682 DownloadIntegration.downloadDoneCalled = false;
michael@0 1683
michael@0 1684 // Some platform specific operations only operate on files outside the
michael@0 1685 // temporary directory or in the Downloads directory (such as setting
michael@0 1686 // the Windows searchable attribute, and the Mac Downloads icon bouncing),
michael@0 1687 // so use the system Downloads directory for the target file.
michael@0 1688 let targetFilePath = yield DownloadIntegration.getSystemDownloadsDirectory();
michael@0 1689 targetFilePath = OS.Path.join(targetFilePath,
michael@0 1690 "test" + (Math.floor(Math.random() * 1000000)));
michael@0 1691 let targetFile = new FileUtils.File(targetFilePath);
michael@0 1692 downloadFiles.push(targetFile);
michael@0 1693
michael@0 1694 let download;
michael@0 1695 if (gUseLegacySaver) {
michael@0 1696 download = yield promiseStartLegacyDownload(httpUrl("source.txt"),
michael@0 1697 { targetFile: targetFile });
michael@0 1698 }
michael@0 1699 else {
michael@0 1700 download = yield Downloads.createDownload({
michael@0 1701 source: httpUrl("source.txt"),
michael@0 1702 target: targetFile,
michael@0 1703 });
michael@0 1704 download.start();
michael@0 1705 }
michael@0 1706
michael@0 1707 // Wait for the whenSucceeded promise to be resolved first.
michael@0 1708 // downloadDone should be called before the whenSucceeded promise is resolved.
michael@0 1709 yield download.whenSucceeded().then(function () {
michael@0 1710 do_check_true(DownloadIntegration.downloadDoneCalled);
michael@0 1711 });
michael@0 1712
michael@0 1713 // Then, wait for the promise returned by "start" to be resolved.
michael@0 1714 yield promiseDownloadStopped(download);
michael@0 1715
michael@0 1716 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
michael@0 1717 }
michael@0 1718 });
michael@0 1719
michael@0 1720 /**
michael@0 1721 * Checks that downloads are added to browsing history when they start.
michael@0 1722 */
michael@0 1723 add_task(function test_history()
michael@0 1724 {
michael@0 1725 mustInterruptResponses();
michael@0 1726
michael@0 1727 // We will wait for the visit to be notified during the download.
michael@0 1728 yield promiseClearHistory();
michael@0 1729 let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt"));
michael@0 1730
michael@0 1731 // Start a download that is not allowed to finish yet.
michael@0 1732 let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
michael@0 1733
michael@0 1734 // The history notifications should be received before the download completes.
michael@0 1735 let [time, transitionType] = yield promiseVisit;
michael@0 1736 do_check_eq(time, download.startTime.getTime() * 1000);
michael@0 1737 do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
michael@0 1738
michael@0 1739 // Restart and complete the download after clearing history.
michael@0 1740 yield promiseClearHistory();
michael@0 1741 download.cancel();
michael@0 1742 continueResponses();
michael@0 1743 yield download.start();
michael@0 1744
michael@0 1745 // The restart should not have added a new history visit.
michael@0 1746 do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt")));
michael@0 1747 });
michael@0 1748
michael@0 1749 /**
michael@0 1750 * Checks that downloads started by nsIHelperAppService are added to the
michael@0 1751 * browsing history when they start.
michael@0 1752 */
michael@0 1753 add_task(function test_history_tryToKeepPartialData()
michael@0 1754 {
michael@0 1755 // We will wait for the visit to be notified during the download.
michael@0 1756 yield promiseClearHistory();
michael@0 1757 let promiseVisit =
michael@0 1758 promiseWaitForVisit(httpUrl("interruptible_resumable.txt"));
michael@0 1759
michael@0 1760 // Start a download that is not allowed to finish yet.
michael@0 1761 let beforeStartTimeMs = Date.now();
michael@0 1762 let download = yield promiseStartDownload_tryToKeepPartialData();
michael@0 1763
michael@0 1764 // The history notifications should be received before the download completes.
michael@0 1765 let [time, transitionType] = yield promiseVisit;
michael@0 1766 do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
michael@0 1767
michael@0 1768 // The time set by nsIHelperAppService may be different than the start time in
michael@0 1769 // the download object, thus we only check that it is a meaningful time. Note
michael@0 1770 // that we subtract one second from the earliest time to account for rounding.
michael@0 1771 do_check_true(time >= beforeStartTimeMs * 1000 - 1000000);
michael@0 1772
michael@0 1773 // Complete the download before finishing the test.
michael@0 1774 continueResponses();
michael@0 1775 yield promiseDownloadStopped(download);
michael@0 1776 });
michael@0 1777
michael@0 1778 /**
michael@0 1779 * Tests that the temp download files are removed on exit and exiting private
michael@0 1780 * mode after they have been launched.
michael@0 1781 */
michael@0 1782 add_task(function test_launchWhenSucceeded_deleteTempFileOnExit() {
michael@0 1783 const kDeleteTempFileOnExit = "browser.helperApps.deleteTempFileOnExit";
michael@0 1784
michael@0 1785 let customLauncherPath = getTempFile("app-launcher").path;
michael@0 1786 let autoDeleteTargetPathOne = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 1787 let autoDeleteTargetPathTwo = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 1788 let noAutoDeleteTargetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
michael@0 1789
michael@0 1790 let autoDeleteDownloadOne = yield Downloads.createDownload({
michael@0 1791 source: { url: httpUrl("source.txt"), isPrivate: true },
michael@0 1792 target: autoDeleteTargetPathOne,
michael@0 1793 launchWhenSucceeded: true,
michael@0 1794 launcherPath: customLauncherPath,
michael@0 1795 });
michael@0 1796 yield autoDeleteDownloadOne.start();
michael@0 1797
michael@0 1798 Services.prefs.setBoolPref(kDeleteTempFileOnExit, true);
michael@0 1799 let autoDeleteDownloadTwo = yield Downloads.createDownload({
michael@0 1800 source: httpUrl("source.txt"),
michael@0 1801 target: autoDeleteTargetPathTwo,
michael@0 1802 launchWhenSucceeded: true,
michael@0 1803 launcherPath: customLauncherPath,
michael@0 1804 });
michael@0 1805 yield autoDeleteDownloadTwo.start();
michael@0 1806
michael@0 1807 Services.prefs.setBoolPref(kDeleteTempFileOnExit, false);
michael@0 1808 let noAutoDeleteDownload = yield Downloads.createDownload({
michael@0 1809 source: httpUrl("source.txt"),
michael@0 1810 target: noAutoDeleteTargetPath,
michael@0 1811 launchWhenSucceeded: true,
michael@0 1812 launcherPath: customLauncherPath,
michael@0 1813 });
michael@0 1814 yield noAutoDeleteDownload.start();
michael@0 1815
michael@0 1816 Services.prefs.clearUserPref(kDeleteTempFileOnExit);
michael@0 1817
michael@0 1818 do_check_true(yield OS.File.exists(autoDeleteTargetPathOne));
michael@0 1819 do_check_true(yield OS.File.exists(autoDeleteTargetPathTwo));
michael@0 1820 do_check_true(yield OS.File.exists(noAutoDeleteTargetPath));
michael@0 1821
michael@0 1822 // Simulate leaving private browsing mode
michael@0 1823 Services.obs.notifyObservers(null, "last-pb-context-exited", null);
michael@0 1824 do_check_false(yield OS.File.exists(autoDeleteTargetPathOne));
michael@0 1825
michael@0 1826 // Simulate browser shutdown
michael@0 1827 let expire = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
michael@0 1828 .getService(Ci.nsIObserver);
michael@0 1829 expire.observe(null, "profile-before-change", null);
michael@0 1830 do_check_false(yield OS.File.exists(autoDeleteTargetPathTwo));
michael@0 1831 do_check_true(yield OS.File.exists(noAutoDeleteTargetPath));
michael@0 1832 });

mercurial