diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/downloads/test/unit/test_DownloadStore.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/downloads/test/unit/test_DownloadStore.js')
-rw-r--r-- | toolkit/components/downloads/test/unit/test_DownloadStore.js | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/toolkit/components/downloads/test/unit/test_DownloadStore.js b/toolkit/components/downloads/test/unit/test_DownloadStore.js new file mode 100644 index 0000000000..9353b63060 --- /dev/null +++ b/toolkit/components/downloads/test/unit/test_DownloadStore.js @@ -0,0 +1,458 @@ +/* -*- 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 + +ChromeUtils.defineESModuleGetters(this, { + DownloadError: "resource://gre/modules/DownloadCore.sys.mjs", + DownloadStore: "resource://gre/modules/DownloadStore.sys.mjs", +}); + +/** + * 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(async function test_save_reload() { + let [listForSave, storeForSave] = await promiseNewListAndStore(); + let [listForLoad, storeForLoad] = await promiseNewListAndStore( + storeForSave.path + ); + let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.EMPTY, + true, + NetUtil.newURI(TEST_REFERRER_URL) + ); + + listForSave.add(await promiseNewDownload(httpUrl("source.txt"))); + listForSave.add( + await Downloads.createDownload({ + source: { url: httpUrl("empty.txt"), referrerInfo }, + target: getTempFile(TEST_TARGET_FILE_NAME), + }) + ); + + // 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 = await Downloads.createDownload({ + source: { + url: httpUrl("empty.txt"), + adjustChannel: () => Promise.resolve(), + }, + target: getTempFile(TEST_TARGET_FILE_NAME), + }); + listForSave.add(adjustedDownload); + + let legacyDownload = await promiseStartLegacyDownload(); + await legacyDownload.cancel(); + listForSave.add(legacyDownload); + + await storeForSave.save(); + await storeForLoad.load(); + + // Remove the adjusted download because it should not appear here. + listForSave.remove(adjustedDownload); + + let itemsForSave = await listForSave.getAll(); + let itemsForLoad = await listForLoad.getAll(); + + Assert.equal(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. + Assert.notEqual(itemsForSave[i], itemsForLoad[i]); + + // The reloaded downloads have the same properties. + Assert.equal(itemsForSave[i].source.url, itemsForLoad[i].source.url); + Assert.equal( + !!itemsForSave[i].source.referrerInfo, + !!itemsForLoad[i].source.referrerInfo + ); + if ( + itemsForSave[i].source.referrerInfo && + itemsForLoad[i].source.referrerInfo + ) { + Assert.ok( + itemsForSave[i].source.referrerInfo.equals( + itemsForLoad[i].source.referrerInfo + ) + ); + } + Assert.equal(itemsForSave[i].target.path, itemsForLoad[i].target.path); + Assert.equal( + itemsForSave[i].saver.toSerializable(), + itemsForLoad[i].saver.toSerializable() + ); + } +}); + +/** + * Checks that saving an empty list deletes any existing file. + */ +add_task(async function test_save_empty() { + let [, store] = await promiseNewListAndStore(); + + await IOUtils.write(store.path, new Uint8Array()); + + await store.save(); + + let successful; + try { + await IOUtils.read(store.path); + successful = true; + } catch (ex) { + successful = ex.name != "NotFoundError"; + } + + ok(!successful, "File should not exist"); + + // If the file does not exist, saving should not generate exceptions. + await store.save(); +}); + +/** + * Checks that loading from a missing file results in an empty list. + */ +add_task(async function test_load_empty() { + let [list, store] = await promiseNewListAndStore(); + + let succeesful; + try { + await IOUtils.read(store.path); + succeesful = true; + } catch (ex) { + succeesful = ex.name != "NotFoundError"; + } + + ok(!succeesful, "File should not exist"); + + let items = await list.getAll(); + Assert.equal(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(async function test_load_string_predefined() { + let [list, store] = await 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 referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.EMPTY, + true, + NetUtil.newURI(TEST_REFERRER_URL) + ); + let referrerInfoLiteral = JSON.stringify( + E10SUtils.serializeReferrerInfo(referrerInfo) + ); + + let string = + '{"list":[{"source":' + + sourceUriLiteral + + "," + + '"target":' + + filePathLiteral + + "}," + + '{"source":{"url":' + + emptyUriLiteral + + "," + + '"referrerInfo":' + + referrerInfoLiteral + + "}," + + '"target":' + + filePathLiteral + + "}]}"; + + await IOUtils.write(store.path, new TextEncoder().encode(string), { + tmpPath: store.path + ".tmp", + }); + + await store.load(); + + let items = await list.getAll(); + + Assert.equal(items.length, 2); + + Assert.equal(items[0].source.url, httpUrl("source.txt")); + Assert.equal(items[0].target.path, targetPath); + + Assert.equal(items[1].source.url, httpUrl("empty.txt")); + + checkEqualReferrerInfos(items[1].source.referrerInfo, referrerInfo); + Assert.equal(items[1].target.path, targetPath); +}); + +/** + * Loads downloads from a well-formed JSON string containing unrecognized data. + */ +add_task(async function test_load_string_unrecognized() { + let [list, store] = await 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"}}]}'; + + await IOUtils.write(store.path, new TextEncoder().encode(string), { + tmpPath: store.path + ".tmp", + }); + + await store.load(); + + let items = await list.getAll(); + + Assert.equal(items.length, 1); + + Assert.equal(items[0].source.url, httpUrl("source.txt")); + Assert.equal(items[0].target.path, targetPath); +}); + +/** + * Loads downloads from a malformed JSON string. + */ +add_task(async function test_load_string_malformed() { + let [list, store] = await promiseNewListAndStore(); + + let string = + '{"list":[{"source":null,"target":null},' + + '{"source":{"url":"about:blank"}}}'; + + await IOUtils.write(store.path, new TextEncoder().encode(string), { + tmpPath: store.path + ".tmp", + }); + + try { + await store.load(); + do_throw("Exception expected when JSON data is malformed."); + } catch (ex) { + if (ex.name != "SyntaxError") { + throw ex; + } + info("The expected SyntaxError exception was thrown."); + } + + let items = await list.getAll(); + + Assert.equal(items.length, 0); +}); + +/** + * Saves downloads with unknown properties to a file and then reloads + * them to ensure that these properties are preserved. + */ +add_task(async function test_save_reload_unknownProperties() { + let [listForSave, storeForSave] = await promiseNewListAndStore(); + let [listForLoad, storeForLoad] = await promiseNewListAndStore( + storeForSave.path + ); + + let download1 = await 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 = await promiseStartLegacyDownload(); + await download2.cancel(); + download2._unknownProperties = { number: 5, object: { test: "string" } }; + listForSave.add(download2); + + let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.EMPTY, + true, + NetUtil.newURI(TEST_REFERRER_URL) + ); + let download3 = await Downloads.createDownload({ + source: { + url: httpUrl("empty.txt"), + referrerInfo, + 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); + + await storeForSave.save(); + await storeForLoad.load(); + + let itemsForSave = await listForSave.getAll(); + let itemsForLoad = await listForLoad.getAll(); + + Assert.equal(itemsForSave.length, itemsForLoad.length); + + Assert.equal(Object.keys(itemsForLoad[0]._unknownProperties).length, 2); + Assert.equal(itemsForLoad[0]._unknownProperties.peanut, "butter"); + Assert.equal(itemsForLoad[0]._unknownProperties.orange, "marmalade"); + Assert.equal(false, "startTime" in itemsForLoad[0]._unknownProperties); + Assert.equal(false, "error" in itemsForLoad[0]._unknownProperties); + + Assert.equal(Object.keys(itemsForLoad[1]._unknownProperties).length, 2); + Assert.equal(itemsForLoad[1]._unknownProperties.number, 5); + Assert.equal(itemsForLoad[1]._unknownProperties.object.test, "string"); + + Assert.equal( + Object.keys(itemsForLoad[2].source._unknownProperties).length, + 2 + ); + Assert.equal( + itemsForLoad[2].source._unknownProperties.source1, + "download3source1" + ); + Assert.equal( + itemsForLoad[2].source._unknownProperties.source2, + "download3source2" + ); + + Assert.equal( + Object.keys(itemsForLoad[2].target._unknownProperties).length, + 2 + ); + Assert.equal( + itemsForLoad[2].target._unknownProperties.target1, + "download3target1" + ); + Assert.equal( + itemsForLoad[2].target._unknownProperties.target2, + "download3target2" + ); + + Assert.equal(Object.keys(itemsForLoad[2].saver._unknownProperties).length, 2); + Assert.equal( + itemsForLoad[2].saver._unknownProperties.saver1, + "download3saver1" + ); + Assert.equal( + itemsForLoad[2].saver._unknownProperties.saver2, + "download3saver2" + ); +}); + +/** + * Saves insecure downloads to a file, then reloads the file and checks if they + * are still there. + */ +add_task(async function test_insecure_download_deletion() { + let [listForSave, storeForSave] = await promiseNewListAndStore(); + let [listForLoad, storeForLoad] = await promiseNewListAndStore( + storeForSave.path + ); + let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.EMPTY, + true, + NetUtil.newURI(TEST_REFERRER_URL) + ); + + const createTestDownload = async startTime => { + // Create a valid test download and start it so it creates a file + let targetFile = getTempFile(TEST_TARGET_FILE_NAME); + let download = await Downloads.createDownload({ + source: { url: httpUrl("empty.txt"), referrerInfo }, + target: targetFile.path, + startTime: new Date().toISOString(), + contentType: "application/zip", + }); + await download.start(); + + // Add an "Insecure Download" error and overwrite the start time for + // serialization + download.hasBlockedData = true; + download.error = DownloadError.fromSerializable({ + becauseBlockedByReputationCheck: true, + reputationCheckVerdict: "Insecure", + }); + download.startTime = startTime; + + let targetPath = download.target.path; + + // Add download to store, save, load and retrieve deserialized download list + listForSave.add(download); + await storeForSave.save(); + await storeForLoad.load(); + let loadedDownloadList = await listForLoad.getAll(); + + return [loadedDownloadList, targetPath]; + }; + + // Insecure downloads that are older than 5 minutes should get removed from + // the download-store and the file should get deleted. (360000 = 6 minutes) + let [loadedDownloadList1, targetPath1] = await createTestDownload( + new Date(Date.now() - 360000) + ); + + Assert.equal(loadedDownloadList1.length, 0, "Download should be removed"); + Assert.ok( + !(await IOUtils.exists(targetPath1)), + "The file should have been deleted." + ); + + // Insecure downloads that are newer than 5 minutes should stay in the + // download store and the file should remain. + let [loadedDownloadList2, targetPath2] = await createTestDownload(new Date()); + + Assert.equal(loadedDownloadList2.length, 1, "Download should be kept"); + Assert.ok( + await IOUtils.exists(targetPath2), + "The file should have not been deleted." + ); +}); |