From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../downloads/test/unit/test_DownloadHistory.js | 273 +++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 toolkit/components/downloads/test/unit/test_DownloadHistory.js (limited to 'toolkit/components/downloads/test/unit/test_DownloadHistory.js') diff --git a/toolkit/components/downloads/test/unit/test_DownloadHistory.js b/toolkit/components/downloads/test/unit/test_DownloadHistory.js new file mode 100644 index 0000000000..69eb1c4728 --- /dev/null +++ b/toolkit/components/downloads/test/unit/test_DownloadHistory.js @@ -0,0 +1,273 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests the DownloadHistory module. + */ + +"use strict"; + +const { DownloadHistory } = ChromeUtils.importESModule( + "resource://gre/modules/DownloadHistory.sys.mjs" +); + +let baseDate = new Date("2000-01-01"); + +/** + * Non-fatal assertion used to test whether the downloads in the list already + * match the expected state. + */ +function areEqual(a, b) { + if (a === b) { + Assert.equal(a, b); + return true; + } + info(a + " !== " + b); + return false; +} + +/** + * This allows waiting for an expected list at various points during the test. + */ +class TestView { + constructor(expected) { + this.expected = [...expected]; + this.downloads = []; + this.resolveWhenExpected = () => {}; + } + onDownloadAdded(download, options = {}) { + if (options.insertBefore) { + let index = this.downloads.indexOf(options.insertBefore); + this.downloads.splice(index, 0, download); + } else { + this.downloads.push(download); + } + this.checkForExpectedDownloads(); + } + onDownloadChanged(download) { + this.checkForExpectedDownloads(); + } + onDownloadRemoved(download) { + let index = this.downloads.indexOf(download); + this.downloads.splice(index, 1); + this.checkForExpectedDownloads(); + } + checkForExpectedDownloads() { + // Wait for all the expected downloads to be added or removed before doing + // the detailed tests. This is done to avoid creating irrelevant output. + if (this.downloads.length != this.expected.length) { + return; + } + for (let i = 0; i < this.downloads.length; i++) { + if ( + this.downloads[i].source.url != this.expected[i].source.url || + this.downloads[i].target.path != this.expected[i].target.path + ) { + return; + } + } + // Check and report the actual state of the downloads. Even if the items + // are in the expected order, the metadata for history downloads might not + // have been updated to the final state yet. + for (let i = 0; i < this.downloads.length; i++) { + let download = this.downloads[i]; + let testDownload = this.expected[i]; + info( + "Checking download source " + + download.source.url + + " with target " + + download.target.path + ); + if ( + !areEqual(download.succeeded, !!testDownload.succeeded) || + !areEqual(download.canceled, !!testDownload.canceled) || + !areEqual(download.hasPartialData, !!testDownload.hasPartialData) || + !areEqual(!!download.error, !!testDownload.error) + ) { + return; + } + // If the above properties match, the error details should be correct. + if (download.error) { + if (testDownload.error.becauseSourceFailed) { + Assert.equal(download.error.message, "History download failed."); + } + Assert.equal( + download.error.becauseBlockedByParentalControls, + testDownload.error.becauseBlockedByParentalControls + ); + Assert.equal( + download.error.becauseBlockedByReputationCheck, + testDownload.error.becauseBlockedByReputationCheck + ); + } + } + this.resolveWhenExpected(); + } + async waitForExpected() { + let promise = new Promise(resolve => (this.resolveWhenExpected = resolve)); + this.checkForExpectedDownloads(); + await promise; + } +} + +/** + * Tests that various operations on session and history downloads are reflected + * by the DownloadHistoryList object, and that the order of results is correct. + */ +add_task(async function test_DownloadHistory() { + // Clean up at the beginning and at the end of the test. + async function cleanup() { + await PlacesUtils.history.clear(); + } + registerCleanupFunction(cleanup); + await cleanup(); + + let testDownloads = [ + // History downloads should appear in order at the beginning of the list. + { offset: 10, canceled: true }, + { offset: 20, succeeded: true }, + { offset: 30, error: { becauseSourceFailed: true } }, + { offset: 40, error: { becauseBlockedByParentalControls: true } }, + { offset: 50, error: { becauseBlockedByReputationCheck: true } }, + // Session downloads should show up after all the history download, in the + // same order as they were added. + { offset: 45, canceled: true, inSession: true }, + { offset: 35, canceled: true, hasPartialData: true, inSession: true }, + { offset: 55, succeeded: true, inSession: true }, + ]; + const NEXT_OFFSET = 60; + + let publicList = await promiseNewList(); + let allList = await Downloads.getList(Downloads.ALL); + + async function addTestDownload(properties) { + properties.source = { + url: httpUrl("source" + properties.offset), + isPrivate: properties.isPrivate, + }; + let targetFile = getTempFile(TEST_TARGET_FILE_NAME + properties.offset); + properties.target = { path: targetFile.path }; + properties.startTime = new Date(baseDate.getTime() + properties.offset); + + let download = await Downloads.createDownload(properties); + if (properties.inSession) { + await allList.add(download); + } + + if (properties.isPrivate) { + return; + } + + // Add the download to history using the XPCOM service, then use the + // DownloadHistory module to save the associated metadata. + let promiseFileAnnotation = waitForAnnotation( + properties.source.url, + "downloads/destinationFileURI" + ); + let promiseMetaAnnotation = waitForAnnotation( + properties.source.url, + "downloads/metaData" + ); + let promiseVisit = promiseWaitForVisit(properties.source.url); + await DownloadHistory.addDownloadToHistory(download); + await promiseVisit; + await DownloadHistory.updateMetaData(download); + await Promise.all([promiseFileAnnotation, promiseMetaAnnotation]); + } + + // Add all the test downloads to history. + for (let properties of testDownloads) { + await addTestDownload(properties); + } + + // Initialize DownloadHistoryList only after having added the history and + // session downloads, and check that they are loaded in the correct order. + let historyList = await DownloadHistory.getList(); + let view = new TestView(testDownloads); + await historyList.addView(view); + await view.waitForExpected(); + + // Remove a download from history and verify that the change is reflected. + let downloadToRemove = view.expected[1]; + view.expected.splice(1, 1); + await PlacesUtils.history.remove(downloadToRemove.source.url); + await view.waitForExpected(); + + // Add a download to history and verify it's placed before session downloads, + // even if the start date is more recent. + let downloadToAdd = { offset: NEXT_OFFSET, canceled: true }; + view.expected.splice( + view.expected.findIndex(d => d.inSession), + 0, + downloadToAdd + ); + await addTestDownload(downloadToAdd); + await view.waitForExpected(); + + // Add a session download and verify it's placed after all session downloads, + // even if the start date is less recent. + let sessionDownloadToAdd = { offset: 0, inSession: true, succeeded: true }; + view.expected.push(sessionDownloadToAdd); + await addTestDownload(sessionDownloadToAdd); + await view.waitForExpected(); + + // Add a session download for the same URI without a history entry, and verify + // it's visible and placed after all session downloads. + view.expected.push(sessionDownloadToAdd); + await publicList.add(await Downloads.createDownload(sessionDownloadToAdd)); + await view.waitForExpected(); + + // Create a new DownloadHistoryList that also shows private downloads. Since + // we only have public downloads, the two lists should contain the same items. + let allHistoryList = await DownloadHistory.getList({ type: Downloads.ALL }); + let allView = new TestView(view.expected); + await allHistoryList.addView(allView); + await allView.waitForExpected(); + + // Add a new private download and verify it appears only on the complete list. + let privateDownloadToAdd = { + offset: NEXT_OFFSET + 10, + inSession: true, + succeeded: true, + isPrivate: true, + }; + allView.expected.push(privateDownloadToAdd); + await addTestDownload(privateDownloadToAdd); + await view.waitForExpected(); + await allView.waitForExpected(); + + // Now test the maxHistoryResults parameter. + let allHistoryList2 = await DownloadHistory.getList({ + type: Downloads.ALL, + maxHistoryResults: 3, + }); + // Prepare the set of downloads to contain fewer history downloads by removing + // the oldest ones. + let allView2 = new TestView(allView.expected.slice(3)); + await allHistoryList2.addView(allView2); + await allView2.waitForExpected(); + + // Create a dummy list and view like the previous limited one to just add and + // remove its view to make sure it doesn't break other lists' updates. + let dummyList = await DownloadHistory.getList({ + type: Downloads.ALL, + maxHistoryResults: 3, + }); + let dummyView = new TestView([]); + await dummyList.addView(dummyView); + await dummyList.removeView(dummyView); + + // Clear history and check that session downloads with partial data remain. + // Private downloads are also not cleared when clearing history. + view.expected = view.expected.filter(d => d.hasPartialData); + allView.expected = allView.expected.filter( + d => d.hasPartialData || d.isPrivate + ); + await PlacesUtils.history.clear(); + await view.waitForExpected(); + await allView.waitForExpected(); + + // Check that the dummy view above did not prevent the limited from updating. + allView2.expected = allView.expected; + await allView2.waitForExpected(); +}); -- cgit v1.2.3