michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: /** michael@0: * Tests the DownloadStore object. michael@0: */ michael@0: michael@0: "use strict"; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Globals michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore", michael@0: "resource://gre/modules/DownloadStore.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "OS", michael@0: "resource://gre/modules/osfile.jsm") michael@0: michael@0: /** michael@0: * Returns a new DownloadList object with an associated DownloadStore. michael@0: * michael@0: * @param aStorePath michael@0: * String pointing to the file to be associated with the DownloadStore, michael@0: * or undefined to use a non-existing temporary file. In this case, the michael@0: * temporary file is deleted when the test file execution finishes. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves Array [ Newly created DownloadList , associated DownloadStore ]. michael@0: * @rejects JavaScript exception. michael@0: */ michael@0: function promiseNewListAndStore(aStorePath) michael@0: { michael@0: return promiseNewList().then(function (aList) { michael@0: let path = aStorePath || getTempFile(TEST_STORE_FILE_NAME).path; michael@0: let store = new DownloadStore(aList, path); michael@0: return [aList, store]; michael@0: }); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Tests michael@0: michael@0: /** michael@0: * Saves downloads to a file, then reloads them. michael@0: */ michael@0: add_task(function test_save_reload() michael@0: { michael@0: let [listForSave, storeForSave] = yield promiseNewListAndStore(); michael@0: let [listForLoad, storeForLoad] = yield promiseNewListAndStore( michael@0: storeForSave.path); michael@0: michael@0: listForSave.add(yield promiseNewDownload(httpUrl("source.txt"))); michael@0: listForSave.add(yield Downloads.createDownload({ michael@0: source: { url: httpUrl("empty.txt"), michael@0: referrer: TEST_REFERRER_URL }, michael@0: target: getTempFile(TEST_TARGET_FILE_NAME), michael@0: })); michael@0: michael@0: let legacyDownload = yield promiseStartLegacyDownload(); michael@0: yield legacyDownload.cancel(); michael@0: listForSave.add(legacyDownload); michael@0: michael@0: yield storeForSave.save(); michael@0: yield storeForLoad.load(); michael@0: michael@0: let itemsForSave = yield listForSave.getAll(); michael@0: let itemsForLoad = yield listForLoad.getAll(); michael@0: michael@0: do_check_eq(itemsForSave.length, itemsForLoad.length); michael@0: michael@0: // Downloads should be reloaded in the same order. michael@0: for (let i = 0; i < itemsForSave.length; i++) { michael@0: // The reloaded downloads are different objects. michael@0: do_check_neq(itemsForSave[i], itemsForLoad[i]); michael@0: michael@0: // The reloaded downloads have the same properties. michael@0: do_check_eq(itemsForSave[i].source.url, michael@0: itemsForLoad[i].source.url); michael@0: do_check_eq(itemsForSave[i].source.referrer, michael@0: itemsForLoad[i].source.referrer); michael@0: do_check_eq(itemsForSave[i].target.path, michael@0: itemsForLoad[i].target.path); michael@0: do_check_eq(itemsForSave[i].saver.toSerializable(), michael@0: itemsForLoad[i].saver.toSerializable()); michael@0: } michael@0: }); michael@0: michael@0: /** michael@0: * Checks that saving an empty list deletes any existing file. michael@0: */ michael@0: add_task(function test_save_empty() michael@0: { michael@0: let [list, store] = yield promiseNewListAndStore(); michael@0: michael@0: let createdFile = yield OS.File.open(store.path, { create: true }); michael@0: yield createdFile.close(); michael@0: michael@0: yield store.save(); michael@0: michael@0: do_check_false(yield OS.File.exists(store.path)); michael@0: michael@0: // If the file does not exist, saving should not generate exceptions. michael@0: yield store.save(); michael@0: }); michael@0: michael@0: /** michael@0: * Checks that loading from a missing file results in an empty list. michael@0: */ michael@0: add_task(function test_load_empty() michael@0: { michael@0: let [list, store] = yield promiseNewListAndStore(); michael@0: michael@0: do_check_false(yield OS.File.exists(store.path)); michael@0: michael@0: yield store.load(); michael@0: michael@0: let items = yield list.getAll(); michael@0: do_check_eq(items.length, 0); michael@0: }); michael@0: michael@0: /** michael@0: * Loads downloads from a string in a predefined format. The purpose of this michael@0: * test is to verify that the JSON format used in previous versions can be michael@0: * loaded, assuming the file is reloaded on the same platform. michael@0: */ michael@0: add_task(function test_load_string_predefined() michael@0: { michael@0: let [list, store] = yield promiseNewListAndStore(); michael@0: michael@0: // The platform-dependent file name should be generated dynamically. michael@0: let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; michael@0: let filePathLiteral = JSON.stringify(targetPath); michael@0: let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); michael@0: let emptyUriLiteral = JSON.stringify(httpUrl("empty.txt")); michael@0: let referrerUriLiteral = JSON.stringify(TEST_REFERRER_URL); michael@0: michael@0: let string = "{\"list\":[{\"source\":" + sourceUriLiteral + "," + michael@0: "\"target\":" + filePathLiteral + "}," + michael@0: "{\"source\":{\"url\":" + emptyUriLiteral + "," + michael@0: "\"referrer\":" + referrerUriLiteral + "}," + michael@0: "\"target\":" + filePathLiteral + "}]}"; michael@0: michael@0: yield OS.File.writeAtomic(store.path, michael@0: new TextEncoder().encode(string), michael@0: { tmpPath: store.path + ".tmp" }); michael@0: michael@0: yield store.load(); michael@0: michael@0: let items = yield list.getAll(); michael@0: michael@0: do_check_eq(items.length, 2); michael@0: michael@0: do_check_eq(items[0].source.url, httpUrl("source.txt")); michael@0: do_check_eq(items[0].target.path, targetPath); michael@0: michael@0: do_check_eq(items[1].source.url, httpUrl("empty.txt")); michael@0: do_check_eq(items[1].source.referrer, TEST_REFERRER_URL); michael@0: do_check_eq(items[1].target.path, targetPath); michael@0: }); michael@0: michael@0: /** michael@0: * Loads downloads from a well-formed JSON string containing unrecognized data. michael@0: */ michael@0: add_task(function test_load_string_unrecognized() michael@0: { michael@0: let [list, store] = yield promiseNewListAndStore(); michael@0: michael@0: // The platform-dependent file name should be generated dynamically. michael@0: let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; michael@0: let filePathLiteral = JSON.stringify(targetPath); michael@0: let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); michael@0: michael@0: let string = "{\"list\":[{\"source\":null," + michael@0: "\"target\":null}," + michael@0: "{\"source\":{\"url\":" + sourceUriLiteral + "}," + michael@0: "\"target\":{\"path\":" + filePathLiteral + "}," + michael@0: "\"saver\":{\"type\":\"copy\"}}]}"; michael@0: michael@0: yield OS.File.writeAtomic(store.path, michael@0: new TextEncoder().encode(string), michael@0: { tmpPath: store.path + ".tmp" }); michael@0: michael@0: yield store.load(); michael@0: michael@0: let items = yield list.getAll(); michael@0: michael@0: do_check_eq(items.length, 1); michael@0: michael@0: do_check_eq(items[0].source.url, httpUrl("source.txt")); michael@0: do_check_eq(items[0].target.path, targetPath); michael@0: }); michael@0: michael@0: /** michael@0: * Loads downloads from a malformed JSON string. michael@0: */ michael@0: add_task(function test_load_string_malformed() michael@0: { michael@0: let [list, store] = yield promiseNewListAndStore(); michael@0: michael@0: let string = "{\"list\":[{\"source\":null,\"target\":null}," + michael@0: "{\"source\":{\"url\":\"about:blank\"}}}"; michael@0: michael@0: yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), michael@0: { tmpPath: store.path + ".tmp" }); michael@0: michael@0: try { michael@0: yield store.load(); michael@0: do_throw("Exception expected when JSON data is malformed."); michael@0: } catch (ex if ex.name == "SyntaxError") { michael@0: do_print("The expected SyntaxError exception was thrown."); michael@0: } michael@0: michael@0: let items = yield list.getAll(); michael@0: michael@0: do_check_eq(items.length, 0); michael@0: }); michael@0: michael@0: /** michael@0: * Saves downloads with unknown properties to a file and then reloads michael@0: * them to ensure that these properties are preserved. michael@0: */ michael@0: add_task(function test_save_reload_unknownProperties() michael@0: { michael@0: let [listForSave, storeForSave] = yield promiseNewListAndStore(); michael@0: let [listForLoad, storeForLoad] = yield promiseNewListAndStore( michael@0: storeForSave.path); michael@0: michael@0: let download1 = yield promiseNewDownload(httpUrl("source.txt")); michael@0: // startTime should be ignored as it is a known property, and error michael@0: // is ignored by serialization michael@0: download1._unknownProperties = { peanut: "butter", michael@0: orange: "marmalade", michael@0: startTime: 77, michael@0: error: { message: "Passed" } }; michael@0: listForSave.add(download1); michael@0: michael@0: let download2 = yield promiseStartLegacyDownload(); michael@0: yield download2.cancel(); michael@0: download2._unknownProperties = { number: 5, object: { test: "string" } }; michael@0: listForSave.add(download2); michael@0: michael@0: let download3 = yield Downloads.createDownload({ michael@0: source: { url: httpUrl("empty.txt"), michael@0: referrer: TEST_REFERRER_URL, michael@0: source1: "download3source1", michael@0: source2: "download3source2" }, michael@0: target: { path: getTempFile(TEST_TARGET_FILE_NAME).path, michael@0: target1: "download3target1", michael@0: target2: "download3target2" }, michael@0: saver : { type: "copy", michael@0: saver1: "download3saver1", michael@0: saver2: "download3saver2" }, michael@0: }); michael@0: listForSave.add(download3); michael@0: michael@0: yield storeForSave.save(); michael@0: yield storeForLoad.load(); michael@0: michael@0: let itemsForSave = yield listForSave.getAll(); michael@0: let itemsForLoad = yield listForLoad.getAll(); michael@0: michael@0: do_check_eq(itemsForSave.length, itemsForLoad.length); michael@0: michael@0: do_check_eq(Object.keys(itemsForLoad[0]._unknownProperties).length, 2); michael@0: do_check_eq(itemsForLoad[0]._unknownProperties.peanut, "butter"); michael@0: do_check_eq(itemsForLoad[0]._unknownProperties.orange, "marmalade"); michael@0: do_check_false("startTime" in itemsForLoad[0]._unknownProperties); michael@0: do_check_false("error" in itemsForLoad[0]._unknownProperties); michael@0: michael@0: do_check_eq(Object.keys(itemsForLoad[1]._unknownProperties).length, 2); michael@0: do_check_eq(itemsForLoad[1]._unknownProperties.number, 5); michael@0: do_check_eq(itemsForLoad[1]._unknownProperties.object.test, "string"); michael@0: michael@0: do_check_eq(Object.keys(itemsForLoad[2].source._unknownProperties).length, 2); michael@0: do_check_eq(itemsForLoad[2].source._unknownProperties.source1, michael@0: "download3source1"); michael@0: do_check_eq(itemsForLoad[2].source._unknownProperties.source2, michael@0: "download3source2"); michael@0: michael@0: do_check_eq(Object.keys(itemsForLoad[2].target._unknownProperties).length, 2); michael@0: do_check_eq(itemsForLoad[2].target._unknownProperties.target1, michael@0: "download3target1"); michael@0: do_check_eq(itemsForLoad[2].target._unknownProperties.target2, michael@0: "download3target2"); michael@0: michael@0: do_check_eq(Object.keys(itemsForLoad[2].saver._unknownProperties).length, 2); michael@0: do_check_eq(itemsForLoad[2].saver._unknownProperties.saver1, michael@0: "download3saver1"); michael@0: do_check_eq(itemsForLoad[2].saver._unknownProperties.saver2, michael@0: "download3saver2"); michael@0: }); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Termination michael@0: michael@0: let tailFile = do_get_file("tail.js"); michael@0: Services.scriptloader.loadSubScript(NetUtil.newURI(tailFile).spec);