/* -*- 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"); });