diff options
Diffstat (limited to 'toolkit/components/jsdownloads')
21 files changed, 0 insertions, 5861 deletions
diff --git a/toolkit/components/jsdownloads/moz.build b/toolkit/components/jsdownloads/moz.build index 62f08b160..56566bc97 100644 --- a/toolkit/components/jsdownloads/moz.build +++ b/toolkit/components/jsdownloads/moz.build @@ -4,15 +4,4 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -with Files('*'): - BUG_COMPONENT = ('Toolkit', 'Download Manager') - DIRS += ['public', 'src'] - -XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] -BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] - -TEST_HARNESS_FILES.xpcshell.toolkit.components.jsdownloads.test.data += [ - 'test/data/empty.txt', - 'test/data/source.txt', -] diff --git a/toolkit/components/jsdownloads/test/browser/.eslintrc.js b/toolkit/components/jsdownloads/test/browser/.eslintrc.js deleted file mode 100644 index 7c8021192..000000000 --- a/toolkit/components/jsdownloads/test/browser/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - "extends": [ - "../../../../../testing/mochitest/browser.eslintrc.js" - ] -}; diff --git a/toolkit/components/jsdownloads/test/browser/browser.ini b/toolkit/components/jsdownloads/test/browser/browser.ini deleted file mode 100644 index 131fc4ec8..000000000 --- a/toolkit/components/jsdownloads/test/browser/browser.ini +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -support-files = - head.js - testFile.html - -[browser_DownloadPDFSaver.js] -skip-if = os != "win" diff --git a/toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js b/toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js deleted file mode 100644 index 80ed9665a..000000000 --- a/toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js +++ /dev/null @@ -1,97 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the PDF download saver, and tests using a window as a - * source for the copy download saver. - */ - -"use strict"; - -/** - * Helper function to make sure a window reference exists on the download source. - */ -function* test_download_windowRef(aTab, aDownload) { - ok(aDownload.source.windowRef, "Download source had a window reference"); - ok(aDownload.source.windowRef instanceof Ci.xpcIJSWeakReference, "Download window reference is a weak ref"); - is(aDownload.source.windowRef.get(), aTab.linkedBrowser.contentWindow, "Download window exists during test"); -} - -/** - * Helper function to check the state of a completed download. - */ -function* test_download_state_complete(aTab, aDownload, aPrivate, aCanceled) { - ok(aDownload.source, "Download has a source"); - is(aDownload.source.url, aTab.linkedBrowser.contentWindow.location, "Download source has correct url"); - is(aDownload.source.isPrivate, aPrivate, "Download source has correct private state"); - ok(aDownload.stopped, "Download is stopped"); - is(aCanceled, aDownload.canceled, "Download has correct canceled state"); - is(!aCanceled, aDownload.succeeded, "Download has correct succeeded state"); - is(aDownload.error, null, "Download error is not defined"); -} - -function* test_createDownload_common(aPrivate, aType) { - let win = yield BrowserTestUtils.openNewBrowserWindow({ private : aPrivate}); - - let tab = yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, getRootDirectory(gTestPath) + "testFile.html"); - let download = yield Downloads.createDownload({ - source: tab.linkedBrowser.contentWindow, - target: { path: getTempFile(TEST_TARGET_FILE_NAME_PDF).path }, - saver: { type: aType } - }); - - yield test_download_windowRef(tab, download); - yield download.start(); - - yield test_download_state_complete(tab, download, aPrivate, false); - if (aType == "pdf") { - let signature = yield OS.File.read(download.target.path, - { bytes: 4, encoding: "us-ascii" }); - is(signature, "%PDF", "File exists and signature matches"); - } else { - ok((yield OS.File.exists(download.target.path)), "File exists"); - } - - win.gBrowser.removeTab(tab); - win.close() -} - -add_task(function* test_createDownload_pdf_private() { - yield test_createDownload_common(true, "pdf"); -}); -add_task(function* test_createDownload_pdf_not_private() { - yield test_createDownload_common(false, "pdf"); -}); - -// Even for the copy saver, using a window should produce valid results -add_task(function* test_createDownload_copy_private() { - yield test_createDownload_common(true, "copy"); -}); -add_task(function* test_createDownload_copy_not_private() { - yield test_createDownload_common(false, "copy"); -}); - -add_task(function* test_cancel_pdf_download() { - let tab = gBrowser.addTab(getRootDirectory(gTestPath) + "testFile.html"); - yield promiseBrowserLoaded(tab.linkedBrowser); - - let download = yield Downloads.createDownload({ - source: tab.linkedBrowser.contentWindow, - target: { path: getTempFile(TEST_TARGET_FILE_NAME_PDF).path }, - saver: "pdf", - }); - - yield test_download_windowRef(tab, download); - download.start().catch(() => {}); - - // Immediately cancel the download to test that it is erased correctly. - yield download.cancel(); - yield test_download_state_complete(tab, download, false, true); - - let exists = yield OS.File.exists(download.target.path) - ok(!exists, "Target file does not exist"); - - gBrowser.removeTab(tab); -}); diff --git a/toolkit/components/jsdownloads/test/browser/head.js b/toolkit/components/jsdownloads/test/browser/head.js deleted file mode 100644 index 769aaacb3..000000000 --- a/toolkit/components/jsdownloads/test/browser/head.js +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Provides infrastructure for automated download components tests. - */ - -"use strict"; - -// Globals - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; -var Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths", - "resource://gre/modules/DownloadPaths.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Downloads", - "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "HttpServer", - "resource://testing-common/httpd.js"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); - -const TEST_TARGET_FILE_NAME_PDF = "test-download.pdf"; - -// Support functions - -// While the previous test file should have deleted all the temporary files it -// used, on Windows these might still be pending deletion on the physical file -// system. Thus, start from a new base number every time, to make a collision -// with a file that is still pending deletion highly unlikely. -var gFileCounter = Math.floor(Math.random() * 1000000); - -/** - * Returns a reference to a temporary file, that is guaranteed not to exist, and - * to have never been created before. - * - * @param aLeafName - * Suggested leaf name for the file to be created. - * - * @return nsIFile pointing to a non-existent file in a temporary directory. - * - * @note It is not enough to delete the file if it exists, or to delete the file - * after calling nsIFile.createUnique, because on Windows the delete - * operation in the file system may still be pending, preventing a new - * file with the same name to be created. - */ -function getTempFile(aLeafName) -{ - // Prepend a serial number to the extension in the suggested leaf name. - let [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName); - let leafName = base + "-" + gFileCounter + ext; - gFileCounter++; - - // Get a file reference under the temporary directory for this test file. - let file = FileUtils.getFile("TmpD", [leafName]); - ok(!file.exists(), "Temp file does not exist"); - - registerCleanupFunction(function () { - if (file.exists()) { - file.remove(false); - } - }); - - return file; -} - -function promiseBrowserLoaded(browser) { - return new Promise(resolve => { - browser.addEventListener("load", function onLoad(event) { - if (event.target == browser.contentDocument) { - browser.removeEventListener("load", onLoad, true); - resolve(); - } - }, true); - }); -} diff --git a/toolkit/components/jsdownloads/test/browser/testFile.html b/toolkit/components/jsdownloads/test/browser/testFile.html deleted file mode 100644 index ee413514b..000000000 --- a/toolkit/components/jsdownloads/test/browser/testFile.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Test Save as PDF</title> - </head> - <body> - <p>Save me as a PDF!</p> - </body> -</html> diff --git a/toolkit/components/jsdownloads/test/data/.eslintrc.js b/toolkit/components/jsdownloads/test/data/.eslintrc.js deleted file mode 100644 index d35787cd2..000000000 --- a/toolkit/components/jsdownloads/test/data/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - "extends": [ - "../../../../../testing/xpcshell/xpcshell.eslintrc.js" - ] -}; diff --git a/toolkit/components/jsdownloads/test/data/empty.txt b/toolkit/components/jsdownloads/test/data/empty.txt deleted file mode 100644 index e69de29bb..000000000 --- a/toolkit/components/jsdownloads/test/data/empty.txt +++ /dev/null diff --git a/toolkit/components/jsdownloads/test/data/source.txt b/toolkit/components/jsdownloads/test/data/source.txt deleted file mode 100644 index 2156cb8c0..000000000 --- a/toolkit/components/jsdownloads/test/data/source.txt +++ /dev/null @@ -1 +0,0 @@ -This test string is downloaded.
\ No newline at end of file diff --git a/toolkit/components/jsdownloads/test/unit/.eslintrc.js b/toolkit/components/jsdownloads/test/unit/.eslintrc.js deleted file mode 100644 index d35787cd2..000000000 --- a/toolkit/components/jsdownloads/test/unit/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - "extends": [ - "../../../../../testing/xpcshell/xpcshell.eslintrc.js" - ] -}; diff --git a/toolkit/components/jsdownloads/test/unit/common_test_Download.js b/toolkit/components/jsdownloads/test/unit/common_test_Download.js deleted file mode 100644 index 42d4c5682..000000000 --- a/toolkit/components/jsdownloads/test/unit/common_test_Download.js +++ /dev/null @@ -1,2432 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * This script is loaded by "test_DownloadCore.js" and "test_DownloadLegacy.js" - * with different values of the gUseLegacySaver variable, to apply tests to both - * the "copy" and "legacy" saver implementations. - */ - -"use strict"; - -// Globals - -const kDeleteTempFileOnExit = "browser.helperApps.deleteTempFileOnExit"; - -/** - * Creates and starts a new download, using either DownloadCopySaver or - * DownloadLegacySaver based on the current test run. - * - * @return {Promise} - * @resolves The newly created Download object. The download may be in progress - * or already finished. The promiseDownloadStopped function can be - * used to wait for completion. - * @rejects JavaScript exception. - */ -function promiseStartDownload(aSourceUrl) { - if (gUseLegacySaver) { - return promiseStartLegacyDownload(aSourceUrl); - } - - return promiseNewDownload(aSourceUrl).then(download => { - download.start().catch(() => {}); - return download; - }); -} - -/** - * Creates and starts a new download, configured to keep partial data, and - * returns only when the first part of "interruptible_resumable.txt" has been - * saved to disk. You must call "continueResponses" to allow the interruptible - * request to continue. - * - * This function uses either DownloadCopySaver or DownloadLegacySaver based on - * the current test run. - * - * @return {Promise} - * @resolves The newly created Download object, still in progress. - * @rejects JavaScript exception. - */ -function promiseStartDownload_tryToKeepPartialData() { - return Task.spawn(function* () { - mustInterruptResponses(); - - // Start a new download and configure it to keep partially downloaded data. - let download; - if (!gUseLegacySaver) { - let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; - download = yield Downloads.createDownload({ - source: httpUrl("interruptible_resumable.txt"), - target: { path: targetFilePath, - partFilePath: targetFilePath + ".part" }, - }); - download.tryToKeepPartialData = true; - download.start().catch(() => {}); - } else { - // Start a download using nsIExternalHelperAppService, that is configured - // to keep partially downloaded data by default. - download = yield promiseStartExternalHelperAppServiceDownload(); - } - - yield promiseDownloadMidway(download); - yield promisePartFileReady(download); - - return download; - }); -} - -/** - * This function should be called after the progress notification for a download - * is received, and waits for the worker thread of BackgroundFileSaver to - * receive the data to be written to the ".part" file on disk. - * - * @return {Promise} - * @resolves When the ".part" file has been written to disk. - * @rejects JavaScript exception. - */ -function promisePartFileReady(aDownload) { - return Task.spawn(function* () { - // We don't have control over the file output code in BackgroundFileSaver. - // After we receive the download progress notification, we may only check - // that the ".part" file has been created, while its size cannot be - // determined because the file is currently open. - try { - do { - yield promiseTimeout(50); - } while (!(yield OS.File.exists(aDownload.target.partFilePath))); - } catch (ex) { - if (!(ex instanceof OS.File.Error)) { - throw ex; - } - // This indicates that the file has been created and cannot be accessed. - // The specific error might vary with the platform. - do_print("Expected exception while checking existence: " + ex.toString()); - // Wait some more time to allow the write to complete. - yield promiseTimeout(100); - } - }); -} - -/** - * Checks that the actual data written to disk matches the expected data as well - * as the properties of the given DownloadTarget object. - * - * @param downloadTarget - * The DownloadTarget object whose details have to be verified. - * @param expectedContents - * String containing the octets that are expected in the file. - * - * @return {Promise} - * @resolves When the properties have been verified. - * @rejects JavaScript exception. - */ -var promiseVerifyTarget = Task.async(function* (downloadTarget, - expectedContents) { - yield promiseVerifyContents(downloadTarget.path, expectedContents); - do_check_true(downloadTarget.exists); - do_check_eq(downloadTarget.size, expectedContents.length); -}); - -/** - * Waits for an attempt to launch a file, and returns the nsIMIMEInfo used for - * the launch, or null if the file was launched with the default handler. - */ -function waitForFileLaunched() { - return new Promise(resolve => { - let waitFn = base => ({ - launchFile(file, mimeInfo) { - Integration.downloads.unregister(waitFn); - if (!mimeInfo || - mimeInfo.preferredAction == Ci.nsIMIMEInfo.useSystemDefault) { - resolve(null); - } else { - resolve(mimeInfo); - } - return Promise.resolve(); - }, - }); - Integration.downloads.register(waitFn); - }); -} - -/** - * Waits for an attempt to show the directory where a file is located, and - * returns the path of the file. - */ -function waitForDirectoryShown() { - return new Promise(resolve => { - let waitFn = base => ({ - showContainingDirectory(path) { - Integration.downloads.unregister(waitFn); - resolve(path); - return Promise.resolve(); - }, - }); - Integration.downloads.register(waitFn); - }); -} - -// Tests - -/** - * Executes a download and checks its basic properties after construction. - * The download is started by constructing the simplest Download object with - * the "copy" saver, or using the legacy nsITransfer interface. - */ -add_task(function* test_basic() -{ - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can check its basic properties before it starts. - download = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt") }, - target: { path: targetFile.path }, - saver: { type: "copy" }, - }); - - do_check_eq(download.source.url, httpUrl("source.txt")); - do_check_eq(download.target.path, targetFile.path); - - yield download.start(); - } else { - // When testing DownloadLegacySaver, the download is already started when it - // is created, thus we must check its basic properties while in progress. - download = yield promiseStartLegacyDownload(null, - { targetFile: targetFile }); - - do_check_eq(download.source.url, httpUrl("source.txt")); - do_check_eq(download.target.path, targetFile.path); - - yield promiseDownloadStopped(download); - } - - // Check additional properties on the finished download. - do_check_true(download.source.referrer === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); -}); - -/** - * Executes a download with the tryToKeepPartialData property set, and ensures - * that the file is saved correctly. When testing DownloadLegacySaver, the - * download is executed using the nsIExternalHelperAppService component. - */ -add_task(function* test_basic_tryToKeepPartialData() -{ - let download = yield promiseStartDownload_tryToKeepPartialData(); - continueResponses(); - yield promiseDownloadStopped(download); - - // The target file should now have been created, and the ".part" file deleted. - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_eq(32, download.saver.getSha256Hash().length); -}); - -/** - * Tests the permissions of the final target file once the download finished. - */ -add_task(function* test_unix_permissions() -{ - // This test is only executed on some Desktop systems. - if (Services.appinfo.OS != "Darwin" && Services.appinfo.OS != "Linux" && - Services.appinfo.OS != "WINNT") { - do_print("Skipping test."); - return; - } - - let launcherPath = getTempFile("app-launcher").path; - - for (let autoDelete of [false, true]) { - for (let isPrivate of [false, true]) { - for (let launchWhenSucceeded of [false, true]) { - do_print("Checking " + JSON.stringify({ autoDelete, - isPrivate, - launchWhenSucceeded })); - - Services.prefs.setBoolPref(kDeleteTempFileOnExit, autoDelete); - - let download; - if (!gUseLegacySaver) { - download = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt"), isPrivate }, - target: getTempFile(TEST_TARGET_FILE_NAME).path, - launchWhenSucceeded, - launcherPath, - }); - yield download.start(); - } else { - download = yield promiseStartLegacyDownload(httpUrl("source.txt"), { - isPrivate, - launchWhenSucceeded, - launcherPath: launchWhenSucceeded && launcherPath, - }); - yield promiseDownloadStopped(download); - } - - let isTemporary = launchWhenSucceeded && (autoDelete || isPrivate); - let stat = yield OS.File.stat(download.target.path); - if (Services.appinfo.OS == "WINNT") { - // On Windows - // Temporary downloads should be read-only - do_check_eq(stat.winAttributes.readOnly, isTemporary ? true : false); - } else { - // On Linux, Mac - // Temporary downloads should be read-only and not accessible to other - // users, while permanently downloaded files should be readable and - // writable as specified by the system umask. - do_check_eq(stat.unixMode, - isTemporary ? 0o400 : (0o666 & ~OS.Constants.Sys.umask)); - } - } - } - } - - // Clean up the changes to the preference. - Services.prefs.clearUserPref(kDeleteTempFileOnExit); -}); - -/** - * Checks the referrer for downloads. - */ -add_task(function* test_referrer() -{ - let sourcePath = "/test_referrer.txt"; - let sourceUrl = httpUrl("test_referrer.txt"); - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - - function cleanup() { - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - - do_check_true(aRequest.hasHeader("Referer")); - do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL); - }); - let download = yield Downloads.createDownload({ - source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, - target: targetPath, - }); - do_check_eq(download.source.referrer, TEST_REFERRER_URL); - yield download.start(); - - download = yield Downloads.createDownload({ - source: { url: sourceUrl, referrer: TEST_REFERRER_URL, - isPrivate: true }, - target: targetPath, - }); - do_check_eq(download.source.referrer, TEST_REFERRER_URL); - yield download.start(); - - // Test the download still works for non-HTTP channel with referrer. - sourceUrl = "data:text/html,<html><body></body></html>"; - download = yield Downloads.createDownload({ - source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, - target: targetPath, - }); - do_check_eq(download.source.referrer, TEST_REFERRER_URL); - yield download.start(); - - cleanup(); -}); - -/** - * Checks the adjustChannel callback for downloads. - */ -add_task(function* test_adjustChannel() -{ - const sourcePath = "/test_post.txt"; - const sourceUrl = httpUrl("test_post.txt"); - const targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - const customHeader = { name: "X-Answer", value: "42" }; - const postData = "Don't Panic"; - - function cleanup() { - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - gHttpServer.registerPathHandler(sourcePath, aRequest => { - do_check_eq(aRequest.method, "POST"); - - do_check_true(aRequest.hasHeader(customHeader.name)); - do_check_eq(aRequest.getHeader(customHeader.name), customHeader.value); - - const stream = aRequest.bodyInputStream; - const body = NetUtil.readInputStreamToString(stream, stream.available()); - do_check_eq(body, postData); - }); - - function adjustChannel(channel) { - channel.QueryInterface(Ci.nsIHttpChannel); - channel.setRequestHeader(customHeader.name, customHeader.value, false); - - const stream = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - stream.setData(postData, postData.length); - - channel.QueryInterface(Ci.nsIUploadChannel2); - channel.explicitSetUploadStream(stream, null, -1, "POST", false); - - return Promise.resolve(); - } - - const download = yield Downloads.createDownload({ - source: { url: sourceUrl, adjustChannel }, - target: targetPath, - }); - do_check_eq(download.source.adjustChannel, adjustChannel); - do_check_eq(download.toSerializable(), null); - yield download.start(); - - cleanup(); -}); - -/** - * Checks initial and final state and progress for a successful download. - */ -add_task(function* test_initial_final_state() -{ - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can check its state before it starts. - download = yield promiseNewDownload(); - - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - do_check_eq(download.progress, 0); - do_check_true(download.startTime === null); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - yield download.start(); - } else { - // When testing DownloadLegacySaver, the download is already started when it - // is created, thus we cannot check its initial state. - download = yield promiseStartLegacyDownload(); - yield promiseDownloadStopped(download); - } - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - do_check_eq(download.progress, 100); - do_check_true(isValidDate(download.startTime)); - do_check_true(download.target.exists); - do_check_eq(download.target.size, TEST_DATA_SHORT.length); -}); - -/** - * Checks the notification of the final download state. - */ -add_task(function* test_final_state_notified() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - let onchangeNotified = false; - let lastNotifiedStopped; - let lastNotifiedProgress; - download.onchange = function () { - onchangeNotified = true; - lastNotifiedStopped = download.stopped; - lastNotifiedProgress = download.progress; - }; - - // Allow the download to complete. - let promiseAttempt = download.start(); - continueResponses(); - yield promiseAttempt; - - // The view should have been notified before the download completes. - do_check_true(onchangeNotified); - do_check_true(lastNotifiedStopped); - do_check_eq(lastNotifiedProgress, 100); -}); - -/** - * Checks intermediate progress for a successful download. - */ -add_task(function* test_intermediate_progress() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - yield promiseDownloadMidway(download); - - do_check_true(download.hasProgress); - do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); - do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); - - // The final file size should not be computed for in-progress downloads. - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - // Continue after the first chunk of data is fully received. - continueResponses(); - yield promiseDownloadStopped(download); - - do_check_true(download.stopped); - do_check_eq(download.progress, 100); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Downloads a file with a "Content-Length" of 0 and checks the progress. - */ -add_task(function* test_empty_progress() -{ - let download = yield promiseStartDownload(httpUrl("empty.txt")); - yield promiseDownloadStopped(download); - - do_check_true(download.stopped); - do_check_true(download.hasProgress); - do_check_eq(download.progress, 100); - do_check_eq(download.currentBytes, 0); - do_check_eq(download.totalBytes, 0); - - // We should have received the content type even for an empty file. - do_check_eq(download.contentType, "text/plain"); - - do_check_eq((yield OS.File.stat(download.target.path)).size, 0); - do_check_true(download.target.exists); - do_check_eq(download.target.size, 0); -}); - -/** - * Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData - * property set, and ensures that the file is saved correctly. - */ -add_task(function* test_empty_progress_tryToKeepPartialData() -{ - // Start a new download and configure it to keep partially downloaded data. - let download; - if (!gUseLegacySaver) { - let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; - download = yield Downloads.createDownload({ - source: httpUrl("empty.txt"), - target: { path: targetFilePath, - partFilePath: targetFilePath + ".part" }, - }); - download.tryToKeepPartialData = true; - download.start().catch(() => {}); - } else { - // Start a download using nsIExternalHelperAppService, that is configured - // to keep partially downloaded data by default. - download = yield promiseStartExternalHelperAppServiceDownload( - httpUrl("empty.txt")); - } - yield promiseDownloadStopped(download); - - // The target file should now have been created, and the ".part" file deleted. - do_check_eq((yield OS.File.stat(download.target.path)).size, 0); - do_check_true(download.target.exists); - do_check_eq(download.target.size, 0); - - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_eq(32, download.saver.getSha256Hash().length); -}); - -/** - * Downloads an empty file with no "Content-Length" and checks the progress. - */ -add_task(function* test_empty_noprogress() -{ - let sourcePath = "/test_empty_noprogress.txt"; - let sourceUrl = httpUrl("test_empty_noprogress.txt"); - let deferRequestReceived = Promise.defer(); - - // Register an interruptible handler that notifies us when the request occurs. - function cleanup() { - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - registerInterruptibleHandler(sourcePath, - function firstPart(aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - deferRequestReceived.resolve(); - }, function secondPart(aRequest, aResponse) { }); - - // Start the download, without allowing the request to finish. - mustInterruptResponses(); - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can hook its onchange callback that will be notified when the - // download starts. - download = yield promiseNewDownload(sourceUrl); - - download.onchange = function () { - if (!download.stopped) { - do_check_false(download.hasProgress); - do_check_eq(download.currentBytes, 0); - do_check_eq(download.totalBytes, 0); - } - }; - - download.start().catch(() => {}); - } else { - // When testing DownloadLegacySaver, the download is already started when it - // is created, and it may have already made all needed property change - // notifications, thus there is no point in checking the onchange callback. - download = yield promiseStartLegacyDownload(sourceUrl); - } - - // Wait for the request to be received by the HTTP server, but don't allow the - // request to finish yet. Before checking the download state, wait for the - // events to be processed by the client. - yield deferRequestReceived.promise; - yield promiseExecuteSoon(); - - // Check that this download has no progress report. - do_check_false(download.stopped); - do_check_false(download.hasProgress); - do_check_eq(download.currentBytes, 0); - do_check_eq(download.totalBytes, 0); - - // Now allow the response to finish. - continueResponses(); - yield promiseDownloadStopped(download); - - // We should have received the content type even if no progress is reported. - do_check_eq(download.contentType, "text/plain"); - - // Verify the state of the completed download. - do_check_true(download.stopped); - do_check_false(download.hasProgress); - do_check_eq(download.progress, 100); - do_check_eq(download.currentBytes, 0); - do_check_eq(download.totalBytes, 0); - do_check_true(download.target.exists); - do_check_eq(download.target.size, 0); - - do_check_eq((yield OS.File.stat(download.target.path)).size, 0); -}); - -/** - * Calls the "start" method two times before the download is finished. - */ -add_task(function* test_start_twice() -{ - mustInterruptResponses(); - - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can start the download later during the test. - download = yield promiseNewDownload(httpUrl("interruptible.txt")); - } else { - // When testing DownloadLegacySaver, the download is already started when it - // is created. Effectively, we are starting the download three times. - download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); - } - - // Call the start method two times. - let promiseAttempt1 = download.start(); - let promiseAttempt2 = download.start(); - - // Allow the download to finish. - continueResponses(); - - // Both promises should now be resolved. - yield promiseAttempt1; - yield promiseAttempt2; - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Cancels a download and verifies that its state is reported correctly. - */ -add_task(function* test_cancel_midway() -{ - mustInterruptResponses(); - - // In this test case, we execute different checks that are only possible with - // DownloadCopySaver or DownloadLegacySaver respectively. - let download; - let options = {}; - if (!gUseLegacySaver) { - download = yield promiseNewDownload(httpUrl("interruptible.txt")); - } else { - download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"), - options); - } - - // Cancel the download after receiving the first part of the response. - let deferCancel = Promise.defer(); - let onchange = function () { - if (!download.stopped && !download.canceled && download.progress == 50) { - // Cancel the download immediately during the notification. - deferCancel.resolve(download.cancel()); - - // The state change happens immediately after calling "cancel", but - // temporary files or part files may still exist at this point. - do_check_true(download.canceled); - } - }; - - // Register for the notification, but also call the function directly in - // case the download already reached the expected progress. This may happen - // when using DownloadLegacySaver. - download.onchange = onchange; - onchange(); - - let promiseAttempt; - if (!gUseLegacySaver) { - promiseAttempt = download.start(); - } - - // Wait on the promise returned by the "cancel" method to ensure that the - // cancellation process finished and temporary files were removed. - yield deferCancel.promise; - - if (gUseLegacySaver) { - // The nsIWebBrowserPersist instance should have been canceled now. - do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT); - } - - do_check_true(download.stopped); - do_check_true(download.canceled); - do_check_true(download.error === null); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - do_check_false(yield OS.File.exists(download.target.path)); - - // Progress properties are not reset by canceling. - do_check_eq(download.progress, 50); - do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); - do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); - - if (!gUseLegacySaver) { - // The promise returned by "start" should have been rejected meanwhile. - try { - yield promiseAttempt; - do_throw("The download should have been canceled."); - } catch (ex) { - if (!(ex instanceof Downloads.Error)) { - throw ex; - } - do_check_false(ex.becauseSourceFailed); - do_check_false(ex.becauseTargetFailed); - } - } -}); - -/** - * Cancels a download while keeping partially downloaded data, and verifies that - * both the target file and the ".part" file are deleted. - */ -add_task(function* test_cancel_midway_tryToKeepPartialData() -{ - let download = yield promiseStartDownload_tryToKeepPartialData(); - - do_check_true(yield OS.File.exists(download.target.path)); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - yield download.cancel(); - yield download.removePartialData(); - - do_check_true(download.stopped); - do_check_true(download.canceled); - do_check_true(download.error === null); - - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(yield OS.File.exists(download.target.partFilePath)); -}); - -/** - * Cancels a download right after starting it. - */ -add_task(function* test_cancel_immediately() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - let promiseAttempt = download.start(); - do_check_false(download.stopped); - - let promiseCancel = download.cancel(); - do_check_true(download.canceled); - - // At this point, we don't know whether the download has already stopped or - // is still waiting for cancellation. We can wait on the promise returned - // by the "start" method to know for sure. - try { - yield promiseAttempt; - do_throw("The download should have been canceled."); - } catch (ex) { - if (!(ex instanceof Downloads.Error)) { - throw ex; - } - do_check_false(ex.becauseSourceFailed); - do_check_false(ex.becauseTargetFailed); - } - - do_check_true(download.stopped); - do_check_true(download.canceled); - do_check_true(download.error === null); - - do_check_false(yield OS.File.exists(download.target.path)); - - // Check that the promise returned by the "cancel" method has been resolved. - yield promiseCancel; -}); - -/** - * Cancels and restarts a download sequentially. - */ -add_task(function* test_cancel_midway_restart() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - // The first time, cancel the download midway. - yield promiseDownloadMidway(download); - yield download.cancel(); - - do_check_true(download.stopped); - - // The second time, we'll provide the entire interruptible response. - continueResponses(); - download.onchange = null; - let promiseAttempt = download.start(); - - // Download state should have already been reset. - do_check_false(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error === null); - - // For the following test, we rely on the network layer reporting its progress - // asynchronously. Otherwise, there is nothing stopping the restarted - // download from reaching the same progress as the first request already. - do_check_eq(download.progress, 0); - do_check_eq(download.totalBytes, 0); - do_check_eq(download.currentBytes, 0); - - yield promiseAttempt; - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Cancels a download and restarts it from where it stopped. - */ -add_task(function* test_cancel_midway_restart_tryToKeepPartialData() -{ - let download = yield promiseStartDownload_tryToKeepPartialData(); - yield download.cancel(); - - do_check_true(download.stopped); - do_check_true(download.hasPartialData); - - // The target file should not exist, but we should have kept the partial data. - do_check_false(yield OS.File.exists(download.target.path)); - yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - // Verify that the server sent the response from the start. - do_check_eq(gMostRecentFirstBytePos, 0); - - // The second time, we'll request and obtain the second part of the response, - // but we still stop when half of the remaining progress is reached. - let deferMidway = Promise.defer(); - download.onchange = function () { - if (!download.stopped && !download.canceled && - download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) { - download.onchange = null; - deferMidway.resolve(); - } - }; - - mustInterruptResponses(); - let promiseAttempt = download.start(); - - // Continue when the number of bytes we received is correct, then check that - // progress is at about 75 percent. The exact figure may vary because of - // rounding issues, since the total number of bytes in the response might not - // be a multiple of four. - yield deferMidway.promise; - do_check_true(download.progress > 72 && download.progress < 78); - - // Now we allow the download to finish. - continueResponses(); - yield promiseAttempt; - - // Check that the server now sent the second part only. - do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length); - - // The target file should now have been created, and the ".part" file deleted. - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); - do_check_false(yield OS.File.exists(download.target.partFilePath)); -}); - -/** - * Cancels a download while keeping partially downloaded data, then removes the - * data and restarts the download from the beginning. - */ -add_task(function* test_cancel_midway_restart_removePartialData() -{ - let download = yield promiseStartDownload_tryToKeepPartialData(); - yield download.cancel(); - - do_check_true(download.hasPartialData); - yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - yield download.removePartialData(); - - do_check_false(download.hasPartialData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - // The second time, we'll request and obtain the entire response again. - continueResponses(); - yield download.start(); - - // Verify that the server sent the response from the start. - do_check_eq(gMostRecentFirstBytePos, 0); - - // The target file should now have been created, and the ".part" file deleted. - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); - do_check_false(yield OS.File.exists(download.target.partFilePath)); -}); - -/** - * Cancels a download while keeping partially downloaded data, then removes the - * data and restarts the download from the beginning without keeping the partial - * data anymore. - */ -add_task(function* test_cancel_midway_restart_tryToKeepPartialData_false() -{ - let download = yield promiseStartDownload_tryToKeepPartialData(); - yield download.cancel(); - - download.tryToKeepPartialData = false; - - // The above property change does not affect existing partial data. - do_check_true(download.hasPartialData); - yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); - - yield download.removePartialData(); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - - // Restart the download from the beginning. - mustInterruptResponses(); - download.start().catch(() => {}); - - yield promiseDownloadMidway(download); - yield promisePartFileReady(download); - - // While the download is in progress, we should still have a ".part" file. - do_check_false(download.hasPartialData); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - // On Unix, verify that the file with the partially downloaded data is not - // accessible by other users on the system. - if (Services.appinfo.OS == "Darwin" || Services.appinfo.OS == "Linux") { - do_check_eq((yield OS.File.stat(download.target.partFilePath)).unixMode, - 0o600); - } - - yield download.cancel(); - - // The ".part" file should be deleted now that the download is canceled. - do_check_false(download.hasPartialData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - - // The third time, we'll request and obtain the entire response again. - continueResponses(); - yield download.start(); - - // Verify that the server sent the response from the start. - do_check_eq(gMostRecentFirstBytePos, 0); - - // The target file should now have been created, and the ".part" file deleted. - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); - do_check_false(yield OS.File.exists(download.target.partFilePath)); -}); - -/** - * Cancels a download right after starting it, then restarts it immediately. - */ -add_task(function* test_cancel_immediately_restart_immediately() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - let promiseAttempt = download.start(); - - do_check_false(download.stopped); - - download.cancel(); - do_check_true(download.canceled); - - let promiseRestarted = download.start(); - do_check_false(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error === null); - - // For the following test, we rely on the network layer reporting its progress - // asynchronously. Otherwise, there is nothing stopping the restarted - // download from reaching the same progress as the first request already. - do_check_eq(download.hasProgress, false); - do_check_eq(download.progress, 0); - do_check_eq(download.totalBytes, 0); - do_check_eq(download.currentBytes, 0); - - // Ensure the next request is now allowed to complete, regardless of whether - // the canceled request was received by the server or not. - continueResponses(); - try { - yield promiseAttempt; - // If we get here, it means that the first attempt actually succeeded. In - // fact, this could be a valid outcome, because the cancellation request may - // not have been processed in time before the download finished. - do_print("The download should have been canceled."); - } catch (ex) { - if (!(ex instanceof Downloads.Error)) { - throw ex; - } - do_check_false(ex.becauseSourceFailed); - do_check_false(ex.becauseTargetFailed); - } - - yield promiseRestarted; - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Cancels a download midway, then restarts it immediately. - */ -add_task(function* test_cancel_midway_restart_immediately() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - let promiseAttempt = download.start(); - - // The first time, cancel the download midway. - yield promiseDownloadMidway(download); - download.cancel(); - do_check_true(download.canceled); - - let promiseRestarted = download.start(); - do_check_false(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error === null); - - // For the following test, we rely on the network layer reporting its progress - // asynchronously. Otherwise, there is nothing stopping the restarted - // download from reaching the same progress as the first request already. - do_check_eq(download.hasProgress, false); - do_check_eq(download.progress, 0); - do_check_eq(download.totalBytes, 0); - do_check_eq(download.currentBytes, 0); - - // The second request is allowed to complete. - continueResponses(); - try { - yield promiseAttempt; - do_throw("The download should have been canceled."); - } catch (ex) { - if (!(ex instanceof Downloads.Error)) { - throw ex; - } - do_check_false(ex.becauseSourceFailed); - do_check_false(ex.becauseTargetFailed); - } - - yield promiseRestarted; - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Calls the "cancel" method on a successful download. - */ -add_task(function* test_cancel_successful() -{ - let download = yield promiseStartDownload(); - yield promiseDownloadStopped(download); - - // The cancel method should succeed with no effect. - yield download.cancel(); - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); -}); - -/** - * Calls the "cancel" method two times in a row. - */ -add_task(function* test_cancel_twice() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - let promiseAttempt = download.start(); - do_check_false(download.stopped); - - let promiseCancel1 = download.cancel(); - do_check_true(download.canceled); - let promiseCancel2 = download.cancel(); - - try { - yield promiseAttempt; - do_throw("The download should have been canceled."); - } catch (ex) { - if (!(ex instanceof Downloads.Error)) { - throw ex; - } - do_check_false(ex.becauseSourceFailed); - do_check_false(ex.becauseTargetFailed); - } - - // Both promises should now be resolved. - yield promiseCancel1; - yield promiseCancel2; - - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_true(download.canceled); - do_check_true(download.error === null); - - do_check_false(yield OS.File.exists(download.target.path)); -}); - -/** - * Checks the "refresh" method for succeeded downloads. - */ -add_task(function* test_refresh_succeeded() -{ - let download = yield promiseStartDownload(); - yield promiseDownloadStopped(download); - - // The DownloadTarget properties should be the same after calling "refresh". - yield download.refresh(); - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); - - // If the file is removed, only the "exists" property should change, and the - // "size" property should keep its previous value. - yield OS.File.move(download.target.path, download.target.path + ".old"); - yield download.refresh(); - do_check_false(download.target.exists); - do_check_eq(download.target.size, TEST_DATA_SHORT.length); - - // The DownloadTarget properties should be restored when the file is put back. - yield OS.File.move(download.target.path + ".old", download.target.path); - yield download.refresh(); - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); -}); - -/** - * Checks that a download cannot be restarted after the "finalize" method. - */ -add_task(function* test_finalize() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - let promiseFinalized = download.finalize(); - - try { - yield download.start(); - do_throw("It should not be possible to restart after finalization."); - } catch (ex) { } - - yield promiseFinalized; - - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_true(download.canceled); - do_check_true(download.error === null); - - do_check_false(yield OS.File.exists(download.target.path)); -}); - -/** - * Checks that the "finalize" method can remove partially downloaded data. - */ -add_task(function* test_finalize_tryToKeepPartialData() -{ - // Check finalization without removing partial data. - let download = yield promiseStartDownload_tryToKeepPartialData(); - yield download.finalize(); - - do_check_true(download.hasPartialData); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - // Clean up. - yield download.removePartialData(); - - // Check finalization while removing partial data. - download = yield promiseStartDownload_tryToKeepPartialData(); - yield download.finalize(true); - - do_check_false(download.hasPartialData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); -}); - -/** - * Checks that whenSucceeded returns a promise that is resolved after a restart. - */ -add_task(function* test_whenSucceeded_after_restart() -{ - mustInterruptResponses(); - - let promiseSucceeded; - - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can verify getting a reference before the first download attempt. - download = yield promiseNewDownload(httpUrl("interruptible.txt")); - promiseSucceeded = download.whenSucceeded(); - download.start().catch(() => {}); - } else { - // When testing DownloadLegacySaver, the download is already started when it - // is created, thus we cannot get the reference before the first attempt. - download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); - promiseSucceeded = download.whenSucceeded(); - } - - // Cancel the first download attempt. - yield download.cancel(); - - // The second request is allowed to complete. - continueResponses(); - download.start().catch(() => {}); - - // Wait for the download to finish by waiting on the whenSucceeded promise. - yield promiseSucceeded; - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); -}); - -/** - * Ensures download error details are reported on network failures. - */ -add_task(function* test_error_source() -{ - let serverSocket = startFakeServer(); - try { - let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt"; - - let download; - try { - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we want to check that the promise - // returned by the "start" method is rejected. - download = yield promiseNewDownload(sourceUrl); - - do_check_true(download.error === null); - - yield download.start(); - } else { - // When testing DownloadLegacySaver, we cannot be sure whether we are - // testing the promise returned by the "start" method or we are testing - // the "error" property checked by promiseDownloadStopped. This happens - // because we don't have control over when the download is started. - download = yield promiseStartLegacyDownload(sourceUrl); - yield promiseDownloadStopped(download); - } - do_throw("The download should have failed."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseSourceFailed) { - throw ex; - } - // A specific error object is thrown when reading from the source fails. - } - - // Check the properties now that the download stopped. - do_check_true(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error !== null); - do_check_true(download.error.becauseSourceFailed); - do_check_false(download.error.becauseTargetFailed); - - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - } finally { - serverSocket.close(); - } -}); - -/** - * Ensures a download error is reported when receiving less bytes than what was - * specified in the Content-Length header. - */ -add_task(function* test_error_source_partial() -{ - let sourceUrl = httpUrl("shorter-than-content-length-http-1-1.txt"); - - let enforcePref = Services.prefs.getBoolPref("network.http.enforce-framing.http1"); - Services.prefs.setBoolPref("network.http.enforce-framing.http1", true); - - function cleanup() { - Services.prefs.setBoolPref("network.http.enforce-framing.http1", enforcePref); - } - do_register_cleanup(cleanup); - - let download; - try { - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we want to check that the promise - // returned by the "start" method is rejected. - download = yield promiseNewDownload(sourceUrl); - - do_check_true(download.error === null); - - yield download.start(); - } else { - // When testing DownloadLegacySaver, we cannot be sure whether we are - // testing the promise returned by the "start" method or we are testing - // the "error" property checked by promiseDownloadStopped. This happens - // because we don't have control over when the download is started. - download = yield promiseStartLegacyDownload(sourceUrl); - yield promiseDownloadStopped(download); - } - do_throw("The download should have failed."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseSourceFailed) { - throw ex; - } - // A specific error object is thrown when reading from the source fails. - } - - // Check the properties now that the download stopped. - do_check_true(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error !== null); - do_check_true(download.error.becauseSourceFailed); - do_check_false(download.error.becauseTargetFailed); - do_check_eq(download.error.result, Cr.NS_ERROR_NET_PARTIAL_TRANSFER); - - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); -}); - -/** - * Ensures download error details are reported on local writing failures. - */ -add_task(function* test_error_target() -{ - // Create a file without write access permissions before downloading. - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); - try { - let download; - try { - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we want to check that the promise - // returned by the "start" method is rejected. - download = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: targetFile, - }); - yield download.start(); - } else { - // When testing DownloadLegacySaver, we cannot be sure whether we are - // testing the promise returned by the "start" method or we are testing - // the "error" property checked by promiseDownloadStopped. This happens - // because we don't have control over when the download is started. - download = yield promiseStartLegacyDownload(null, - { targetFile: targetFile }); - yield promiseDownloadStopped(download); - } - do_throw("The download should have failed."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseTargetFailed) { - throw ex; - } - // A specific error object is thrown when writing to the target fails. - } - - // Check the properties now that the download stopped. - do_check_true(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error !== null); - do_check_true(download.error.becauseTargetFailed); - do_check_false(download.error.becauseSourceFailed); - } finally { - // Restore the default permissions to allow deleting the file on Windows. - if (targetFile.exists()) { - targetFile.permissions = FileUtils.PERMS_FILE; - targetFile.remove(false); - } - } -}); - -/** - * Restarts a failed download. - */ -add_task(function* test_error_restart() -{ - let download; - - // Create a file without write access permissions before downloading. - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); - try { - // Use DownloadCopySaver or DownloadLegacySaver based on the test run, - // specifying the target file we created. - if (!gUseLegacySaver) { - download = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: targetFile, - }); - download.start().catch(() => {}); - } else { - download = yield promiseStartLegacyDownload(null, - { targetFile: targetFile }); - } - yield promiseDownloadStopped(download); - do_throw("The download should have failed."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseTargetFailed) { - throw ex; - } - // A specific error object is thrown when writing to the target fails. - } finally { - // Restore the default permissions to allow deleting the file on Windows. - if (targetFile.exists()) { - targetFile.permissions = FileUtils.PERMS_FILE; - - // Also for Windows, rename the file before deleting. This makes the - // current file name available immediately for a new file, while deleting - // in place prevents creation of a file with the same name for some time. - targetFile.moveTo(null, targetFile.leafName + ".delete.tmp"); - targetFile.remove(false); - } - } - - // Restart the download and wait for completion. - yield download.start(); - - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.canceled); - do_check_true(download.error === null); - do_check_eq(download.progress, 100); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); -}); - -/** - * Executes download in both public and private modes. - */ -add_task(function* test_public_and_private() -{ - let sourcePath = "/test_public_and_private.txt"; - let sourceUrl = httpUrl("test_public_and_private.txt"); - let testCount = 0; - - // Apply pref to allow all cookies. - Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); - - function cleanup() { - Services.prefs.clearUserPref("network.cookie.cookieBehavior"); - Services.cookies.removeAll(); - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - - if (testCount == 0) { - // No cookies should exist for first public download. - do_check_false(aRequest.hasHeader("Cookie")); - aResponse.setHeader("Set-Cookie", "foobar=1", false); - testCount++; - } else if (testCount == 1) { - // The cookie should exists for second public download. - do_check_true(aRequest.hasHeader("Cookie")); - do_check_eq(aRequest.getHeader("Cookie"), "foobar=1"); - testCount++; - } else if (testCount == 2) { - // No cookies should exist for first private download. - do_check_false(aRequest.hasHeader("Cookie")); - } - }); - - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.fetch(sourceUrl, targetFile); - yield Downloads.fetch(sourceUrl, targetFile); - - if (!gUseLegacySaver) { - let download = yield Downloads.createDownload({ - source: { url: sourceUrl, isPrivate: true }, - target: targetFile, - }); - yield download.start(); - } else { - let download = yield promiseStartLegacyDownload(sourceUrl, - { isPrivate: true }); - yield promiseDownloadStopped(download); - } - - cleanup(); -}); - -/** - * Checks the startTime gets updated even after a restart. - */ -add_task(function* test_cancel_immediately_restart_and_check_startTime() -{ - let download = yield promiseStartDownload(); - - let startTime = download.startTime; - do_check_true(isValidDate(download.startTime)); - - yield download.cancel(); - do_check_eq(download.startTime.getTime(), startTime.getTime()); - - // Wait for a timeout. - yield promiseTimeout(10); - - yield download.start(); - do_check_true(download.startTime.getTime() > startTime.getTime()); -}); - -/** - * Executes download with content-encoding. - */ -add_task(function* test_with_content_encoding() -{ - let sourcePath = "/test_with_content_encoding.txt"; - let sourceUrl = httpUrl("test_with_content_encoding.txt"); - - function cleanup() { - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.setHeader("Content-Encoding", "gzip", false); - aResponse.setHeader("Content-Length", - "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); - - let bos = new BinaryOutputStream(aResponse.bodyOutputStream); - bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, - TEST_DATA_SHORT_GZIP_ENCODED.length); - }); - - let download = yield promiseStartDownload(sourceUrl); - yield promiseDownloadStopped(download); - - do_check_eq(download.progress, 100); - do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); - - // Ensure the content matches the decoded test data. - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); - - cleanup(); -}); - -/** - * Checks that the file is not decoded if the extension matches the encoding. - */ -add_task(function* test_with_content_encoding_ignore_extension() -{ - let sourcePath = "/test_with_content_encoding_ignore_extension.gz"; - let sourceUrl = httpUrl("test_with_content_encoding_ignore_extension.gz"); - - function cleanup() { - gHttpServer.registerPathHandler(sourcePath, null); - } - do_register_cleanup(cleanup); - - gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.setHeader("Content-Encoding", "gzip", false); - aResponse.setHeader("Content-Length", - "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); - - let bos = new BinaryOutputStream(aResponse.bodyOutputStream); - bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, - TEST_DATA_SHORT_GZIP_ENCODED.length); - }); - - let download = yield promiseStartDownload(sourceUrl); - yield promiseDownloadStopped(download); - - do_check_eq(download.progress, 100); - do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); - do_check_eq(download.target.size, TEST_DATA_SHORT_GZIP_ENCODED.length); - - // Ensure the content matches the encoded test data. We convert the data to a - // string before executing the content check. - yield promiseVerifyTarget(download.target, - String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED)); - - cleanup(); -}); - -/** - * Cancels and restarts a download sequentially with content-encoding. - */ -add_task(function* test_cancel_midway_restart_with_content_encoding() -{ - mustInterruptResponses(); - - let download = yield promiseStartDownload(httpUrl("interruptible_gzip.txt")); - - // The first time, cancel the download midway. - let deferCancel = Promise.defer(); - let onchange = function () { - if (!download.stopped && !download.canceled && - download.currentBytes == TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length) { - deferCancel.resolve(download.cancel()); - } - }; - - // Register for the notification, but also call the function directly in - // case the download already reached the expected progress. - download.onchange = onchange; - onchange(); - - yield deferCancel.promise; - - do_check_true(download.stopped); - - // The second time, we'll provide the entire interruptible response. - continueResponses(); - download.onchange = null; - yield download.start(); - - do_check_eq(download.progress, 100); - do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); - - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT); -}); - -/** - * Download with parental controls enabled. - */ -add_task(function* test_blocked_parental_controls() -{ - let blockFn = base => ({ - shouldBlockForParentalControls: () => Promise.resolve(true), - }); - - Integration.downloads.register(blockFn); - function cleanup() { - Integration.downloads.unregister(blockFn); - } - do_register_cleanup(cleanup); - - let download; - try { - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we want to check that the promise - // returned by the "start" method is rejected. - download = yield promiseNewDownload(); - yield download.start(); - } else { - // When testing DownloadLegacySaver, we cannot be sure whether we are - // testing the promise returned by the "start" method or we are testing - // the "error" property checked by promiseDownloadStopped. This happens - // because we don't have control over when the download is started. - download = yield promiseStartLegacyDownload(); - yield promiseDownloadStopped(download); - } - do_throw("The download should have blocked."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseBlocked) { - throw ex; - } - do_check_true(ex.becauseBlockedByParentalControls); - do_check_true(download.error.becauseBlockedByParentalControls); - } - - // Now that the download stopped, the target file should not exist. - do_check_false(yield OS.File.exists(download.target.path)); - - cleanup(); -}); - -/** - * Test a download that will be blocked by Windows parental controls by - * resulting in an HTTP status code of 450. - */ -add_task(function* test_blocked_parental_controls_httpstatus450() -{ - let download; - try { - if (!gUseLegacySaver) { - download = yield promiseNewDownload(httpUrl("parentalblocked.zip")); - yield download.start(); - } - else { - download = yield promiseStartLegacyDownload(httpUrl("parentalblocked.zip")); - yield promiseDownloadStopped(download); - } - do_throw("The download should have blocked."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseBlocked) { - throw ex; - } - do_check_true(ex.becauseBlockedByParentalControls); - do_check_true(download.error.becauseBlockedByParentalControls); - do_check_true(download.stopped); - } - - do_check_false(yield OS.File.exists(download.target.path)); -}); - -/** - * Download with runtime permissions - */ -add_task(function* test_blocked_runtime_permissions() -{ - let blockFn = base => ({ - shouldBlockForRuntimePermissions: () => Promise.resolve(true), - }); - - Integration.downloads.register(blockFn); - function cleanup() { - Integration.downloads.unregister(blockFn); - } - do_register_cleanup(cleanup); - - let download; - try { - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we want to check that the promise - // returned by the "start" method is rejected. - download = yield promiseNewDownload(); - yield download.start(); - } else { - // When testing DownloadLegacySaver, we cannot be sure whether we are - // testing the promise returned by the "start" method or we are testing - // the "error" property checked by promiseDownloadStopped. This happens - // because we don't have control over when the download is started. - download = yield promiseStartLegacyDownload(); - yield promiseDownloadStopped(download); - } - do_throw("The download should have blocked."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseBlocked) { - throw ex; - } - do_check_true(ex.becauseBlockedByRuntimePermissions); - do_check_true(download.error.becauseBlockedByRuntimePermissions); - } - - // Now that the download stopped, the target file should not exist. - do_check_false(yield OS.File.exists(download.target.path)); - - cleanup(); -}); - -/** - * Check that DownloadCopySaver can always retrieve the hash. - * DownloadLegacySaver can only retrieve the hash when - * nsIExternalHelperAppService is invoked. - */ -add_task(function* test_getSha256Hash() -{ - if (!gUseLegacySaver) { - let download = yield promiseStartDownload(httpUrl("source.txt")); - yield promiseDownloadStopped(download); - do_check_true(download.stopped); - do_check_eq(32, download.saver.getSha256Hash().length); - } -}); - -/** - * Create a download which will be reputation blocked. - * - * @param options - * { - * keepPartialData: bool, - * keepBlockedData: bool, - * } - * @return {Promise} - * @resolves The reputation blocked download. - * @rejects JavaScript exception. - */ -var promiseBlockedDownload = Task.async(function* (options) { - let blockFn = base => ({ - shouldBlockForReputationCheck: () => Promise.resolve({ - shouldBlock: true, - verdict: Downloads.Error.BLOCK_VERDICT_UNCOMMON, - }), - shouldKeepBlockedData: () => Promise.resolve(options.keepBlockedData), - }); - - Integration.downloads.register(blockFn); - function cleanup() { - Integration.downloads.unregister(blockFn); - } - do_register_cleanup(cleanup); - - let download; - - try { - if (options.keepPartialData) { - download = yield promiseStartDownload_tryToKeepPartialData(); - continueResponses(); - } else if (gUseLegacySaver) { - download = yield promiseStartLegacyDownload(); - } else { - download = yield promiseNewDownload(); - yield download.start(); - do_throw("The download should have blocked."); - } - - yield promiseDownloadStopped(download); - do_throw("The download should have blocked."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseBlocked) { - throw ex; - } - do_check_true(ex.becauseBlockedByReputationCheck); - do_check_eq(ex.reputationCheckVerdict, - Downloads.Error.BLOCK_VERDICT_UNCOMMON); - do_check_true(download.error.becauseBlockedByReputationCheck); - do_check_eq(download.error.reputationCheckVerdict, - Downloads.Error.BLOCK_VERDICT_UNCOMMON); - } - - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_false(yield OS.File.exists(download.target.path)); - - cleanup(); - return download; -}); - -/** - * Checks that application reputation blocks the download and the target file - * does not exist. - */ -add_task(function* test_blocked_applicationReputation() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: false, - keepBlockedData: false, - }); - - // Now that the download is blocked, the target file should not exist. - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); - - // There should also be no blocked data in this case - do_check_false(download.hasBlockedData); -}); - -/** - * Checks that if a download restarts while processing an application reputation - * request, the status is handled correctly. - */ -add_task(function* test_blocked_applicationReputation_race() -{ - let isFirstShouldBlockCall = true; - - let blockFn = base => ({ - shouldBlockForReputationCheck(download) { - if (isFirstShouldBlockCall) { - isFirstShouldBlockCall = false; - - // 2. Cancel and restart the download before the first attempt has a - // chance to finish. - download.cancel(); - download.removePartialData(); - download.start(); - - // 3. Allow the first attempt to finish with a blocked response. - return Promise.resolve({ - shouldBlock: true, - verdict: Downloads.Error.BLOCK_VERDICT_UNCOMMON, - }); - } - - // 4/5. Don't block the download the second time. The race condition would - // occur with the first attempt regardless of whether the second one - // is blocked, but not blocking here makes the test simpler. - return Promise.resolve({ - shouldBlock: false, - verdict: "", - }); - }, - shouldKeepBlockedData: () => Promise.resolve(true), - }); - - Integration.downloads.register(blockFn); - function cleanup() { - Integration.downloads.unregister(blockFn); - } - do_register_cleanup(cleanup); - - let download; - - try { - // 1. Start the download and get a reference to the promise asociated with - // the first attempt, before allowing the response to continue. - download = yield promiseStartDownload_tryToKeepPartialData(); - let firstAttempt = promiseDownloadStopped(download); - continueResponses(); - - // 4/5. Wait for the first attempt to be completed. The result of this - // should appear as a cancellation. - yield firstAttempt; - - do_throw("The first attempt should have been canceled."); - } catch (ex) { - // The "becauseBlocked" property should be false. - if (!(ex instanceof Downloads.Error) || ex.becauseBlocked) { - throw ex; - } - } - - // 6. Wait for the second attempt to be completed. - yield promiseDownloadStopped(download); - - // 7. At this point, "hasBlockedData" should be false. - do_check_false(download.hasBlockedData); - - cleanup(); -}); - -/** - * Checks that application reputation blocks the download but maintains the - * blocked data, which will be deleted when the block is confirmed. - */ -add_task(function* test_blocked_applicationReputation_confirmBlock() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - do_check_true(download.hasBlockedData); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - yield download.confirmBlock(); - - // After confirming the block the download should be in a failed state and - // have no downloaded data left on disk. - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_false(download.hasBlockedData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); -}); - -/** - * Checks that application reputation blocks the download but maintains the - * blocked data, which will be used to complete the download when unblocking. - */ -add_task(function* test_blocked_applicationReputation_unblock() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - do_check_true(download.hasBlockedData); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - yield download.unblock(); - - // After unblocking the download should have succeeded and be - // present at the final path. - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.hasBlockedData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT); - - // The only indication the download was previously blocked is the - // existence of the error, so we make sure it's still set. - do_check_true(download.error instanceof Downloads.Error); - do_check_true(download.error.becauseBlocked); - do_check_true(download.error.becauseBlockedByReputationCheck); -}); - -/** - * Check that calling cancel on a blocked download will not cause errors - */ -add_task(function* test_blocked_applicationReputation_cancel() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - // This call should succeed on a blocked download. - yield download.cancel(); - - // Calling cancel should not have changed the current state, the download - // should still be blocked. - do_check_true(download.error.becauseBlockedByReputationCheck); - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_true(download.hasBlockedData); -}); - -/** - * Checks that unblock and confirmBlock cannot race on a blocked download - */ -add_task(function* test_blocked_applicationReputation_decisionRace() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - let unblockPromise = download.unblock(); - let confirmBlockPromise = download.confirmBlock(); - - yield confirmBlockPromise.then(() => { - do_throw("confirmBlock should have failed."); - }, () => {}); - - yield unblockPromise; - - // After unblocking the download should have succeeded and be - // present at the final path. - do_check_true(download.stopped); - do_check_true(download.succeeded); - do_check_false(download.hasBlockedData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_true(yield OS.File.exists(download.target.path)); - - download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - confirmBlockPromise = download.confirmBlock(); - unblockPromise = download.unblock(); - - yield unblockPromise.then(() => { - do_throw("unblock should have failed."); - }, () => {}); - - yield confirmBlockPromise; - - // After confirming the block the download should be in a failed state and - // have no downloaded data left on disk. - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_false(download.hasBlockedData); - do_check_false(yield OS.File.exists(download.target.partFilePath)); - do_check_false(yield OS.File.exists(download.target.path)); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); -}); - -/** - * Checks that unblocking a blocked download fails if the blocked data has been - * removed. - */ -add_task(function* test_blocked_applicationReputation_unblock() -{ - let download = yield promiseBlockedDownload({ - keepPartialData: true, - keepBlockedData: true, - }); - - do_check_true(download.hasBlockedData); - do_check_true(yield OS.File.exists(download.target.partFilePath)); - - // Remove the blocked data without telling the download. - yield OS.File.remove(download.target.partFilePath); - - let unblockPromise = download.unblock(); - yield unblockPromise.then(() => { - do_throw("unblock should have failed."); - }, () => {}); - - // Even though unblocking failed the download state should have been updated - // to reflect the lack of blocked data. - do_check_false(download.hasBlockedData); - do_check_true(download.stopped); - do_check_false(download.succeeded); - do_check_false(download.target.exists); - do_check_eq(download.target.size, 0); -}); - -/** - * download.showContainingDirectory() action - */ -add_task(function* test_showContainingDirectory() { - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - - let download = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt") }, - target: "" - }); - - let promiseDirectoryShown = waitForDirectoryShown(); - yield download.showContainingDirectory(); - let path = yield promiseDirectoryShown; - try { - new FileUtils.File(path); - do_throw("Should have failed because of an invalid path."); - } catch (ex) { - if (!(ex instanceof Components.Exception)) { - throw ex; - } - // Invalid paths on Windows are reported with NS_ERROR_FAILURE, - // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux - let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || - ex.result == Cr.NS_ERROR_FAILURE; - do_check_true(validResult); - } - - download = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt") }, - target: targetPath - }); - - promiseDirectoryShown = waitForDirectoryShown(); - download.showContainingDirectory(); - yield promiseDirectoryShown; -}); - -/** - * download.launch() action - */ -add_task(function* test_launch() { - let customLauncher = getTempFile("app-launcher"); - - // Test both with and without setting a custom application. - for (let launcherPath of [null, customLauncher.path]) { - let download; - if (!gUseLegacySaver) { - // When testing DownloadCopySaver, we have control over the download, thus - // we can test that file is not launched if download.succeeded is not set. - download = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: getTempFile(TEST_TARGET_FILE_NAME).path, - launcherPath: launcherPath, - launchWhenSucceeded: true - }); - - try { - yield download.launch(); - do_throw("Can't launch download file as it has not completed yet"); - } catch (ex) { - do_check_eq(ex.message, - "launch can only be called if the download succeeded"); - } - - yield download.start(); - } else { - // When testing DownloadLegacySaver, the download is already started when - // it is created, thus we don't test calling "launch" before starting. - download = yield promiseStartLegacyDownload( - httpUrl("source.txt"), - { launcherPath: launcherPath, - launchWhenSucceeded: true }); - yield promiseDownloadStopped(download); - } - - do_check_true(download.launchWhenSucceeded); - - let promiseFileLaunched = waitForFileLaunched(); - download.launch(); - let result = yield promiseFileLaunched; - - // Verify that the results match the test case. - if (!launcherPath) { - // This indicates that the default handler has been chosen. - do_check_true(result === null); - } else { - // Check the nsIMIMEInfo instance that would have been used for launching. - do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); - do_check_true(result.preferredApplicationHandler - .QueryInterface(Ci.nsILocalHandlerApp) - .executable.equals(customLauncher)); - } - } -}); - -/** - * Test passing an invalid path as the launcherPath property. - */ -add_task(function* test_launcherPath_invalid() { - let download = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt") }, - target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, - launcherPath: " " - }); - - let promiseDownloadLaunched = new Promise(resolve => { - let waitFn = base => ({ - __proto__: base, - launchDownload() { - Integration.downloads.unregister(waitFn); - let superPromise = super.launchDownload(...arguments); - resolve(superPromise); - return superPromise; - }, - }); - Integration.downloads.register(waitFn); - }); - - yield download.start(); - try { - download.launch(); - yield promiseDownloadLaunched; - do_throw("Can't launch file with invalid custom launcher") - } catch (ex) { - if (!(ex instanceof Components.Exception)) { - throw ex; - } - // Invalid paths on Windows are reported with NS_ERROR_FAILURE, - // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux - let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || - ex.result == Cr.NS_ERROR_FAILURE; - do_check_true(validResult); - } -}); - -/** - * Tests that download.launch() is automatically called after - * the download finishes if download.launchWhenSucceeded = true - */ -add_task(function* test_launchWhenSucceeded() { - let customLauncher = getTempFile("app-launcher"); - - // Test both with and without setting a custom application. - for (let launcherPath of [null, customLauncher.path]) { - let promiseFileLaunched = waitForFileLaunched(); - - if (!gUseLegacySaver) { - let download = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: getTempFile(TEST_TARGET_FILE_NAME).path, - launchWhenSucceeded: true, - launcherPath: launcherPath, - }); - yield download.start(); - } else { - let download = yield promiseStartLegacyDownload( - httpUrl("source.txt"), - { launcherPath: launcherPath, - launchWhenSucceeded: true }); - yield promiseDownloadStopped(download); - } - - let result = yield promiseFileLaunched; - - // Verify that the results match the test case. - if (!launcherPath) { - // This indicates that the default handler has been chosen. - do_check_true(result === null); - } else { - // Check the nsIMIMEInfo instance that would have been used for launching. - do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); - do_check_true(result.preferredApplicationHandler - .QueryInterface(Ci.nsILocalHandlerApp) - .executable.equals(customLauncher)); - } - } -}); - -/** - * Tests that the proper content type is set for a normal download. - */ -add_task(function* test_contentType() { - let download = yield promiseStartDownload(httpUrl("source.txt")); - yield promiseDownloadStopped(download); - - do_check_eq("text/plain", download.contentType); -}); - -/** - * Tests that the serialization/deserialization of the startTime Date - * object works correctly. - */ -add_task(function* test_toSerializable_startTime() -{ - let download1 = yield promiseStartDownload(httpUrl("source.txt")); - yield promiseDownloadStopped(download1); - - let serializable = download1.toSerializable(); - let reserialized = JSON.parse(JSON.stringify(serializable)); - - let download2 = yield Downloads.createDownload(reserialized); - - do_check_eq(download1.startTime.constructor.name, "Date"); - do_check_eq(download2.startTime.constructor.name, "Date"); - do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON()); -}); - -/** - * Checks that downloads are added to browsing history when they start. - */ -add_task(function* test_history() -{ - mustInterruptResponses(); - - // We will wait for the visit to be notified during the download. - yield PlacesTestUtils.clearHistory(); - let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt")); - - // Start a download that is not allowed to finish yet. - let download = yield promiseStartDownload(httpUrl("interruptible.txt")); - - // The history notifications should be received before the download completes. - let [time, transitionType] = yield promiseVisit; - do_check_eq(time, download.startTime.getTime() * 1000); - do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); - - // Restart and complete the download after clearing history. - yield PlacesTestUtils.clearHistory(); - download.cancel(); - continueResponses(); - yield download.start(); - - // The restart should not have added a new history visit. - do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt"))); -}); - -/** - * Checks that downloads started by nsIHelperAppService are added to the - * browsing history when they start. - */ -add_task(function* test_history_tryToKeepPartialData() -{ - // We will wait for the visit to be notified during the download. - yield PlacesTestUtils.clearHistory(); - let promiseVisit = - promiseWaitForVisit(httpUrl("interruptible_resumable.txt")); - - // Start a download that is not allowed to finish yet. - let beforeStartTimeMs = Date.now(); - let download = yield promiseStartDownload_tryToKeepPartialData(); - - // The history notifications should be received before the download completes. - let [time, transitionType] = yield promiseVisit; - do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); - - // The time set by nsIHelperAppService may be different than the start time in - // the download object, thus we only check that it is a meaningful time. Note - // that we subtract one second from the earliest time to account for rounding. - do_check_true(time >= beforeStartTimeMs * 1000 - 1000000); - - // Complete the download before finishing the test. - continueResponses(); - yield promiseDownloadStopped(download); -}); - -/** - * Tests that the temp download files are removed on exit and exiting private - * mode after they have been launched. - */ -add_task(function* test_launchWhenSucceeded_deleteTempFileOnExit() { - let customLauncherPath = getTempFile("app-launcher").path; - let autoDeleteTargetPathOne = getTempFile(TEST_TARGET_FILE_NAME).path; - let autoDeleteTargetPathTwo = getTempFile(TEST_TARGET_FILE_NAME).path; - let noAutoDeleteTargetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - - let autoDeleteDownloadOne = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt"), isPrivate: true }, - target: autoDeleteTargetPathOne, - launchWhenSucceeded: true, - launcherPath: customLauncherPath, - }); - yield autoDeleteDownloadOne.start(); - - Services.prefs.setBoolPref(kDeleteTempFileOnExit, true); - let autoDeleteDownloadTwo = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: autoDeleteTargetPathTwo, - launchWhenSucceeded: true, - launcherPath: customLauncherPath, - }); - yield autoDeleteDownloadTwo.start(); - - Services.prefs.setBoolPref(kDeleteTempFileOnExit, false); - let noAutoDeleteDownload = yield Downloads.createDownload({ - source: httpUrl("source.txt"), - target: noAutoDeleteTargetPath, - launchWhenSucceeded: true, - launcherPath: customLauncherPath, - }); - yield noAutoDeleteDownload.start(); - - Services.prefs.clearUserPref(kDeleteTempFileOnExit); - - do_check_true(yield OS.File.exists(autoDeleteTargetPathOne)); - do_check_true(yield OS.File.exists(autoDeleteTargetPathTwo)); - do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); - - // Simulate leaving private browsing mode - Services.obs.notifyObservers(null, "last-pb-context-exited", null); - do_check_false(yield OS.File.exists(autoDeleteTargetPathOne)); - - // Simulate browser shutdown - let expire = Cc["@mozilla.org/uriloader/external-helper-app-service;1"] - .getService(Ci.nsIObserver); - expire.observe(null, "profile-before-change", null); - do_check_false(yield OS.File.exists(autoDeleteTargetPathTwo)); - do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); -}); diff --git a/toolkit/components/jsdownloads/test/unit/head.js b/toolkit/components/jsdownloads/test/unit/head.js deleted file mode 100644 index f322244c4..000000000 --- a/toolkit/components/jsdownloads/test/unit/head.js +++ /dev/null @@ -1,843 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Provides infrastructure for automated download components tests. - */ - -"use strict"; - -// Globals - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; -var Cr = Components.results; - -Cu.import("resource://gre/modules/Integration.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths", - "resource://gre/modules/DownloadPaths.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Downloads", - "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "HttpServer", - "resource://testing-common/httpd.js"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", - "resource://testing-common/PlacesTestUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "MockRegistrar", - "resource://testing-common/MockRegistrar.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "gExternalHelperAppService", - "@mozilla.org/uriloader/external-helper-app-service;1", - Ci.nsIExternalHelperAppService); - -Integration.downloads.defineModuleGetter(this, "DownloadIntegration", - "resource://gre/modules/DownloadIntegration.jsm"); - -const ServerSocket = Components.Constructor( - "@mozilla.org/network/server-socket;1", - "nsIServerSocket", - "init"); -const BinaryOutputStream = Components.Constructor( - "@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream") - -XPCOMUtils.defineLazyServiceGetter(this, "gMIMEService", - "@mozilla.org/mime;1", - "nsIMIMEService"); - -const TEST_TARGET_FILE_NAME = "test-download.txt"; -const TEST_STORE_FILE_NAME = "test-downloads.json"; - -const TEST_REFERRER_URL = "http://www.example.com/referrer.html"; - -const TEST_DATA_SHORT = "This test string is downloaded."; -// Generate using gzipCompressString in TelemetryController.jsm. -const TEST_DATA_SHORT_GZIP_ENCODED_FIRST = [ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 11, 201, 200, 44, 86, 40, 73, 45, 46, 81, 40, 46, 41, 202, 204 -]; -const TEST_DATA_SHORT_GZIP_ENCODED_SECOND = [ - 75, 87, 0, 114, 83, 242, 203, 243, 114, 242, 19, 83, 82, 83, 244, 0, 151, 222, 109, 43, 31, 0, 0, 0 -]; -const TEST_DATA_SHORT_GZIP_ENCODED = - TEST_DATA_SHORT_GZIP_ENCODED_FIRST.concat(TEST_DATA_SHORT_GZIP_ENCODED_SECOND); - -/** - * All the tests are implemented with add_task, this starts them automatically. - */ -function run_test() -{ - do_get_profile(); - run_next_test(); -} - -// Support functions - -/** - * HttpServer object initialized before tests start. - */ -var gHttpServer; - -/** - * Given a file name, returns a string containing an URI that points to the file - * on the currently running instance of the test HTTP server. - */ -function httpUrl(aFileName) { - return "http://localhost:" + gHttpServer.identity.primaryPort + "/" + - aFileName; -} - -// While the previous test file should have deleted all the temporary files it -// used, on Windows these might still be pending deletion on the physical file -// system. Thus, start from a new base number every time, to make a collision -// with a file that is still pending deletion highly unlikely. -var gFileCounter = Math.floor(Math.random() * 1000000); - -/** - * Returns a reference to a temporary file, that is guaranteed not to exist, and - * to have never been created before. - * - * @param aLeafName - * Suggested leaf name for the file to be created. - * - * @return nsIFile pointing to a non-existent file in a temporary directory. - * - * @note It is not enough to delete the file if it exists, or to delete the file - * after calling nsIFile.createUnique, because on Windows the delete - * operation in the file system may still be pending, preventing a new - * file with the same name to be created. - */ -function getTempFile(aLeafName) -{ - // Prepend a serial number to the extension in the suggested leaf name. - let [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName); - let leafName = base + "-" + gFileCounter + ext; - gFileCounter++; - - // Get a file reference under the temporary directory for this test file. - let file = FileUtils.getFile("TmpD", [leafName]); - do_check_false(file.exists()); - - do_register_cleanup(function () { - try { - file.remove(false) - } catch (e) { - if (!(e instanceof Components.Exception && - (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED || - e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST || - e.result == Cr.NS_ERROR_FILE_NOT_FOUND))) { - throw e; - } - // On Windows, we may get an access denied error if the file existed before, - // and was recently deleted. - // Don't bother checking file.exists() as that may also cause an access - // denied error. - } - }); - - return file; -} - -/** - * Waits for pending events to be processed. - * - * @return {Promise} - * @resolves When pending events have been processed. - * @rejects Never. - */ -function promiseExecuteSoon() -{ - let deferred = Promise.defer(); - do_execute_soon(deferred.resolve); - return deferred.promise; -} - -/** - * Waits for a pending events to be processed after a timeout. - * - * @return {Promise} - * @resolves When pending events have been processed. - * @rejects Never. - */ -function promiseTimeout(aTime) -{ - let deferred = Promise.defer(); - do_timeout(aTime, deferred.resolve); - return deferred.promise; -} - -/** - * Waits for a new history visit to be notified for the specified URI. - * - * @param aUrl - * String containing the URI that will be visited. - * - * @return {Promise} - * @resolves Array [aTime, aTransitionType] from nsINavHistoryObserver.onVisit. - * @rejects Never. - */ -function promiseWaitForVisit(aUrl) -{ - let deferred = Promise.defer(); - - let uri = NetUtil.newURI(aUrl); - - PlacesUtils.history.addObserver({ - QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]), - onBeginUpdateBatch: function () {}, - onEndUpdateBatch: function () {}, - onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID, - aTransitionType, aGUID, aHidden) { - if (aURI.equals(uri)) { - PlacesUtils.history.removeObserver(this); - deferred.resolve([aTime, aTransitionType]); - } - }, - onTitleChanged: function () {}, - onDeleteURI: function () {}, - onClearHistory: function () {}, - onPageChanged: function () {}, - onDeleteVisits: function () {}, - }, false); - - return deferred.promise; -} - -/** - * Check browsing history to see whether the given URI has been visited. - * - * @param aUrl - * String containing the URI that will be visited. - * - * @return {Promise} - * @resolves Boolean indicating whether the URI has been visited. - * @rejects JavaScript exception. - */ -function promiseIsURIVisited(aUrl) { - let deferred = Promise.defer(); - - PlacesUtils.asyncHistory.isURIVisited(NetUtil.newURI(aUrl), - function (aURI, aIsVisited) { - deferred.resolve(aIsVisited); - }); - - return deferred.promise; -} - -/** - * Creates a new Download object, setting a temporary file as the target. - * - * @param aSourceUrl - * String containing the URI for the download source, or null to use - * httpUrl("source.txt"). - * - * @return {Promise} - * @resolves The newly created Download object. - * @rejects JavaScript exception. - */ -function promiseNewDownload(aSourceUrl) { - return Downloads.createDownload({ - source: aSourceUrl || httpUrl("source.txt"), - target: getTempFile(TEST_TARGET_FILE_NAME), - }); -} - -/** - * Starts a new download using the nsIWebBrowserPersist interface, and controls - * it using the legacy nsITransfer interface. - * - * @param aSourceUrl - * String containing the URI for the download source, or null to use - * httpUrl("source.txt"). - * @param aOptions - * An optional object used to control the behavior of this function. - * You may pass an object with a subset of the following fields: - * { - * isPrivate: Boolean indicating whether the download originated from a - * private window. - * targetFile: nsIFile for the target, or null to use a temporary file. - * outPersist: Receives a reference to the created nsIWebBrowserPersist - * instance. - * launchWhenSucceeded: Boolean indicating whether the target should - * be launched when it has completed successfully. - * launcherPath: String containing the path of the custom executable to - * use to launch the target of the download. - * } - * - * @return {Promise} - * @resolves The Download object created as a consequence of controlling the - * download through the legacy nsITransfer interface. - * @rejects Never. The current test fails in case of exceptions. - */ -function promiseStartLegacyDownload(aSourceUrl, aOptions) { - let sourceURI = NetUtil.newURI(aSourceUrl || httpUrl("source.txt")); - let targetFile = (aOptions && aOptions.targetFile) - || getTempFile(TEST_TARGET_FILE_NAME); - - let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] - .createInstance(Ci.nsIWebBrowserPersist); - if (aOptions) { - aOptions.outPersist = persist; - } - - let fileExtension = null, mimeInfo = null; - let match = sourceURI.path.match(/\.([^.\/]+)$/); - if (match) { - fileExtension = match[1]; - } - - if (fileExtension) { - try { - mimeInfo = gMIMEService.getFromTypeAndExtension(null, fileExtension); - mimeInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk; - } catch (ex) { } - } - - if (aOptions && aOptions.launcherPath) { - do_check_true(mimeInfo != null); - - let localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"] - .createInstance(Ci.nsILocalHandlerApp); - localHandlerApp.executable = new FileUtils.File(aOptions.launcherPath); - - mimeInfo.preferredApplicationHandler = localHandlerApp; - mimeInfo.preferredAction = Ci.nsIMIMEInfo.useHelperApp; - } - - if (aOptions && aOptions.launchWhenSucceeded) { - do_check_true(mimeInfo != null); - - mimeInfo.preferredAction = Ci.nsIMIMEInfo.useHelperApp; - } - - // Apply decoding if required by the "Content-Encoding" header. - persist.persistFlags &= ~Ci.nsIWebBrowserPersist.PERSIST_FLAGS_NO_CONVERSION; - persist.persistFlags |= - Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; - - let transfer = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer); - - let deferred = Promise.defer(); - - Downloads.getList(Downloads.ALL).then(function (aList) { - // Temporarily register a view that will get notified when the download we - // are controlling becomes visible in the list of downloads. - aList.addView({ - onDownloadAdded: function (aDownload) { - aList.removeView(this).then(null, do_report_unexpected_exception); - - // Remove the download to keep the list empty for the next test. This - // also allows the caller to register the "onchange" event directly. - let promise = aList.remove(aDownload); - - // When the download object is ready, make it available to the caller. - promise.then(() => deferred.resolve(aDownload), - do_report_unexpected_exception); - }, - }).then(null, do_report_unexpected_exception); - - let isPrivate = aOptions && aOptions.isPrivate; - - // Initialize the components so they reference each other. This will cause - // the Download object to be created and added to the public downloads. - transfer.init(sourceURI, NetUtil.newURI(targetFile), null, mimeInfo, null, - null, persist, isPrivate); - persist.progressListener = transfer; - - // Start the actual download process. - persist.savePrivacyAwareURI(sourceURI, null, null, 0, null, null, targetFile, - isPrivate); - }.bind(this)).then(null, do_report_unexpected_exception); - - return deferred.promise; -} - -/** - * Starts a new download using the nsIHelperAppService interface, and controls - * it using the legacy nsITransfer interface. The source of the download will - * be "interruptible_resumable.txt" and partially downloaded data will be kept. - * - * @param aSourceUrl - * String containing the URI for the download source, or null to use - * httpUrl("interruptible_resumable.txt"). - * - * @return {Promise} - * @resolves The Download object created as a consequence of controlling the - * download through the legacy nsITransfer interface. - * @rejects Never. The current test fails in case of exceptions. - */ -function promiseStartExternalHelperAppServiceDownload(aSourceUrl) { - let sourceURI = NetUtil.newURI(aSourceUrl || - httpUrl("interruptible_resumable.txt")); - - let deferred = Promise.defer(); - - Downloads.getList(Downloads.PUBLIC).then(function (aList) { - // Temporarily register a view that will get notified when the download we - // are controlling becomes visible in the list of downloads. - aList.addView({ - onDownloadAdded: function (aDownload) { - aList.removeView(this).then(null, do_report_unexpected_exception); - - // Remove the download to keep the list empty for the next test. This - // also allows the caller to register the "onchange" event directly. - let promise = aList.remove(aDownload); - - // When the download object is ready, make it available to the caller. - promise.then(() => deferred.resolve(aDownload), - do_report_unexpected_exception); - }, - }).then(null, do_report_unexpected_exception); - - let channel = NetUtil.newChannel({ - uri: sourceURI, - loadUsingSystemPrincipal: true - }); - - // Start the actual download process. - channel.asyncOpen2({ - contentListener: null, - - onStartRequest: function (aRequest, aContext) - { - let requestChannel = aRequest.QueryInterface(Ci.nsIChannel); - this.contentListener = gExternalHelperAppService.doContent( - requestChannel.contentType, aRequest, null, true); - this.contentListener.onStartRequest(aRequest, aContext); - }, - - onStopRequest: function (aRequest, aContext, aStatusCode) - { - this.contentListener.onStopRequest(aRequest, aContext, aStatusCode); - }, - - onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, - aCount) - { - this.contentListener.onDataAvailable(aRequest, aContext, aInputStream, - aOffset, aCount); - }, - }); - }.bind(this)).then(null, do_report_unexpected_exception); - - return deferred.promise; -} - -/** - * Waits for a download to reach half of its progress, in case it has not - * reached the expected progress already. - * - * @param aDownload - * The Download object to wait upon. - * - * @return {Promise} - * @resolves When the download has reached half of its progress. - * @rejects Never. - */ -function promiseDownloadMidway(aDownload) { - let deferred = Promise.defer(); - - // Wait for the download to reach half of its progress. - let onchange = function () { - if (!aDownload.stopped && !aDownload.canceled && aDownload.progress == 50) { - aDownload.onchange = null; - deferred.resolve(); - } - }; - - // Register for the notification, but also call the function directly in - // case the download already reached the expected progress. - aDownload.onchange = onchange; - onchange(); - - return deferred.promise; -} - -/** - * Waits for a download to finish, in case it has not finished already. - * - * @param aDownload - * The Download object to wait upon. - * - * @return {Promise} - * @resolves When the download has finished successfully. - * @rejects JavaScript exception if the download failed. - */ -function promiseDownloadStopped(aDownload) { - if (!aDownload.stopped) { - // The download is in progress, wait for the current attempt to finish and - // report any errors that may occur. - return aDownload.start(); - } - - if (aDownload.succeeded) { - return Promise.resolve(); - } - - // The download failed or was canceled. - return Promise.reject(aDownload.error || new Error("Download canceled.")); -} - -/** - * Returns a new public or private DownloadList object. - * - * @param aIsPrivate - * True for the private list, false or undefined for the public list. - * - * @return {Promise} - * @resolves The newly created DownloadList object. - * @rejects JavaScript exception. - */ -function promiseNewList(aIsPrivate) -{ - // We need to clear all the internal state for the list and summary objects, - // since all the objects are interdependent internally. - Downloads._promiseListsInitialized = null; - Downloads._lists = {}; - Downloads._summaries = {}; - - return Downloads.getList(aIsPrivate ? Downloads.PRIVATE : Downloads.PUBLIC); -} - -/** - * Ensures that the given file contents are equal to the given string. - * - * @param aPath - * String containing the path of the file whose contents should be - * verified. - * @param aExpectedContents - * String containing the octets that are expected in the file. - * - * @return {Promise} - * @resolves When the operation completes. - * @rejects Never. - */ -function promiseVerifyContents(aPath, aExpectedContents) -{ - return Task.spawn(function* () { - let file = new FileUtils.File(aPath); - - if (!(yield OS.File.exists(aPath))) { - do_throw("File does not exist: " + aPath); - } - - if ((yield OS.File.stat(aPath)).size == 0) { - do_throw("File is empty: " + aPath); - } - - let deferred = Promise.defer(); - NetUtil.asyncFetch( - { uri: NetUtil.newURI(file), loadUsingSystemPrincipal: true }, - function(aInputStream, aStatus) { - do_check_true(Components.isSuccessCode(aStatus)); - let contents = NetUtil.readInputStreamToString(aInputStream, - aInputStream.available()); - if (contents.length > TEST_DATA_SHORT.length * 2 || - /[^\x20-\x7E]/.test(contents)) { - // Do not print the entire content string to the test log. - do_check_eq(contents.length, aExpectedContents.length); - do_check_true(contents == aExpectedContents); - } else { - // Print the string if it is short and made of printable characters. - do_check_eq(contents, aExpectedContents); - } - deferred.resolve(); - }); - - yield deferred.promise; - }); -} - -/** - * Starts a socket listener that closes each incoming connection. - * - * @returns nsIServerSocket that listens for connections. Call its "close" - * method to stop listening and free the server port. - */ -function startFakeServer() -{ - let serverSocket = new ServerSocket(-1, true, -1); - serverSocket.asyncListen({ - onSocketAccepted: function (aServ, aTransport) { - aTransport.close(Cr.NS_BINDING_ABORTED); - }, - onStopListening: function () { }, - }); - return serverSocket; -} - -/** - * This is an internal reference that should not be used directly by tests. - */ -var _gDeferResponses = Promise.defer(); - -/** - * Ensures that all the interruptible requests started after this function is - * called won't complete until the continueResponses function is called. - * - * Normally, the internal HTTP server returns all the available data as soon as - * a request is received. In order for some requests to be served one part at a - * time, special interruptible handlers are registered on the HTTP server. This - * allows testing events or actions that need to happen in the middle of a - * download. - * - * For example, the handler accessible at the httpUri("interruptible.txt") - * address returns the TEST_DATA_SHORT text, then it may block until the - * continueResponses method is called. At this point, the handler sends the - * TEST_DATA_SHORT text again to complete the response. - * - * If an interruptible request is started before the function is called, it may - * or may not be blocked depending on the actual sequence of events. - */ -function mustInterruptResponses() -{ - // If there are pending blocked requests, allow them to complete. This is - // done to prevent requests from being blocked forever, but should not affect - // the test logic, since previously started requests should not be monitored - // on the client side anymore. - _gDeferResponses.resolve(); - - do_print("Interruptible responses will be blocked midway."); - _gDeferResponses = Promise.defer(); -} - -/** - * Allows all the current and future interruptible requests to complete. - */ -function continueResponses() -{ - do_print("Interruptible responses are now allowed to continue."); - _gDeferResponses.resolve(); -} - -/** - * Registers an interruptible response handler. - * - * @param aPath - * Path passed to nsIHttpServer.registerPathHandler. - * @param aFirstPartFn - * This function is called when the response is received, with the - * aRequest and aResponse arguments of the server. - * @param aSecondPartFn - * This function is called with the aRequest and aResponse arguments of - * the server, when the continueResponses function is called. - */ -function registerInterruptibleHandler(aPath, aFirstPartFn, aSecondPartFn) -{ - gHttpServer.registerPathHandler(aPath, function (aRequest, aResponse) { - do_print("Interruptible request started."); - - // Process the first part of the response. - aResponse.processAsync(); - aFirstPartFn(aRequest, aResponse); - - // Wait on the current deferred object, then finish the request. - _gDeferResponses.promise.then(function RIH_onSuccess() { - aSecondPartFn(aRequest, aResponse); - aResponse.finish(); - do_print("Interruptible request finished."); - }).then(null, Cu.reportError); - }); -} - -/** - * Ensure the given date object is valid. - * - * @param aDate - * The date object to be checked. This value can be null. - */ -function isValidDate(aDate) { - return aDate && aDate.getTime && !isNaN(aDate.getTime()); -} - -/** - * Position of the first byte served by the "interruptible_resumable.txt" - * handler during the most recent response. - */ -var gMostRecentFirstBytePos; - -// Initialization functions common to all tests - -add_task(function test_common_initialize() -{ - // Start the HTTP server. - gHttpServer = new HttpServer(); - gHttpServer.registerDirectory("/", do_get_file("../data")); - gHttpServer.start(-1); - do_register_cleanup(() => { - return new Promise(resolve => { - // Ensure all the pending HTTP requests have a chance to finish. - continueResponses(); - // Stop the HTTP server, calling resolve when it's done. - gHttpServer.stop(resolve); - }); - }); - - // Cache locks might prevent concurrent requests to the same resource, and - // this may block tests that use the interruptible handlers. - Services.prefs.setBoolPref("browser.cache.disk.enable", false); - Services.prefs.setBoolPref("browser.cache.memory.enable", false); - do_register_cleanup(function () { - Services.prefs.clearUserPref("browser.cache.disk.enable"); - Services.prefs.clearUserPref("browser.cache.memory.enable"); - }); - - registerInterruptibleHandler("/interruptible.txt", - function firstPart(aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.setHeader("Content-Length", "" + (TEST_DATA_SHORT.length * 2), - false); - aResponse.write(TEST_DATA_SHORT); - }, function secondPart(aRequest, aResponse) { - aResponse.write(TEST_DATA_SHORT); - }); - - registerInterruptibleHandler("/interruptible_resumable.txt", - function firstPart(aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - - // Determine if only part of the data should be sent. - let data = TEST_DATA_SHORT + TEST_DATA_SHORT; - if (aRequest.hasHeader("Range")) { - var matches = aRequest.getHeader("Range") - .match(/^\s*bytes=(\d+)?-(\d+)?\s*$/); - var firstBytePos = (matches[1] === undefined) ? 0 : matches[1]; - var lastBytePos = (matches[2] === undefined) ? data.length - 1 - : matches[2]; - if (firstBytePos >= data.length) { - aResponse.setStatusLine(aRequest.httpVersion, 416, - "Requested Range Not Satisfiable"); - aResponse.setHeader("Content-Range", "*/" + data.length, false); - aResponse.finish(); - return; - } - - aResponse.setStatusLine(aRequest.httpVersion, 206, "Partial Content"); - aResponse.setHeader("Content-Range", firstBytePos + "-" + - lastBytePos + "/" + - data.length, false); - - data = data.substring(firstBytePos, lastBytePos + 1); - - gMostRecentFirstBytePos = firstBytePos; - } else { - gMostRecentFirstBytePos = 0; - } - - aResponse.setHeader("Content-Length", "" + data.length, false); - - aResponse.write(data.substring(0, data.length / 2)); - - // Store the second part of the data on the response object, so that it - // can be used by the secondPart function. - aResponse.secondPartData = data.substring(data.length / 2); - }, function secondPart(aRequest, aResponse) { - aResponse.write(aResponse.secondPartData); - }); - - registerInterruptibleHandler("/interruptible_gzip.txt", - function firstPart(aRequest, aResponse) { - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.setHeader("Content-Encoding", "gzip", false); - aResponse.setHeader("Content-Length", "" + TEST_DATA_SHORT_GZIP_ENCODED.length); - - let bos = new BinaryOutputStream(aResponse.bodyOutputStream); - bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED_FIRST, - TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length); - }, function secondPart(aRequest, aResponse) { - let bos = new BinaryOutputStream(aResponse.bodyOutputStream); - bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED_SECOND, - TEST_DATA_SHORT_GZIP_ENCODED_SECOND.length); - }); - - gHttpServer.registerPathHandler("/shorter-than-content-length-http-1-1.txt", - function (aRequest, aResponse) { - aResponse.processAsync(); - aResponse.setStatusLine("1.1", 200, "OK"); - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.setHeader("Content-Length", "" + (TEST_DATA_SHORT.length * 2), - false); - aResponse.write(TEST_DATA_SHORT); - aResponse.finish(); - }); - - // This URL will emulate being blocked by Windows Parental controls - gHttpServer.registerPathHandler("/parentalblocked.zip", - function (aRequest, aResponse) { - aResponse.setStatusLine(aRequest.httpVersion, 450, - "Blocked by Windows Parental Controls"); - }); - - // During unit tests, most of the functions that require profile access or - // operating system features will be disabled. Individual tests may override - // them again to check for specific behaviors. - Integration.downloads.register(base => ({ - __proto__: base, - loadPublicDownloadListFromStore: () => Promise.resolve(), - shouldKeepBlockedData: () => Promise.resolve(false), - shouldBlockForParentalControls: () => Promise.resolve(false), - shouldBlockForRuntimePermissions: () => Promise.resolve(false), - shouldBlockForReputationCheck: () => Promise.resolve({ - shouldBlock: false, - verdict: "", - }), - confirmLaunchExecutable: () => Promise.resolve(), - launchFile: () => Promise.resolve(), - showContainingDirectory: () => Promise.resolve(), - // This flag allows re-enabling the default observers during their tests. - allowObservers: false, - addListObservers() { - return this.allowObservers ? super.addListObservers(...arguments) - : Promise.resolve(); - }, - // This flag allows re-enabling the download directory logic for its tests. - _allowDirectories: false, - set allowDirectories(value) { - this._allowDirectories = value; - // We have to invalidate the previously computed directory path. - this._downloadsDirectory = null; - }, - _getDirectory(name) { - return super._getDirectory(this._allowDirectories ? name : "TmpD"); - }, - })); - - // Make sure that downloads started using nsIExternalHelperAppService are - // saved to disk without asking for a destination interactively. - let mock = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]), - promptForSaveToFileAsync(aLauncher, - aWindowContext, - aDefaultFileName, - aSuggestedFileExtension, - aForcePrompt) { - // The dialog should create the empty placeholder file. - let file = getTempFile(TEST_TARGET_FILE_NAME); - file.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); - aLauncher.saveDestinationAvailable(file); - }, - }; - - let cid = MockRegistrar.register("@mozilla.org/helperapplauncherdialog;1", mock); - do_register_cleanup(() => { - MockRegistrar.unregister(cid); - }); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js b/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js deleted file mode 100644 index 6e32c63d3..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the main download interfaces using DownloadCopySaver. - */ - -"use strict"; - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadError", - "resource://gre/modules/DownloadCore.jsm"); - -// Execution of common tests - -var gUseLegacySaver = false; - -var scriptFile = do_get_file("common_test_Download.js"); -Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec); - -// Tests - -/** - * Tests the DownloadError object. - */ -add_task(function test_DownloadError() -{ - let error = new DownloadError({ result: Cr.NS_ERROR_NOT_RESUMABLE, - message: "Not resumable."}); - do_check_eq(error.result, Cr.NS_ERROR_NOT_RESUMABLE); - do_check_eq(error.message, "Not resumable."); - do_check_false(error.becauseSourceFailed); - do_check_false(error.becauseTargetFailed); - do_check_false(error.becauseBlocked); - do_check_false(error.becauseBlockedByParentalControls); - - error = new DownloadError({ message: "Unknown error."}); - do_check_eq(error.result, Cr.NS_ERROR_FAILURE); - do_check_eq(error.message, "Unknown error."); - - error = new DownloadError({ result: Cr.NS_ERROR_NOT_RESUMABLE }); - do_check_eq(error.result, Cr.NS_ERROR_NOT_RESUMABLE); - do_check_true(error.message.indexOf("Exception") > 0); - - // becauseSourceFailed will be set, but not the unknown property. - error = new DownloadError({ message: "Unknown error.", - becauseSourceFailed: true, - becauseUnknown: true }); - do_check_true(error.becauseSourceFailed); - do_check_false("becauseUnknown" in error); - - error = new DownloadError({ result: Cr.NS_ERROR_MALFORMED_URI, - inferCause: true }); - do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI); - do_check_true(error.becauseSourceFailed); - do_check_false(error.becauseTargetFailed); - do_check_false(error.becauseBlocked); - do_check_false(error.becauseBlockedByParentalControls); - - // This test does not set inferCause, so becauseSourceFailed will not be set. - error = new DownloadError({ result: Cr.NS_ERROR_MALFORMED_URI }); - do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI); - do_check_false(error.becauseSourceFailed); - - error = new DownloadError({ result: Cr.NS_ERROR_FILE_INVALID_PATH, - inferCause: true }); - do_check_eq(error.result, Cr.NS_ERROR_FILE_INVALID_PATH); - do_check_false(error.becauseSourceFailed); - do_check_true(error.becauseTargetFailed); - do_check_false(error.becauseBlocked); - do_check_false(error.becauseBlockedByParentalControls); - - error = new DownloadError({ becauseBlocked: true }); - do_check_eq(error.message, "Download blocked."); - do_check_false(error.becauseSourceFailed); - do_check_false(error.becauseTargetFailed); - do_check_true(error.becauseBlocked); - do_check_false(error.becauseBlockedByParentalControls); - - error = new DownloadError({ becauseBlockedByParentalControls: true }); - do_check_eq(error.message, "Download blocked."); - do_check_false(error.becauseSourceFailed); - do_check_false(error.becauseTargetFailed); - do_check_true(error.becauseBlocked); - do_check_true(error.becauseBlockedByParentalControls); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadImport.js b/toolkit/components/jsdownloads/test/unit/test_DownloadImport.js deleted file mode 100644 index 388870f00..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadImport.js +++ /dev/null @@ -1,701 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the DownloadImport object. - */ - -"use strict"; - -// Globals - -XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", - "resource://gre/modules/Sqlite.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport", - "resource://gre/modules/DownloadImport.jsm"); - -// Importable states -const DOWNLOAD_NOTSTARTED = -1; -const DOWNLOAD_DOWNLOADING = 0; -const DOWNLOAD_PAUSED = 4; -const DOWNLOAD_QUEUED = 5; - -// Non importable states -const DOWNLOAD_FAILED = 2; -const DOWNLOAD_CANCELED = 3; -const DOWNLOAD_BLOCKED_PARENTAL = 6; -const DOWNLOAD_SCANNING = 7; -const DOWNLOAD_DIRTY = 8; -const DOWNLOAD_BLOCKED_POLICY = 9; - -// The TEST_DATA_TAINTED const is a version of TEST_DATA_SHORT in which the -// beginning of the data was changed (with the TEST_DATA_REPLACEMENT value). -// We use this to test that the entityID is properly imported and the download -// can be resumed from where it was paused. -// For simplification purposes, the test requires that TEST_DATA_SHORT and -// TEST_DATA_TAINTED have the same length. -const TEST_DATA_REPLACEMENT = "-changed- "; -const TEST_DATA_TAINTED = TEST_DATA_REPLACEMENT + - TEST_DATA_SHORT.substr(TEST_DATA_REPLACEMENT.length); -const TEST_DATA_LENGTH = TEST_DATA_SHORT.length; - -// The length of the partial file that we'll write to disk as an existing -// ongoing download. -const TEST_DATA_PARTIAL_LENGTH = TEST_DATA_REPLACEMENT.length; - -// The value of the "maxBytes" column stored in the DB about the downloads. -// It's intentionally different than TEST_DATA_LENGTH to test that each value -// is seen when expected. -const MAXBYTES_IN_DB = TEST_DATA_LENGTH - 10; - -var gDownloadsRowToImport; -var gDownloadsRowNonImportable; - -/** - * Creates a database with an empty moz_downloads table and leaves an - * open connection to it. - * - * @param aPath - * String containing the path of the database file to be created. - * @param aSchemaVersion - * Number with the version of the database schema to set. - * - * @return {Promise} - * @resolves The open connection to the database. - * @rejects If an error occurred during the database creation. - */ -function promiseEmptyDatabaseConnection({aPath, aSchemaVersion}) { - return Task.spawn(function* () { - let connection = yield Sqlite.openConnection({ path: aPath }); - - yield connection.execute("CREATE TABLE moz_downloads (" - + "id INTEGER PRIMARY KEY," - + "name TEXT," - + "source TEXT," - + "target TEXT," - + "tempPath TEXT," - + "startTime INTEGER," - + "endTime INTEGER," - + "state INTEGER," - + "referrer TEXT," - + "entityID TEXT," - + "currBytes INTEGER NOT NULL DEFAULT 0," - + "maxBytes INTEGER NOT NULL DEFAULT -1," - + "mimeType TEXT," - + "preferredApplication TEXT," - + "preferredAction INTEGER NOT NULL DEFAULT 0," - + "autoResume INTEGER NOT NULL DEFAULT 0," - + "guid TEXT)"); - - yield connection.setSchemaVersion(aSchemaVersion); - - return connection; - }); -} - -/** - * Inserts a new entry in the database with the given columns' values. - * - * @param aConnection - * The database connection. - * @param aDownloadRow - * An object representing the values for each column of the row - * being inserted. - * - * @return {Promise} - * @resolves When the operation completes. - * @rejects If there's an error inserting the row. - */ -function promiseInsertRow(aConnection, aDownloadRow) { - // We can't use the aDownloadRow obj directly in the execute statement - // because the obj bind code in Sqlite.jsm doesn't allow objects - // with extra properties beyond those being binded. So we might as well - // use an array as it is simpler. - let values = [ - aDownloadRow.source, aDownloadRow.target, aDownloadRow.tempPath, - aDownloadRow.startTime.getTime() * 1000, aDownloadRow.state, - aDownloadRow.referrer, aDownloadRow.entityID, aDownloadRow.maxBytes, - aDownloadRow.mimeType, aDownloadRow.preferredApplication, - aDownloadRow.preferredAction, aDownloadRow.autoResume - ]; - - return aConnection.execute("INSERT INTO moz_downloads (" - + "name, source, target, tempPath, startTime," - + "endTime, state, referrer, entityID, currBytes," - + "maxBytes, mimeType, preferredApplication," - + "preferredAction, autoResume, guid)" - + "VALUES (" - + "'', ?, ?, ?, ?, " // name, - + "0, ?, ?, ?, 0, " // endTime, currBytes - + " ?, ?, ?, " // - + " ?, ?, '')", // and guid are not imported - values); -} - -/** - * Retrieves the number of rows in the moz_downloads table of the - * database. - * - * @param aConnection - * The database connection. - * - * @return {Promise} - * @resolves With the number of rows. - * @rejects Never. - */ -function promiseTableCount(aConnection) { - return aConnection.execute("SELECT COUNT(*) FROM moz_downloads") - .then(res => res[0].getResultByName("COUNT(*)")) - .then(null, Cu.reportError); -} - -/** - * Briefly opens a network channel to a given URL to retrieve - * the entityID of this url, as generated by the network code. - * - * @param aUrl - * The URL to retrieve the entityID. - * - * @return {Promise} - * @resolves The EntityID of the given URL. - * @rejects When there's a problem accessing the URL. - */ -function promiseEntityID(aUrl) { - let deferred = Promise.defer(); - let entityID = ""; - let channel = NetUtil.newChannel({ - uri: NetUtil.newURI(aUrl), - loadUsingSystemPrincipal: true - }); - - channel.asyncOpen2({ - onStartRequest: function (aRequest) { - if (aRequest instanceof Ci.nsIResumableChannel) { - entityID = aRequest.entityID; - } - aRequest.cancel(Cr.NS_BINDING_ABORTED); - }, - - onStopRequest: function (aRequest, aContext, aStatusCode) { - if (aStatusCode == Cr.NS_BINDING_ABORTED) { - deferred.resolve(entityID); - } else { - deferred.reject("Unexpected status code received"); - } - }, - - onDataAvailable: function () {} - }); - - return deferred.promise; -} - -/** - * Gets a file path to a temporary writeable download target, in the - * correct format as expected to be stored in the downloads database, - * which is file:///absolute/path/to/file - * - * @param aLeafName - * A hint leaf name for the file. - * - * @return String The path to the download target. - */ -function getDownloadTarget(aLeafName) { - return NetUtil.newURI(getTempFile(aLeafName)).spec; -} - -/** - * Generates a temporary partial file to use as an in-progress - * download. The file is written to disk with a part of the total expected - * download content pre-written. - * - * @param aLeafName - * A hint leaf name for the file. - * @param aTainted - * A boolean value. When true, the partial content of the file - * will be different from the expected content of the original source - * file. See the declaration of TEST_DATA_TAINTED for more information. - * - * @return {Promise} - * @resolves When the operation completes, and returns a string with the path - * to the generated file. - * @rejects If there's an error writing the file. - */ -function getPartialFile(aLeafName, aTainted = false) { - let tempDownload = getTempFile(aLeafName); - let partialContent = aTainted - ? TEST_DATA_TAINTED.substr(0, TEST_DATA_PARTIAL_LENGTH) - : TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH); - - return OS.File.writeAtomic(tempDownload.path, partialContent, - { tmpPath: tempDownload.path + ".tmp", - flush: true }) - .then(() => tempDownload.path); -} - -/** - * Generates a Date object to be used as the startTime for the download rows - * in the DB. A date that is obviously different from the current time is - * generated to make sure this stored data and a `new Date()` can't collide. - * - * @param aOffset - * A offset from the base generated date is used to differentiate each - * row in the database. - * - * @return A Date object. - */ -function getStartTime(aOffset) { - return new Date(1000000 + (aOffset * 10000)); -} - -/** - * Performs various checks on an imported Download object to make sure - * all properties are properly set as expected from the import procedure. - * - * @param aDownload - * The Download object to be checked. - * @param aDownloadRow - * An object that represents a row from the original database table, - * with extra properties describing expected values that are not - * explictly part of the database. - * - * @return {Promise} - * @resolves When the operation completes - * @rejects Never - */ -function checkDownload(aDownload, aDownloadRow) { - return Task.spawn(function*() { - do_check_eq(aDownload.source.url, aDownloadRow.source); - do_check_eq(aDownload.source.referrer, aDownloadRow.referrer); - - do_check_eq(aDownload.target.path, - NetUtil.newURI(aDownloadRow.target) - .QueryInterface(Ci.nsIFileURL).file.path); - - do_check_eq(aDownload.target.partFilePath, aDownloadRow.tempPath); - - if (aDownloadRow.expectedResume) { - do_check_true(!aDownload.stopped || aDownload.succeeded); - yield promiseDownloadStopped(aDownload); - - do_check_true(aDownload.succeeded); - do_check_eq(aDownload.progress, 100); - // If the download has resumed, a new startTime will be set. - // By calling toJSON we're also testing that startTime is a Date object. - do_check_neq(aDownload.startTime.toJSON(), - aDownloadRow.startTime.toJSON()); - } else { - do_check_false(aDownload.succeeded); - do_check_eq(aDownload.startTime.toJSON(), - aDownloadRow.startTime.toJSON()); - } - - do_check_eq(aDownload.stopped, true); - - let serializedSaver = aDownload.saver.toSerializable(); - if (typeof(serializedSaver) == "object") { - do_check_eq(serializedSaver.type, "copy"); - } else { - do_check_eq(serializedSaver, "copy"); - } - - if (aDownloadRow.entityID) { - do_check_eq(aDownload.saver.entityID, aDownloadRow.entityID); - } - - do_check_eq(aDownload.currentBytes, aDownloadRow.expectedCurrentBytes); - do_check_eq(aDownload.totalBytes, aDownloadRow.expectedTotalBytes); - - if (aDownloadRow.expectedContent) { - let fileToCheck = aDownloadRow.expectedResume - ? aDownload.target.path - : aDownload.target.partFilePath; - yield promiseVerifyContents(fileToCheck, aDownloadRow.expectedContent); - } - - do_check_eq(aDownload.contentType, aDownloadRow.expectedContentType); - do_check_eq(aDownload.launcherPath, aDownloadRow.preferredApplication); - - do_check_eq(aDownload.launchWhenSucceeded, - aDownloadRow.preferredAction != Ci.nsIMIMEInfo.saveToDisk); - }); -} - -// Preparation tasks - -/** - * Prepares the list of downloads to be added to the database that should - * be imported by the import procedure. - */ -add_task(function* prepareDownloadsToImport() { - - let sourceUrl = httpUrl("source.txt"); - let sourceEntityId = yield promiseEntityID(sourceUrl); - - gDownloadsRowToImport = [ - // Paused download with autoResume and a partial file. By - // setting the correct entityID the download can resume from - // where it stopped, and to test that this works properly we - // intentionally set different data in the beginning of the - // partial file to make sure it was not replaced. - { - source: sourceUrl, - target: getDownloadTarget("inprogress1.txt"), - tempPath: yield getPartialFile("inprogress1.txt.part", true), - startTime: getStartTime(1), - state: DOWNLOAD_PAUSED, - referrer: httpUrl("referrer1"), - entityID: sourceEntityId, - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType1", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication1", - autoResume: 1, - - // Even though the information stored in the DB said - // maxBytes was MAXBYTES_IN_DB, the download turned out to be - // a different length. Here we make sure the totalBytes property - // was correctly set with the actual value. The same consideration - // applies to the contentType. - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_TAINTED, - }, - - // Paused download with autoResume and a partial file, - // but missing entityID. This means that the download will - // start from beginning, and the entire original content of the - // source file should replace the different data that was stored - // in the partial file. - { - source: sourceUrl, - target: getDownloadTarget("inprogress2.txt"), - tempPath: yield getPartialFile("inprogress2.txt.part", true), - startTime: getStartTime(2), - state: DOWNLOAD_PAUSED, - referrer: httpUrl("referrer2"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType2", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication2", - autoResume: 1, - - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_SHORT - }, - - // Paused download with no autoResume and a partial file. - { - source: sourceUrl, - target: getDownloadTarget("inprogress3.txt"), - tempPath: yield getPartialFile("inprogress3.txt.part"), - startTime: getStartTime(3), - state: DOWNLOAD_PAUSED, - referrer: httpUrl("referrer3"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType3", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication3", - autoResume: 0, - - // Since this download has not been resumed, the actual data - // about its total size and content type is not known. - // Therefore, we're going by the information imported from the DB. - expectedCurrentBytes: TEST_DATA_PARTIAL_LENGTH, - expectedTotalBytes: MAXBYTES_IN_DB, - expectedResume: false, - expectedContentType: "mimeType3", - expectedContent: TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH), - }, - - // Paused download with autoResume and no partial file. - { - source: sourceUrl, - target: getDownloadTarget("inprogress4.txt"), - tempPath: "", - startTime: getStartTime(4), - state: DOWNLOAD_PAUSED, - referrer: httpUrl("referrer4"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "text/plain", - preferredAction: Ci.nsIMIMEInfo.useHelperApp, - preferredApplication: "prerredApplication4", - autoResume: 1, - - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_SHORT - }, - - // Paused download with no autoResume and no partial file. - { - source: sourceUrl, - target: getDownloadTarget("inprogress5.txt"), - tempPath: "", - startTime: getStartTime(5), - state: DOWNLOAD_PAUSED, - referrer: httpUrl("referrer4"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "text/plain", - preferredAction: Ci.nsIMIMEInfo.useSystemDefault, - preferredApplication: "prerredApplication5", - autoResume: 0, - - expectedCurrentBytes: 0, - expectedTotalBytes: MAXBYTES_IN_DB, - expectedResume: false, - expectedContentType: "text/plain", - }, - - // Queued download with no autoResume and no partial file. - // Even though autoResume=0, queued downloads always autoResume. - { - source: sourceUrl, - target: getDownloadTarget("inprogress6.txt"), - tempPath: "", - startTime: getStartTime(6), - state: DOWNLOAD_QUEUED, - referrer: httpUrl("referrer6"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "text/plain", - preferredAction: Ci.nsIMIMEInfo.useHelperApp, - preferredApplication: "prerredApplication6", - autoResume: 0, - - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_SHORT - }, - - // Notstarted download with no autoResume and no partial file. - // Even though autoResume=0, notstarted downloads always autoResume. - { - source: sourceUrl, - target: getDownloadTarget("inprogress7.txt"), - tempPath: "", - startTime: getStartTime(7), - state: DOWNLOAD_NOTSTARTED, - referrer: httpUrl("referrer7"), - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "text/plain", - preferredAction: Ci.nsIMIMEInfo.useHelperApp, - preferredApplication: "prerredApplication7", - autoResume: 0, - - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_SHORT - }, - - // Downloading download with no autoResume and a partial file. - // Even though autoResume=0, downloading downloads always autoResume. - { - source: sourceUrl, - target: getDownloadTarget("inprogress8.txt"), - tempPath: yield getPartialFile("inprogress8.txt.part", true), - startTime: getStartTime(8), - state: DOWNLOAD_DOWNLOADING, - referrer: httpUrl("referrer8"), - entityID: sourceEntityId, - maxBytes: MAXBYTES_IN_DB, - mimeType: "text/plain", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication8", - autoResume: 0, - - expectedCurrentBytes: TEST_DATA_LENGTH, - expectedTotalBytes: TEST_DATA_LENGTH, - expectedResume: true, - expectedContentType: "text/plain", - expectedContent: TEST_DATA_TAINTED - }, - ]; -}); - -/** - * Prepares the list of downloads to be added to the database that should - * *not* be imported by the import procedure. - */ -add_task(function* prepareNonImportableDownloads() -{ - gDownloadsRowNonImportable = [ - // Download with no source (should never happen in normal circumstances). - { - source: "", - target: "nonimportable1.txt", - tempPath: "", - startTime: getStartTime(1), - state: DOWNLOAD_PAUSED, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType1", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication1", - autoResume: 1 - }, - - // state = DOWNLOAD_FAILED - { - source: httpUrl("source.txt"), - target: "nonimportable2.txt", - tempPath: "", - startTime: getStartTime(2), - state: DOWNLOAD_FAILED, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType2", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication2", - autoResume: 1 - }, - - // state = DOWNLOAD_CANCELED - { - source: httpUrl("source.txt"), - target: "nonimportable3.txt", - tempPath: "", - startTime: getStartTime(3), - state: DOWNLOAD_CANCELED, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType3", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication3", - autoResume: 1 - }, - - // state = DOWNLOAD_BLOCKED_PARENTAL - { - source: httpUrl("source.txt"), - target: "nonimportable4.txt", - tempPath: "", - startTime: getStartTime(4), - state: DOWNLOAD_BLOCKED_PARENTAL, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType4", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication4", - autoResume: 1 - }, - - // state = DOWNLOAD_SCANNING - { - source: httpUrl("source.txt"), - target: "nonimportable5.txt", - tempPath: "", - startTime: getStartTime(5), - state: DOWNLOAD_SCANNING, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType5", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication5", - autoResume: 1 - }, - - // state = DOWNLOAD_DIRTY - { - source: httpUrl("source.txt"), - target: "nonimportable6.txt", - tempPath: "", - startTime: getStartTime(6), - state: DOWNLOAD_DIRTY, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType6", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication6", - autoResume: 1 - }, - - // state = DOWNLOAD_BLOCKED_POLICY - { - source: httpUrl("source.txt"), - target: "nonimportable7.txt", - tempPath: "", - startTime: getStartTime(7), - state: DOWNLOAD_BLOCKED_POLICY, - referrer: "", - entityID: "", - maxBytes: MAXBYTES_IN_DB, - mimeType: "mimeType7", - preferredAction: Ci.nsIMIMEInfo.saveToDisk, - preferredApplication: "prerredApplication7", - autoResume: 1 - }, - ]; -}); - -// Test - -/** - * Creates a temporary Sqlite database with download data and perform an - * import of that data to the new Downloads API to verify that the import - * worked correctly. - */ -add_task(function* test_downloadImport() -{ - let connection = null; - let downloadsSqlite = getTempFile("downloads.sqlite").path; - - try { - // Set up the database. - connection = yield promiseEmptyDatabaseConnection({ - aPath: downloadsSqlite, - aSchemaVersion: 9 - }); - - // Insert both the importable and non-importable - // downloads together. - for (let downloadRow of gDownloadsRowToImport) { - yield promiseInsertRow(connection, downloadRow); - } - - for (let downloadRow of gDownloadsRowNonImportable) { - yield promiseInsertRow(connection, downloadRow); - } - - // Check that every item was inserted. - do_check_eq((yield promiseTableCount(connection)), - gDownloadsRowToImport.length + - gDownloadsRowNonImportable.length); - } finally { - // Close the connection so that DownloadImport can open it. - yield connection.close(); - } - - // Import items. - let list = yield promiseNewList(false); - yield new DownloadImport(list, downloadsSqlite).import(); - let items = yield list.getAll(); - - do_check_eq(items.length, gDownloadsRowToImport.length); - - for (let i = 0; i < gDownloadsRowToImport.length; i++) { - yield checkDownload(items[i], gDownloadsRowToImport[i]); - } -}) diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js b/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js deleted file mode 100644 index 31dd7c7a4..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js +++ /dev/null @@ -1,432 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the DownloadIntegration object. - */ - -"use strict"; - -// Globals - -/** - * Notifies the prompt observers and verify the expected downloads count. - * - * @param aIsPrivate - * Flag to know is test private observers. - * @param aExpectedCount - * the expected downloads count for quit and offline observers. - * @param aExpectedPBCount - * the expected downloads count for private browsing observer. - */ -function notifyPromptObservers(aIsPrivate, aExpectedCount, aExpectedPBCount) { - let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]. - createInstance(Ci.nsISupportsPRBool); - - // Notify quit application requested observer. - DownloadIntegration._testPromptDownloads = -1; - Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); - do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedCount); - - // Notify offline requested observer. - DownloadIntegration._testPromptDownloads = -1; - Services.obs.notifyObservers(cancelQuit, "offline-requested", null); - do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedCount); - - if (aIsPrivate) { - // Notify last private browsing requested observer. - DownloadIntegration._testPromptDownloads = -1; - Services.obs.notifyObservers(cancelQuit, "last-pb-context-exiting", null); - do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedPBCount); - } - - delete DownloadIntegration._testPromptDownloads; -} - -// Tests - -/** - * Allows re-enabling the real download directory logic during one test. - */ -function allowDirectoriesInTest() { - DownloadIntegration.allowDirectories = true; - function cleanup() { - DownloadIntegration.allowDirectories = false; - } - do_register_cleanup(cleanup); - return cleanup; -} - -XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { - return Services.strings. - createBundle("chrome://mozapps/locale/downloads/downloads.properties"); -}); - -/** - * Tests that getSystemDownloadsDirectory returns an existing directory or - * creates a new directory depending on the platform. Instead of the real - * directory, this test is executed in the temporary directory so we can safely - * delete the created folder to check whether it is created again. - */ -add_task(function* test_getSystemDownloadsDirectory_exists_or_creates() -{ - let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); - let downloadDir; - - // OSX / Linux / Windows but not XP/2k - if (Services.appinfo.OS == "Darwin" || - Services.appinfo.OS == "Linux" || - (Services.appinfo.OS == "WINNT" && - parseFloat(Services.sysinfo.getProperty("version")) >= 6)) { - downloadDir = yield DownloadIntegration.getSystemDownloadsDirectory(); - do_check_eq(downloadDir, tempDir.path); - do_check_true(yield OS.File.exists(downloadDir)); - - let info = yield OS.File.stat(downloadDir); - do_check_true(info.isDir); - } else { - let targetPath = OS.Path.join(tempDir.path, - gStringBundle.GetStringFromName("downloadsFolder")); - try { - yield OS.File.removeEmptyDir(targetPath); - } catch (e) {} - downloadDir = yield DownloadIntegration.getSystemDownloadsDirectory(); - do_check_eq(downloadDir, targetPath); - do_check_true(yield OS.File.exists(downloadDir)); - - let info = yield OS.File.stat(downloadDir); - do_check_true(info.isDir); - yield OS.File.removeEmptyDir(targetPath); - } -}); - -/** - * Tests that the real directory returned by getSystemDownloadsDirectory is not - * the one that is used during unit tests. Since this is the actual downloads - * directory of the operating system, we don't try to delete it afterwards. - */ -add_task(function* test_getSystemDownloadsDirectory_real() -{ - let fakeDownloadDir = yield DownloadIntegration.getSystemDownloadsDirectory(); - - let cleanup = allowDirectoriesInTest(); - let realDownloadDir = yield DownloadIntegration.getSystemDownloadsDirectory(); - cleanup(); - - do_check_neq(fakeDownloadDir, realDownloadDir); -}); - -/** - * Tests that the getPreferredDownloadsDirectory returns a valid download - * directory string path. - */ -add_task(function* test_getPreferredDownloadsDirectory() -{ - let cleanupDirectories = allowDirectoriesInTest(); - - let folderListPrefName = "browser.download.folderList"; - let dirPrefName = "browser.download.dir"; - function cleanupPrefs() { - Services.prefs.clearUserPref(folderListPrefName); - Services.prefs.clearUserPref(dirPrefName); - } - do_register_cleanup(cleanupPrefs); - - // Should return the system downloads directory. - Services.prefs.setIntPref(folderListPrefName, 1); - let systemDir = yield DownloadIntegration.getSystemDownloadsDirectory(); - let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_neq(downloadDir, ""); - do_check_eq(downloadDir, systemDir); - - // Should return the desktop directory. - Services.prefs.setIntPref(folderListPrefName, 0); - downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_neq(downloadDir, ""); - do_check_eq(downloadDir, Services.dirsvc.get("Desk", Ci.nsIFile).path); - - // Should return the system downloads directory because the dir preference - // is not set. - Services.prefs.setIntPref(folderListPrefName, 2); - downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_neq(downloadDir, ""); - do_check_eq(downloadDir, systemDir); - - // Should return the directory which is listed in the dir preference. - let time = (new Date()).getTime(); - let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); - tempDir.append(time); - Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); - downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_neq(downloadDir, ""); - do_check_eq(downloadDir, tempDir.path); - do_check_true(yield OS.File.exists(downloadDir)); - yield OS.File.removeEmptyDir(tempDir.path); - - // Should return the system downloads directory beacause the path is invalid - // in the dir preference. - tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); - tempDir.append("dir_not_exist"); - tempDir.append(time); - Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); - downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_eq(downloadDir, systemDir); - - // Should return the system downloads directory because the folderList - // preference is invalid - Services.prefs.setIntPref(folderListPrefName, 999); - downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_eq(downloadDir, systemDir); - - cleanupPrefs(); - cleanupDirectories(); -}); - -/** - * Tests that the getTemporaryDownloadsDirectory returns a valid download - * directory string path. - */ -add_task(function* test_getTemporaryDownloadsDirectory() -{ - let cleanup = allowDirectoriesInTest(); - - let downloadDir = yield DownloadIntegration.getTemporaryDownloadsDirectory(); - do_check_neq(downloadDir, ""); - - if ("nsILocalFileMac" in Ci) { - let preferredDownloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory(); - do_check_eq(downloadDir, preferredDownloadDir); - } else { - let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); - do_check_eq(downloadDir, tempDir.path); - } - - cleanup(); -}); - -// Tests DownloadObserver - -/** - * Re-enables the default observers for the following tests. - * - * This takes effect the first time a DownloadList object is created, and lasts - * until this test file has completed. - */ -add_task(function* test_observers_setup() -{ - DownloadIntegration.allowObservers = true; - do_register_cleanup(function () { - DownloadIntegration.allowObservers = false; - }); -}); - -/** - * Tests notifications prompts when observers are notified if there are public - * and private active downloads. - */ -add_task(function* test_notifications() -{ - for (let isPrivate of [false, true]) { - mustInterruptResponses(); - - let list = yield promiseNewList(isPrivate); - let download1 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let download2 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let download3 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let promiseAttempt1 = download1.start(); - let promiseAttempt2 = download2.start(); - download3.start().catch(() => {}); - - // Add downloads to list. - yield list.add(download1); - yield list.add(download2); - yield list.add(download3); - // Cancel third download - yield download3.cancel(); - - notifyPromptObservers(isPrivate, 2, 2); - - // Allow the downloads to complete. - continueResponses(); - yield promiseAttempt1; - yield promiseAttempt2; - - // Clean up. - yield list.remove(download1); - yield list.remove(download2); - yield list.remove(download3); - } -}); - -/** - * Tests that notifications prompts observers are not notified if there are no - * public or private active downloads. - */ -add_task(function* test_no_notifications() -{ - for (let isPrivate of [false, true]) { - let list = yield promiseNewList(isPrivate); - let download1 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let download2 = yield promiseNewDownload(httpUrl("interruptible.txt")); - download1.start().catch(() => {}); - download2.start().catch(() => {}); - - // Add downloads to list. - yield list.add(download1); - yield list.add(download2); - - yield download1.cancel(); - yield download2.cancel(); - - notifyPromptObservers(isPrivate, 0, 0); - - // Clean up. - yield list.remove(download1); - yield list.remove(download2); - } -}); - -/** - * Tests notifications prompts when observers are notified if there are public - * and private active downloads at the same time. - */ -add_task(function* test_mix_notifications() -{ - mustInterruptResponses(); - - let publicList = yield promiseNewList(); - let privateList = yield Downloads.getList(Downloads.PRIVATE); - let download1 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let download2 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let promiseAttempt1 = download1.start(); - let promiseAttempt2 = download2.start(); - - // Add downloads to lists. - yield publicList.add(download1); - yield privateList.add(download2); - - notifyPromptObservers(true, 2, 1); - - // Allow the downloads to complete. - continueResponses(); - yield promiseAttempt1; - yield promiseAttempt2; - - // Clean up. - yield publicList.remove(download1); - yield privateList.remove(download2); -}); - -/** - * Tests suspending and resuming as well as going offline and then online again. - * The downloads should stop when suspending and start again when resuming. - */ -add_task(function* test_suspend_resume() -{ - // The default wake delay is 10 seconds, so set the wake delay to be much - // faster for these tests. - Services.prefs.setIntPref("browser.download.manager.resumeOnWakeDelay", 5); - - let addDownload = function(list) - { - return Task.spawn(function* () { - let download = yield promiseNewDownload(httpUrl("interruptible.txt")); - download.start().catch(() => {}); - list.add(download); - return download; - }); - } - - let publicList = yield promiseNewList(); - let privateList = yield promiseNewList(true); - - let download1 = yield addDownload(publicList); - let download2 = yield addDownload(publicList); - let download3 = yield addDownload(privateList); - let download4 = yield addDownload(privateList); - let download5 = yield addDownload(publicList); - - // First, check that the downloads are all canceled when going to sleep. - Services.obs.notifyObservers(null, "sleep_notification", null); - do_check_true(download1.canceled); - do_check_true(download2.canceled); - do_check_true(download3.canceled); - do_check_true(download4.canceled); - do_check_true(download5.canceled); - - // Remove a download. It should not be started again. - publicList.remove(download5); - do_check_true(download5.canceled); - - // When waking up again, the downloads start again after the wake delay. To be - // more robust, don't check after a delay but instead just wait for the - // downloads to finish. - Services.obs.notifyObservers(null, "wake_notification", null); - yield download1.whenSucceeded(); - yield download2.whenSucceeded(); - yield download3.whenSucceeded(); - yield download4.whenSucceeded(); - - // Downloads should no longer be canceled. However, as download5 was removed - // from the public list, it will not be restarted. - do_check_false(download1.canceled); - do_check_true(download5.canceled); - - // Create four new downloads and check for going offline and then online again. - - download1 = yield addDownload(publicList); - download2 = yield addDownload(publicList); - download3 = yield addDownload(privateList); - download4 = yield addDownload(privateList); - - // Going offline should cancel the downloads. - Services.obs.notifyObservers(null, "network:offline-about-to-go-offline", null); - do_check_true(download1.canceled); - do_check_true(download2.canceled); - do_check_true(download3.canceled); - do_check_true(download4.canceled); - - // Going back online should start the downloads again. - Services.obs.notifyObservers(null, "network:offline-status-changed", "online"); - yield download1.whenSucceeded(); - yield download2.whenSucceeded(); - yield download3.whenSucceeded(); - yield download4.whenSucceeded(); - - Services.prefs.clearUserPref("browser.download.manager.resumeOnWakeDelay"); -}); - -/** - * Tests both the downloads list and the in-progress downloads are clear when - * private browsing observer is notified. - */ -add_task(function* test_exit_private_browsing() -{ - mustInterruptResponses(); - - let privateList = yield promiseNewList(true); - let download1 = yield promiseNewDownload(httpUrl("source.txt")); - let download2 = yield promiseNewDownload(httpUrl("interruptible.txt")); - let promiseAttempt1 = download1.start(); - download2.start(); - - // Add downloads to list. - yield privateList.add(download1); - yield privateList.add(download2); - - // Complete the download. - yield promiseAttempt1; - - do_check_eq((yield privateList.getAll()).length, 2); - - // Simulate exiting the private browsing. - yield new Promise(resolve => { - DownloadIntegration._testResolveClearPrivateList = resolve; - Services.obs.notifyObservers(null, "last-pb-context-exited", null); - }); - delete DownloadIntegration._testResolveClearPrivateList; - - do_check_eq((yield privateList.getAll()).length, 0); - - continueResponses(); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js b/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js deleted file mode 100644 index dc6c18623..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js +++ /dev/null @@ -1,17 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the integration with legacy interfaces for downloads. - */ - -"use strict"; - -// Execution of common tests - -var gUseLegacySaver = true; - -var scriptFile = do_get_file("common_test_Download.js"); -Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js b/toolkit/components/jsdownloads/test/unit/test_DownloadList.js deleted file mode 100644 index 71e880741..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js +++ /dev/null @@ -1,564 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the DownloadList object. - */ - -"use strict"; - -// Globals - -/** - * Returns a PRTime in the past usable to add expirable visits. - * - * @note Expiration ignores any visit added in the last 7 days, but it's - * better be safe against DST issues, by going back one day more. - */ -function getExpirablePRTime() -{ - let dateObj = new Date(); - // Normalize to midnight - dateObj.setHours(0); - dateObj.setMinutes(0); - dateObj.setSeconds(0); - dateObj.setMilliseconds(0); - dateObj = new Date(dateObj.getTime() - 8 * 86400000); - return dateObj.getTime() * 1000; -} - -/** - * Adds an expirable history visit for a download. - * - * @param aSourceUrl - * String containing the URI for the download source, or null to use - * httpUrl("source.txt"). - * - * @return {Promise} - * @rejects JavaScript exception. - */ -function promiseExpirableDownloadVisit(aSourceUrl) -{ - let deferred = Promise.defer(); - PlacesUtils.asyncHistory.updatePlaces( - { - uri: NetUtil.newURI(aSourceUrl || httpUrl("source.txt")), - visits: [{ - transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD, - visitDate: getExpirablePRTime(), - }] - }, - { - handleError: function handleError(aResultCode, aPlaceInfo) { - let ex = new Components.Exception("Unexpected error in adding visits.", - aResultCode); - deferred.reject(ex); - }, - handleResult: function () {}, - handleCompletion: function handleCompletion() { - deferred.resolve(); - } - }); - return deferred.promise; -} - -// Tests - -/** - * Checks the testing mechanism used to build different download lists. - */ -add_task(function* test_construction() -{ - let downloadListOne = yield promiseNewList(); - let downloadListTwo = yield promiseNewList(); - let privateDownloadListOne = yield promiseNewList(true); - let privateDownloadListTwo = yield promiseNewList(true); - - do_check_neq(downloadListOne, downloadListTwo); - do_check_neq(privateDownloadListOne, privateDownloadListTwo); - do_check_neq(downloadListOne, privateDownloadListOne); -}); - -/** - * Checks the methods to add and retrieve items from the list. - */ -add_task(function* test_add_getAll() -{ - let list = yield promiseNewList(); - - let downloadOne = yield promiseNewDownload(); - yield list.add(downloadOne); - - let itemsOne = yield list.getAll(); - do_check_eq(itemsOne.length, 1); - do_check_eq(itemsOne[0], downloadOne); - - let downloadTwo = yield promiseNewDownload(); - yield list.add(downloadTwo); - - let itemsTwo = yield list.getAll(); - do_check_eq(itemsTwo.length, 2); - do_check_eq(itemsTwo[0], downloadOne); - do_check_eq(itemsTwo[1], downloadTwo); - - // The first snapshot should not have been modified. - do_check_eq(itemsOne.length, 1); -}); - -/** - * Checks the method to remove items from the list. - */ -add_task(function* test_remove() -{ - let list = yield promiseNewList(); - - yield list.add(yield promiseNewDownload()); - yield list.add(yield promiseNewDownload()); - - let items = yield list.getAll(); - yield list.remove(items[0]); - - // Removing an item that was never added should not raise an error. - yield list.remove(yield promiseNewDownload()); - - items = yield list.getAll(); - do_check_eq(items.length, 1); -}); - -/** - * Tests that the "add", "remove", and "getAll" methods on the global - * DownloadCombinedList object combine the contents of the global DownloadList - * objects for public and private downloads. - */ -add_task(function* test_DownloadCombinedList_add_remove_getAll() -{ - let publicList = yield promiseNewList(); - let privateList = yield Downloads.getList(Downloads.PRIVATE); - let combinedList = yield Downloads.getList(Downloads.ALL); - - let publicDownload = yield promiseNewDownload(); - let privateDownload = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt"), isPrivate: true }, - target: getTempFile(TEST_TARGET_FILE_NAME).path, - }); - - yield publicList.add(publicDownload); - yield privateList.add(privateDownload); - - do_check_eq((yield combinedList.getAll()).length, 2); - - yield combinedList.remove(publicDownload); - yield combinedList.remove(privateDownload); - - do_check_eq((yield combinedList.getAll()).length, 0); - - yield combinedList.add(publicDownload); - yield combinedList.add(privateDownload); - - do_check_eq((yield publicList.getAll()).length, 1); - do_check_eq((yield privateList.getAll()).length, 1); - do_check_eq((yield combinedList.getAll()).length, 2); - - yield publicList.remove(publicDownload); - yield privateList.remove(privateDownload); - - do_check_eq((yield combinedList.getAll()).length, 0); -}); - -/** - * Checks that views receive the download add and remove notifications, and that - * adding and removing views works as expected, both for a normal and a combined - * list. - */ -add_task(function* test_notifications_add_remove() -{ - for (let isCombined of [false, true]) { - // Force creating a new list for both the public and combined cases. - let list = yield promiseNewList(); - if (isCombined) { - list = yield Downloads.getList(Downloads.ALL); - } - - let downloadOne = yield promiseNewDownload(); - let downloadTwo = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt"), isPrivate: true }, - target: getTempFile(TEST_TARGET_FILE_NAME).path, - }); - yield list.add(downloadOne); - yield list.add(downloadTwo); - - // Check that we receive add notifications for existing elements. - let addNotifications = 0; - let viewOne = { - onDownloadAdded: function (aDownload) { - // The first download to be notified should be the first that was added. - if (addNotifications == 0) { - do_check_eq(aDownload, downloadOne); - } else if (addNotifications == 1) { - do_check_eq(aDownload, downloadTwo); - } - addNotifications++; - }, - }; - yield list.addView(viewOne); - do_check_eq(addNotifications, 2); - - // Check that we receive add notifications for new elements. - yield list.add(yield promiseNewDownload()); - do_check_eq(addNotifications, 3); - - // Check that we receive remove notifications. - let removeNotifications = 0; - let viewTwo = { - onDownloadRemoved: function (aDownload) { - do_check_eq(aDownload, downloadOne); - removeNotifications++; - }, - }; - yield list.addView(viewTwo); - yield list.remove(downloadOne); - do_check_eq(removeNotifications, 1); - - // We should not receive remove notifications after the view is removed. - yield list.removeView(viewTwo); - yield list.remove(downloadTwo); - do_check_eq(removeNotifications, 1); - - // We should not receive add notifications after the view is removed. - yield list.removeView(viewOne); - yield list.add(yield promiseNewDownload()); - do_check_eq(addNotifications, 3); - } -}); - -/** - * Checks that views receive the download change notifications, both for a - * normal and a combined list. - */ -add_task(function* test_notifications_change() -{ - for (let isCombined of [false, true]) { - // Force creating a new list for both the public and combined cases. - let list = yield promiseNewList(); - if (isCombined) { - list = yield Downloads.getList(Downloads.ALL); - } - - let downloadOne = yield promiseNewDownload(); - let downloadTwo = yield Downloads.createDownload({ - source: { url: httpUrl("source.txt"), isPrivate: true }, - target: getTempFile(TEST_TARGET_FILE_NAME).path, - }); - yield list.add(downloadOne); - yield list.add(downloadTwo); - - // Check that we receive change notifications. - let receivedOnDownloadChanged = false; - yield list.addView({ - onDownloadChanged: function (aDownload) { - do_check_eq(aDownload, downloadOne); - receivedOnDownloadChanged = true; - }, - }); - yield downloadOne.start(); - do_check_true(receivedOnDownloadChanged); - - // We should not receive change notifications after a download is removed. - receivedOnDownloadChanged = false; - yield list.remove(downloadTwo); - yield downloadTwo.start(); - do_check_false(receivedOnDownloadChanged); - } -}); - -/** - * Checks that the reference to "this" is correct in the view callbacks. - */ -add_task(function* test_notifications_this() -{ - let list = yield promiseNewList(); - - // Check that we receive change notifications. - let receivedOnDownloadAdded = false; - let receivedOnDownloadChanged = false; - let receivedOnDownloadRemoved = false; - let view = { - onDownloadAdded: function () { - do_check_eq(this, view); - receivedOnDownloadAdded = true; - }, - onDownloadChanged: function () { - // Only do this check once. - if (!receivedOnDownloadChanged) { - do_check_eq(this, view); - receivedOnDownloadChanged = true; - } - }, - onDownloadRemoved: function () { - do_check_eq(this, view); - receivedOnDownloadRemoved = true; - }, - }; - yield list.addView(view); - - let download = yield promiseNewDownload(); - yield list.add(download); - yield download.start(); - yield list.remove(download); - - // Verify that we executed the checks. - do_check_true(receivedOnDownloadAdded); - do_check_true(receivedOnDownloadChanged); - do_check_true(receivedOnDownloadRemoved); -}); - -/** - * Checks that download is removed on history expiration. - */ -add_task(function* test_history_expiration() -{ - mustInterruptResponses(); - - function cleanup() { - Services.prefs.clearUserPref("places.history.expiration.max_pages"); - } - do_register_cleanup(cleanup); - - // Set max pages to 0 to make the download expire. - Services.prefs.setIntPref("places.history.expiration.max_pages", 0); - - let list = yield promiseNewList(); - let downloadOne = yield promiseNewDownload(); - let downloadTwo = yield promiseNewDownload(httpUrl("interruptible.txt")); - - let deferred = Promise.defer(); - let removeNotifications = 0; - let downloadView = { - onDownloadRemoved: function (aDownload) { - if (++removeNotifications == 2) { - deferred.resolve(); - } - }, - }; - yield list.addView(downloadView); - - // Work with one finished download and one canceled download. - yield downloadOne.start(); - downloadTwo.start().catch(() => {}); - yield downloadTwo.cancel(); - - // We must replace the visits added while executing the downloads with visits - // that are older than 7 days, otherwise they will not be expired. - yield PlacesTestUtils.clearHistory(); - yield promiseExpirableDownloadVisit(); - yield promiseExpirableDownloadVisit(httpUrl("interruptible.txt")); - - // After clearing history, we can add the downloads to be removed to the list. - yield list.add(downloadOne); - yield list.add(downloadTwo); - - // Force a history expiration. - Cc["@mozilla.org/places/expiration;1"] - .getService(Ci.nsIObserver).observe(null, "places-debug-start-expiration", -1); - - // Wait for both downloads to be removed. - yield deferred.promise; - - cleanup(); -}); - -/** - * Checks all downloads are removed after clearing history. - */ -add_task(function* test_history_clear() -{ - let list = yield promiseNewList(); - let downloadOne = yield promiseNewDownload(); - let downloadTwo = yield promiseNewDownload(); - yield list.add(downloadOne); - yield list.add(downloadTwo); - - let deferred = Promise.defer(); - let removeNotifications = 0; - let downloadView = { - onDownloadRemoved: function (aDownload) { - if (++removeNotifications == 2) { - deferred.resolve(); - } - }, - }; - yield list.addView(downloadView); - - yield downloadOne.start(); - yield downloadTwo.start(); - - yield PlacesTestUtils.clearHistory(); - - // Wait for the removal notifications that may still be pending. - yield deferred.promise; -}); - -/** - * Tests the removeFinished method to ensure that it only removes - * finished downloads. - */ -add_task(function* test_removeFinished() -{ - let list = yield promiseNewList(); - let downloadOne = yield promiseNewDownload(); - let downloadTwo = yield promiseNewDownload(); - let downloadThree = yield promiseNewDownload(); - let downloadFour = yield promiseNewDownload(); - yield list.add(downloadOne); - yield list.add(downloadTwo); - yield list.add(downloadThree); - yield list.add(downloadFour); - - let deferred = Promise.defer(); - let removeNotifications = 0; - let downloadView = { - onDownloadRemoved: function (aDownload) { - do_check_true(aDownload == downloadOne || - aDownload == downloadTwo || - aDownload == downloadThree); - do_check_true(removeNotifications < 3); - if (++removeNotifications == 3) { - deferred.resolve(); - } - }, - }; - yield list.addView(downloadView); - - // Start three of the downloads, but don't start downloadTwo, then set - // downloadFour to have partial data. All downloads except downloadFour - // should be removed. - yield downloadOne.start(); - yield downloadThree.start(); - yield downloadFour.start(); - downloadFour.hasPartialData = true; - - list.removeFinished(); - yield deferred.promise; - - let downloads = yield list.getAll() - do_check_eq(downloads.length, 1); -}); - -/** - * Tests the global DownloadSummary objects for the public, private, and - * combined download lists. - */ -add_task(function* test_DownloadSummary() -{ - mustInterruptResponses(); - - let publicList = yield promiseNewList(); - let privateList = yield Downloads.getList(Downloads.PRIVATE); - - let publicSummary = yield Downloads.getSummary(Downloads.PUBLIC); - let privateSummary = yield Downloads.getSummary(Downloads.PRIVATE); - let combinedSummary = yield Downloads.getSummary(Downloads.ALL); - - // Add a public download that has succeeded. - let succeededPublicDownload = yield promiseNewDownload(); - yield succeededPublicDownload.start(); - yield publicList.add(succeededPublicDownload); - - // Add a public download that has been canceled midway. - let canceledPublicDownload = - yield promiseNewDownload(httpUrl("interruptible.txt")); - canceledPublicDownload.start().catch(() => {}); - yield promiseDownloadMidway(canceledPublicDownload); - yield canceledPublicDownload.cancel(); - yield publicList.add(canceledPublicDownload); - - // Add a public download that is in progress. - let inProgressPublicDownload = - yield promiseNewDownload(httpUrl("interruptible.txt")); - inProgressPublicDownload.start().catch(() => {}); - yield promiseDownloadMidway(inProgressPublicDownload); - yield publicList.add(inProgressPublicDownload); - - // Add a private download that is in progress. - let inProgressPrivateDownload = yield Downloads.createDownload({ - source: { url: httpUrl("interruptible.txt"), isPrivate: true }, - target: getTempFile(TEST_TARGET_FILE_NAME).path, - }); - inProgressPrivateDownload.start().catch(() => {}); - yield promiseDownloadMidway(inProgressPrivateDownload); - yield privateList.add(inProgressPrivateDownload); - - // Verify that the summary includes the total number of bytes and the - // currently transferred bytes only for the downloads that are not stopped. - // For simplicity, we assume that after a download is added to the list, its - // current state is immediately propagated to the summary object, which is - // true in the current implementation, though it is not guaranteed as all the - // download operations may happen asynchronously. - do_check_false(publicSummary.allHaveStopped); - do_check_eq(publicSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2); - do_check_eq(publicSummary.progressCurrentBytes, TEST_DATA_SHORT.length); - - do_check_false(privateSummary.allHaveStopped); - do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2); - do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length); - - do_check_false(combinedSummary.allHaveStopped); - do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 4); - do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length * 2); - - yield inProgressPublicDownload.cancel(); - - // Stopping the download should have excluded it from the summary. - do_check_true(publicSummary.allHaveStopped); - do_check_eq(publicSummary.progressTotalBytes, 0); - do_check_eq(publicSummary.progressCurrentBytes, 0); - - do_check_false(privateSummary.allHaveStopped); - do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2); - do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length); - - do_check_false(combinedSummary.allHaveStopped); - do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2); - do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length); - - yield inProgressPrivateDownload.cancel(); - - // All the downloads should be stopped now. - do_check_true(publicSummary.allHaveStopped); - do_check_eq(publicSummary.progressTotalBytes, 0); - do_check_eq(publicSummary.progressCurrentBytes, 0); - - do_check_true(privateSummary.allHaveStopped); - do_check_eq(privateSummary.progressTotalBytes, 0); - do_check_eq(privateSummary.progressCurrentBytes, 0); - - do_check_true(combinedSummary.allHaveStopped); - do_check_eq(combinedSummary.progressTotalBytes, 0); - do_check_eq(combinedSummary.progressCurrentBytes, 0); -}); - -/** - * Checks that views receive the summary change notification. This is tested on - * the combined summary when adding a public download, as we assume that if we - * pass the test in this case we will also pass it in the others. - */ -add_task(function* test_DownloadSummary_notifications() -{ - let list = yield promiseNewList(); - let summary = yield Downloads.getSummary(Downloads.ALL); - - let download = yield promiseNewDownload(); - yield list.add(download); - - // Check that we receive change notifications. - let receivedOnSummaryChanged = false; - yield summary.addView({ - onSummaryChanged: function () { - receivedOnSummaryChanged = true; - }, - }); - yield download.start(); - do_check_true(receivedOnSummaryChanged); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js b/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js deleted file mode 100644 index 3a23dfbe3..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js +++ /dev/null @@ -1,315 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the DownloadStore object. - */ - -"use strict"; - -// Globals - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore", - "resource://gre/modules/DownloadStore.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm") - -/** - * Returns a new DownloadList object with an associated DownloadStore. - * - * @param aStorePath - * String pointing to the file to be associated with the DownloadStore, - * or undefined to use a non-existing temporary file. In this case, the - * temporary file is deleted when the test file execution finishes. - * - * @return {Promise} - * @resolves Array [ Newly created DownloadList , associated DownloadStore ]. - * @rejects JavaScript exception. - */ -function promiseNewListAndStore(aStorePath) -{ - return promiseNewList().then(function (aList) { - let path = aStorePath || getTempFile(TEST_STORE_FILE_NAME).path; - let store = new DownloadStore(aList, path); - return [aList, store]; - }); -} - -// Tests - -/** - * Saves downloads to a file, then reloads them. - */ -add_task(function* test_save_reload() -{ - let [listForSave, storeForSave] = yield promiseNewListAndStore(); - let [listForLoad, storeForLoad] = yield promiseNewListAndStore( - storeForSave.path); - - listForSave.add(yield promiseNewDownload(httpUrl("source.txt"))); - listForSave.add(yield Downloads.createDownload({ - source: { url: httpUrl("empty.txt"), - referrer: TEST_REFERRER_URL }, - target: getTempFile(TEST_TARGET_FILE_NAME), - })); - - // This PDF download should not be serialized because it never succeeds. - let pdfDownload = yield Downloads.createDownload({ - source: { url: httpUrl("empty.txt"), - referrer: TEST_REFERRER_URL }, - target: getTempFile(TEST_TARGET_FILE_NAME), - saver: "pdf", - }); - listForSave.add(pdfDownload); - - // If we used a callback to adjust the channel, the download should - // not be serialized because we can't recreate it across sessions. - let adjustedDownload = yield Downloads.createDownload({ - source: { url: httpUrl("empty.txt"), - adjustChannel: () => Promise.resolve() }, - target: getTempFile(TEST_TARGET_FILE_NAME), - }); - listForSave.add(adjustedDownload); - - let legacyDownload = yield promiseStartLegacyDownload(); - yield legacyDownload.cancel(); - listForSave.add(legacyDownload); - - yield storeForSave.save(); - yield storeForLoad.load(); - - // Remove the PDF and adjusted downloads because they should not appear here. - listForSave.remove(adjustedDownload); - listForSave.remove(pdfDownload); - - let itemsForSave = yield listForSave.getAll(); - let itemsForLoad = yield listForLoad.getAll(); - - do_check_eq(itemsForSave.length, itemsForLoad.length); - - // Downloads should be reloaded in the same order. - for (let i = 0; i < itemsForSave.length; i++) { - // The reloaded downloads are different objects. - do_check_neq(itemsForSave[i], itemsForLoad[i]); - - // The reloaded downloads have the same properties. - do_check_eq(itemsForSave[i].source.url, - itemsForLoad[i].source.url); - do_check_eq(itemsForSave[i].source.referrer, - itemsForLoad[i].source.referrer); - do_check_eq(itemsForSave[i].target.path, - itemsForLoad[i].target.path); - do_check_eq(itemsForSave[i].saver.toSerializable(), - itemsForLoad[i].saver.toSerializable()); - } -}); - -/** - * Checks that saving an empty list deletes any existing file. - */ -add_task(function* test_save_empty() -{ - let [, store] = yield promiseNewListAndStore(); - - let createdFile = yield OS.File.open(store.path, { create: true }); - yield createdFile.close(); - - yield store.save(); - - do_check_false(yield OS.File.exists(store.path)); - - // If the file does not exist, saving should not generate exceptions. - yield store.save(); -}); - -/** - * Checks that loading from a missing file results in an empty list. - */ -add_task(function* test_load_empty() -{ - let [list, store] = yield promiseNewListAndStore(); - - do_check_false(yield OS.File.exists(store.path)); - - yield store.load(); - - let items = yield list.getAll(); - do_check_eq(items.length, 0); -}); - -/** - * Loads downloads from a string in a predefined format. The purpose of this - * test is to verify that the JSON format used in previous versions can be - * loaded, assuming the file is reloaded on the same platform. - */ -add_task(function* test_load_string_predefined() -{ - let [list, store] = yield promiseNewListAndStore(); - - // The platform-dependent file name should be generated dynamically. - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - let filePathLiteral = JSON.stringify(targetPath); - let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); - let emptyUriLiteral = JSON.stringify(httpUrl("empty.txt")); - let referrerUriLiteral = JSON.stringify(TEST_REFERRER_URL); - - let string = "{\"list\":[{\"source\":" + sourceUriLiteral + "," + - "\"target\":" + filePathLiteral + "}," + - "{\"source\":{\"url\":" + emptyUriLiteral + "," + - "\"referrer\":" + referrerUriLiteral + "}," + - "\"target\":" + filePathLiteral + "}]}"; - - yield OS.File.writeAtomic(store.path, - new TextEncoder().encode(string), - { tmpPath: store.path + ".tmp" }); - - yield store.load(); - - let items = yield list.getAll(); - - do_check_eq(items.length, 2); - - do_check_eq(items[0].source.url, httpUrl("source.txt")); - do_check_eq(items[0].target.path, targetPath); - - do_check_eq(items[1].source.url, httpUrl("empty.txt")); - do_check_eq(items[1].source.referrer, TEST_REFERRER_URL); - do_check_eq(items[1].target.path, targetPath); -}); - -/** - * Loads downloads from a well-formed JSON string containing unrecognized data. - */ -add_task(function* test_load_string_unrecognized() -{ - let [list, store] = yield promiseNewListAndStore(); - - // The platform-dependent file name should be generated dynamically. - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - let filePathLiteral = JSON.stringify(targetPath); - let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); - - let string = "{\"list\":[{\"source\":null," + - "\"target\":null}," + - "{\"source\":{\"url\":" + sourceUriLiteral + "}," + - "\"target\":{\"path\":" + filePathLiteral + "}," + - "\"saver\":{\"type\":\"copy\"}}]}"; - - yield OS.File.writeAtomic(store.path, - new TextEncoder().encode(string), - { tmpPath: store.path + ".tmp" }); - - yield store.load(); - - let items = yield list.getAll(); - - do_check_eq(items.length, 1); - - do_check_eq(items[0].source.url, httpUrl("source.txt")); - do_check_eq(items[0].target.path, targetPath); -}); - -/** - * Loads downloads from a malformed JSON string. - */ -add_task(function* test_load_string_malformed() -{ - let [list, store] = yield promiseNewListAndStore(); - - let string = "{\"list\":[{\"source\":null,\"target\":null}," + - "{\"source\":{\"url\":\"about:blank\"}}}"; - - yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), - { tmpPath: store.path + ".tmp" }); - - try { - yield store.load(); - do_throw("Exception expected when JSON data is malformed."); - } catch (ex) { - if (ex.name != "SyntaxError") { - throw ex; - } - do_print("The expected SyntaxError exception was thrown."); - } - - let items = yield list.getAll(); - - do_check_eq(items.length, 0); -}); - -/** - * Saves downloads with unknown properties to a file and then reloads - * them to ensure that these properties are preserved. - */ -add_task(function* test_save_reload_unknownProperties() -{ - let [listForSave, storeForSave] = yield promiseNewListAndStore(); - let [listForLoad, storeForLoad] = yield promiseNewListAndStore( - storeForSave.path); - - let download1 = yield promiseNewDownload(httpUrl("source.txt")); - // startTime should be ignored as it is a known property, and error - // is ignored by serialization - download1._unknownProperties = { peanut: "butter", - orange: "marmalade", - startTime: 77, - error: { message: "Passed" } }; - listForSave.add(download1); - - let download2 = yield promiseStartLegacyDownload(); - yield download2.cancel(); - download2._unknownProperties = { number: 5, object: { test: "string" } }; - listForSave.add(download2); - - let download3 = yield Downloads.createDownload({ - source: { url: httpUrl("empty.txt"), - referrer: TEST_REFERRER_URL, - source1: "download3source1", - source2: "download3source2" }, - target: { path: getTempFile(TEST_TARGET_FILE_NAME).path, - target1: "download3target1", - target2: "download3target2" }, - saver : { type: "copy", - saver1: "download3saver1", - saver2: "download3saver2" }, - }); - listForSave.add(download3); - - yield storeForSave.save(); - yield storeForLoad.load(); - - let itemsForSave = yield listForSave.getAll(); - let itemsForLoad = yield listForLoad.getAll(); - - do_check_eq(itemsForSave.length, itemsForLoad.length); - - do_check_eq(Object.keys(itemsForLoad[0]._unknownProperties).length, 2); - do_check_eq(itemsForLoad[0]._unknownProperties.peanut, "butter"); - do_check_eq(itemsForLoad[0]._unknownProperties.orange, "marmalade"); - do_check_false("startTime" in itemsForLoad[0]._unknownProperties); - do_check_false("error" in itemsForLoad[0]._unknownProperties); - - do_check_eq(Object.keys(itemsForLoad[1]._unknownProperties).length, 2); - do_check_eq(itemsForLoad[1]._unknownProperties.number, 5); - do_check_eq(itemsForLoad[1]._unknownProperties.object.test, "string"); - - do_check_eq(Object.keys(itemsForLoad[2].source._unknownProperties).length, 2); - do_check_eq(itemsForLoad[2].source._unknownProperties.source1, - "download3source1"); - do_check_eq(itemsForLoad[2].source._unknownProperties.source2, - "download3source2"); - - do_check_eq(Object.keys(itemsForLoad[2].target._unknownProperties).length, 2); - do_check_eq(itemsForLoad[2].target._unknownProperties.target1, - "download3target1"); - do_check_eq(itemsForLoad[2].target._unknownProperties.target2, - "download3target2"); - - do_check_eq(Object.keys(itemsForLoad[2].saver._unknownProperties).length, 2); - do_check_eq(itemsForLoad[2].saver._unknownProperties.saver1, - "download3saver1"); - do_check_eq(itemsForLoad[2].saver._unknownProperties.saver2, - "download3saver2"); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_Downloads.js b/toolkit/components/jsdownloads/test/unit/test_Downloads.js deleted file mode 100644 index 2027beee1..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_Downloads.js +++ /dev/null @@ -1,194 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the functions located directly in the "Downloads" object. - */ - -"use strict"; - -// Tests - -/** - * Tests that the createDownload function exists and can be called. More - * detailed tests are implemented separately for the DownloadCore module. - */ -add_task(function* test_createDownload() -{ - // Creates a simple Download object without starting the download. - yield Downloads.createDownload({ - source: { url: "about:blank" }, - target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, - saver: { type: "copy" }, - }); -}); - -/** - * Tests createDownload for private download. - */ -add_task(function* test_createDownload_private() -{ - let download = yield Downloads.createDownload({ - source: { url: "about:blank", isPrivate: true }, - target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, - saver: { type: "copy" } - }); - do_check_true(download.source.isPrivate); -}); - -/** - * Tests createDownload for normal (public) download. - */ -add_task(function* test_createDownload_public() -{ - let tempPath = getTempFile(TEST_TARGET_FILE_NAME).path; - let download = yield Downloads.createDownload({ - source: { url: "about:blank", isPrivate: false }, - target: { path: tempPath }, - saver: { type: "copy" } - }); - do_check_false(download.source.isPrivate); - - download = yield Downloads.createDownload({ - source: { url: "about:blank" }, - target: { path: tempPath }, - saver: { type: "copy" } - }); - do_check_false(download.source.isPrivate); -}); - -/** - * Tests createDownload for a pdf saver throws if only given a url. - */ -add_task(function* test_createDownload_pdf() -{ - let download = yield Downloads.createDownload({ - source: { url: "about:blank" }, - target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, - saver: { type: "pdf" }, - }); - - try { - yield download.start(); - do_throw("The download should have failed."); - } catch (ex) { - if (!(ex instanceof Downloads.Error) || !ex.becauseSourceFailed) { - throw ex; - } - } - - do_check_false(download.succeeded); - do_check_true(download.stopped); - do_check_false(download.canceled); - do_check_true(download.error !== null); - do_check_true(download.error.becauseSourceFailed); - do_check_false(download.error.becauseTargetFailed); - do_check_false(yield OS.File.exists(download.target.path)); -}); - -/** - * Tests "fetch" with nsIURI and nsIFile as arguments. - */ -add_task(function* test_fetch_uri_file_arguments() -{ - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.fetch(NetUtil.newURI(httpUrl("source.txt")), targetFile); - yield promiseVerifyContents(targetFile.path, TEST_DATA_SHORT); -}); - -/** - * Tests "fetch" with DownloadSource and DownloadTarget as arguments. - */ -add_task(function* test_fetch_object_arguments() -{ - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - yield Downloads.fetch({ url: httpUrl("source.txt") }, { path: targetPath }); - yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); -}); - -/** - * Tests "fetch" with string arguments. - */ -add_task(function* test_fetch_string_arguments() -{ - let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - yield Downloads.fetch(httpUrl("source.txt"), targetPath); - yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); - - targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; - yield Downloads.fetch(new String(httpUrl("source.txt")), - new String(targetPath)); - yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); -}); - -/** - * Tests that the getList function returns the same list when called multiple - * times with the same argument, but returns different lists when called with - * different arguments. More detailed tests are implemented separately for the - * DownloadList module. - */ -add_task(function* test_getList() -{ - let publicListOne = yield Downloads.getList(Downloads.PUBLIC); - let privateListOne = yield Downloads.getList(Downloads.PRIVATE); - - let publicListTwo = yield Downloads.getList(Downloads.PUBLIC); - let privateListTwo = yield Downloads.getList(Downloads.PRIVATE); - - do_check_eq(publicListOne, publicListTwo); - do_check_eq(privateListOne, privateListTwo); - - do_check_neq(publicListOne, privateListOne); -}); - -/** - * Tests that the getSummary function returns the same summary when called - * multiple times with the same argument, but returns different summaries when - * called with different arguments. More detailed tests are implemented - * separately for the DownloadSummary object in the DownloadList module. - */ -add_task(function* test_getSummary() -{ - let publicSummaryOne = yield Downloads.getSummary(Downloads.PUBLIC); - let privateSummaryOne = yield Downloads.getSummary(Downloads.PRIVATE); - - let publicSummaryTwo = yield Downloads.getSummary(Downloads.PUBLIC); - let privateSummaryTwo = yield Downloads.getSummary(Downloads.PRIVATE); - - do_check_eq(publicSummaryOne, publicSummaryTwo); - do_check_eq(privateSummaryOne, privateSummaryTwo); - - do_check_neq(publicSummaryOne, privateSummaryOne); -}); - -/** - * Tests that the getSystemDownloadsDirectory returns a non-empty download - * directory string. - */ -add_task(function* test_getSystemDownloadsDirectory() -{ - let downloadDir = yield Downloads.getSystemDownloadsDirectory(); - do_check_neq(downloadDir, ""); -}); - -/** - * Tests that the getPreferredDownloadsDirectory returns a non-empty download - * directory string. - */ -add_task(function* test_getPreferredDownloadsDirectory() -{ - let downloadDir = yield Downloads.getPreferredDownloadsDirectory(); - do_check_neq(downloadDir, ""); -}); - -/** - * Tests that the getTemporaryDownloadsDirectory returns a non-empty download - * directory string. - */ -add_task(function* test_getTemporaryDownloadsDirectory() -{ - let downloadDir = yield Downloads.getTemporaryDownloadsDirectory(); - do_check_neq(downloadDir, ""); -}); diff --git a/toolkit/components/jsdownloads/test/unit/test_PrivateTemp.js b/toolkit/components/jsdownloads/test/unit/test_PrivateTemp.js deleted file mode 100644 index 1308e9782..000000000 --- a/toolkit/components/jsdownloads/test/unit/test_PrivateTemp.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -/* - * The temporary directory downloads saves to, should be only readable - * for the current user. - */ -add_task(function* test_private_temp() { - - let download = yield promiseStartExternalHelperAppServiceDownload( - httpUrl("empty.txt")); - - yield promiseDownloadStopped(download); - - var targetFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile); - targetFile.initWithPath(download.target.path); - - // 488 is the decimal value of 0o700. - equal(targetFile.parent.permissions, 448); -}); diff --git a/toolkit/components/jsdownloads/test/unit/xpcshell.ini b/toolkit/components/jsdownloads/test/unit/xpcshell.ini deleted file mode 100644 index 8de554540..000000000 --- a/toolkit/components/jsdownloads/test/unit/xpcshell.ini +++ /dev/null @@ -1,19 +0,0 @@ -[DEFAULT] -head = head.js -tail = -skip-if = toolkit == 'android' - -# Note: The "tail.js" file is not defined in the "tail" key because it calls -# the "add_test_task" function, that does not work properly in tail files. -support-files = - common_test_Download.js - -[test_DownloadCore.js] -[test_DownloadImport.js] -[test_DownloadIntegration.js] -[test_DownloadLegacy.js] -[test_DownloadList.js] -[test_Downloads.js] -[test_DownloadStore.js] -[test_PrivateTemp.js] -skip-if = os != 'linux' |