diff options
Diffstat (limited to 'toolkit/mozapps/update/tests/browser')
98 files changed, 5617 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/tests/browser/browser.bits.ini b/toolkit/mozapps/update/tests/browser/browser.bits.ini new file mode 100644 index 0000000000..a9b812680c --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser.bits.ini @@ -0,0 +1,81 @@ +[DEFAULT] +skip-if = + os != 'win' + msix # Updater is disabled in MSIX builds +reason = BITS is only available on Windows. +dupe-manifest = +tags = appupdate bits +head = head.js +support-files = + ../data/shared.js + ../data/sharedUpdateXML.js + ../data/app_update.sjs + downloadPage.html + testConstants.js + +prefs = + app.update.BITS.enabled=true + app.update.langpack.enabled=true + +# BITS Download Tests +##################### + +# About Dialog Application Update Tests +[browser_aboutDialog_bc_downloading.js] +[browser_aboutDialog_bc_downloading_staging.js] +[browser_aboutDialog_bc_downloading_notify.js] +[browser_aboutDialog_bc_downloaded.js] +[browser_aboutDialog_bc_downloaded_staging.js] +[browser_aboutDialog_bc_downloaded_staged.js] +[browser_aboutDialog_bc_downloaded_stagingFailure.js] +[browser_aboutDialog_fc_downloadAuto.js] +[browser_aboutDialog_fc_downloadAuto_staging.js] +[browser_aboutDialog_fc_downloadOptIn.js] +[browser_aboutDialog_fc_downloadOptIn_staging.js] +[browser_aboutDialog_fc_patch_completeBadSize.js] +[browser_aboutDialog_fc_patch_partialBadSize.js] +[browser_aboutDialog_fc_patch_partialBadSize_complete.js] +[browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js] +[browser_aboutDialog_bc_multiUpdate.js] + +# about:preferences Application Update Tests +[browser_aboutPrefs_bc_downloading.js] +[browser_aboutPrefs_bc_downloading_staging.js] +[browser_aboutPrefs_bc_downloaded.js] +[browser_aboutPrefs_bc_downloaded_staging.js] +[browser_aboutPrefs_bc_downloaded_stagingFailure.js] +[browser_aboutPrefs_bc_downloaded_staged.js] +[browser_aboutPrefs_fc_downloadAuto.js] +[browser_aboutPrefs_fc_downloadAuto_staging.js] +[browser_aboutPrefs_fc_downloadOptIn.js] +[browser_aboutPrefs_fc_downloadOptIn_staging.js] +[browser_aboutPrefs_fc_patch_completeBadSize.js] +[browser_aboutPrefs_fc_patch_partialBadSize.js] +[browser_aboutPrefs_fc_patch_partialBadSize_complete.js] +[browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js] +[browser_aboutPrefs_bc_multiUpdate.js] + +# Doorhanger Application Update Tests +[browser_doorhanger_bc_downloaded.js] +[browser_doorhanger_bc_downloaded_staged.js] +[browser_doorhanger_bc_downloadAutoFailures.js] +[browser_doorhanger_bc_downloadAutoFailures_bgWin.js] +[browser_doorhanger_bc_downloadOptIn.js] +[browser_doorhanger_bc_downloadOptIn_bgWin.js] +[browser_doorhanger_bc_downloadOptIn_staging.js] +[browser_doorhanger_bc_patch_completeBadSize.js] +[browser_doorhanger_bc_patch_partialBadSize.js] +[browser_doorhanger_bc_patch_partialBadSize_complete.js] +[browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js] +[browser_doorhanger_sp_patch_completeApplyFailure.js] +[browser_doorhanger_sp_patch_partialApplyFailure.js] +[browser_doorhanger_sp_patch_partialApplyFailure_complete.js] +[browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js] +[browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js] +[browser_doorhanger_bc_downloaded_disableBITS.js] +[browser_doorhanger_bc_multiUpdate.js] +[browser_doorhanger_bc_multiUpdate_promptWaitTime.js] + +# Telemetry Update Ping Tests +[browser_telemetry_updatePing_downloaded_ready.js] +[browser_telemetry_updatePing_staged_ready.js] diff --git a/toolkit/mozapps/update/tests/browser/browser.ini b/toolkit/mozapps/update/tests/browser/browser.ini new file mode 100644 index 0000000000..07b297471a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser.ini @@ -0,0 +1,119 @@ +[DEFAULT] +dupe-manifest = +tags = appupdate internal +head = head.js +support-files = + ../data/shared.js + ../data/sharedUpdateXML.js + ../data/app_update.sjs + downloadPage.html + testConstants.js +prefs = + app.update.BITS.enabled=false + app.update.langpack.enabled=true +skip-if = os == 'win' && msix # Updater is disabled in MSIX builds + +# About Dialog Application Update Tests +[browser_aboutDialog_bc_downloading.js] +[browser_aboutDialog_bc_downloading_staging.js] +[browser_aboutDialog_bc_downloading_notify.js] +[browser_aboutDialog_bc_downloaded.js] +[browser_aboutDialog_bc_downloaded_staging.js] +[browser_aboutDialog_bc_downloaded_stagingFailure.js] +[browser_aboutDialog_bc_downloaded_staged.js] +[browser_aboutDialog_fc_apply_blocked.js] +[browser_aboutDialog_fc_downloadAuto.js] +skip-if = tsan # Bug 1683730 +[browser_aboutDialog_fc_downloadAuto_staging.js] +[browser_aboutDialog_fc_downloadOptIn.js] +[browser_aboutDialog_fc_downloadOptIn_staging.js] +[browser_aboutDialog_fc_patch_completeBadSize.js] +[browser_aboutDialog_fc_patch_partialBadSize.js] +[browser_aboutDialog_fc_patch_partialBadSize_complete.js] +[browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js] +[browser_aboutDialog_fc_check_cantApply.js] +skip-if = os != 'win' +reason = test must be able to prevent file deletion. +[browser_aboutDialog_fc_network_failure.js] +[browser_aboutDialog_fc_network_offline.js] +[browser_aboutDialog_fc_check_malformedXML.js] +[browser_aboutDialog_fc_check_noUpdate.js] +[browser_aboutDialog_fc_check_otherInstance.js] +skip-if = os != 'win' +reason = Windows only feature. +[browser_aboutDialog_fc_check_unsupported.js] +[browser_aboutDialog_bc_multiUpdate.js] +[browser_aboutDialog_internalError.js] +[browser_aboutDialog_AppUpdater_stop_checking.js] +[browser_aboutDialog_AppUpdater_stop_no_update.js] +[browser_aboutDialog_AppUpdater_stop_download_and_install.js] +[browser_aboutDialog_AppUpdater_stop_downloading.js] +[browser_aboutDialog_AppUpdater_stop_download_failed.js] +[browser_aboutDialog_AppUpdater_stop_staging.js] +[browser_aboutDialog_AppUpdater_stop_ready_for_restart.js] +[browser_aboutDialog_AppUpdater_stop_internal_error.js] +[browser_aboutDialog_AppUpdater_stop_swap.js] + +# about:preferences Application Update Tests +[browser_aboutPrefs_fc_apply_blocked.js] +[browser_aboutPrefs_bc_downloading.js] +[browser_aboutPrefs_bc_downloading_staging.js] +[browser_aboutPrefs_bc_downloaded.js] +[browser_aboutPrefs_bc_downloaded_staging.js] +[browser_aboutPrefs_bc_downloaded_stagingFailure.js] +[browser_aboutPrefs_bc_downloaded_staged.js] +[browser_aboutPrefs_fc_downloadAuto.js] +[browser_aboutPrefs_fc_downloadAuto_staging.js] +[browser_aboutPrefs_fc_downloadOptIn.js] +[browser_aboutPrefs_fc_downloadOptIn_staging.js] +[browser_aboutPrefs_fc_patch_completeBadSize.js] +[browser_aboutPrefs_fc_patch_partialBadSize.js] +[browser_aboutPrefs_fc_patch_partialBadSize_complete.js] +[browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js] +[browser_aboutPrefs_fc_check_cantApply.js] +skip-if = os != 'win' +reason = test must be able to prevent file deletion. +[browser_aboutPrefs_fc_network_failure.js] +[browser_aboutPrefs_fc_network_offline.js] +[browser_aboutPrefs_fc_check_malformedXML.js] +[browser_aboutPrefs_fc_check_noUpdate.js] +[browser_aboutPrefs_fc_check_otherInstance.js] +skip-if = os != 'win' +reason = Windows only feature. +[browser_aboutPrefs_fc_check_unsupported.js] +[browser_aboutPrefs_settings.js] +[browser_aboutPrefs_bc_multiUpdate.js] +[browser_aboutPrefs_backgroundUpdateSetting.js] +[browser_aboutPrefs_internalError.js] + +# Doorhanger Application Update Tests +[browser_doorhanger_bc_check_cantApply.js] +skip-if = os != 'win' +reason = test must be able to prevent file deletion. +[browser_doorhanger_bc_check_malformedXML.js] +[browser_doorhanger_bc_check_unsupported.js] +[browser_doorhanger_bc_downloaded.js] +[browser_doorhanger_bc_downloaded_staged.js] +[browser_doorhanger_bc_downloadAutoFailures.js] +[browser_doorhanger_bc_downloadAutoFailures_bgWin.js] +[browser_doorhanger_bc_downloadOptIn.js] +[browser_doorhanger_bc_downloadOptIn_bgWin.js] +[browser_doorhanger_bc_downloadOptIn_staging.js] +[browser_doorhanger_bc_patch_completeBadSize.js] +[browser_doorhanger_bc_patch_partialBadSize.js] +[browser_doorhanger_bc_patch_partialBadSize_complete.js] +[browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js] +[browser_doorhanger_sp_patch_completeApplyFailure.js] +[browser_doorhanger_sp_patch_partialApplyFailure.js] +[browser_doorhanger_sp_patch_partialApplyFailure_complete.js] +[browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js] +[browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js] +[browser_doorhanger_bc_multiUpdate.js] +[browser_doorhanger_bc_multiUpdate_promptWaitTime.js] + +# Elevation Dialog Tests +[browser_elevationDialog.js] + +# Telemetry Update Ping Tests +[browser_telemetry_updatePing_downloaded_ready.js] +[browser_telemetry_updatePing_staged_ready.js] diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js new file mode 100644 index 0000000000..370f658656 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the checking state causes the interface to return to the `NEVER_CHECKED` +// state. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_checking() { + let params = { queryString: "&noUpdates=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + // Omit the continue file to keep us in the checking state. + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "checkForUpdates", + }, + ]); + + // Ideally this would go in a cleanup function. But this needs to happen + // before any other cleanup functions and for some reason cleanup functions + // do not always seem to execute in reverse registration order. + dump("Cleanup: Waiting for checking to finish.\n"); + await continueFileHandler(CONTINUE_CHECK); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js new file mode 100644 index 0000000000..74e179043d --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the "download and install" state causes the interface to return to the +// `NEVER_CHECKED` state. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_download_and_install() { + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + noContinue: true, + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "checkForUpdates", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js new file mode 100644 index 0000000000..8900765f81 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the "downloadFailed" state doesn't cause a shift to any other state, such +// as internal error. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_download_failed() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult }; + } else { + downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult }; + } + + let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "downloadFailed", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js new file mode 100644 index 0000000000..d0de7e03b9 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` during +// the downloading state causes the interface to return to the `NEVER_CHECKED` +// state. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_downloading() { + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + // Omit continue file to keep the UI in the downloading state. + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "checkForUpdates", + // The update will still be in the downloading state even though + // AppUpdater has stopped because stopping AppUpdater doesn't stop the + // Application Update Service from continuing with the update. + checkActiveUpdate: { state: STATE_DOWNLOADING }, + expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_DOWNLOADING, + }, + ]); + + // Ideally this would go in a cleanup function. But this needs to happen + // before any other cleanup functions and for some reason cleanup functions + // do not always seem to execute in reverse registration order. + dump("Cleanup: Waiting for downloading to finish.\n"); + await continueFileHandler(CONTINUE_DOWNLOAD); + if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_DOWNLOADING) { + await gAUS.stateTransition; + } +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js new file mode 100644 index 0000000000..70fbce97b2 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +XPCOMUtils.defineLazyModuleGetters(this, { + AppUpdater: "resource://gre/modules/AppUpdater.jsm", + sinon: "resource://testing-common/Sinon.jsm", +}); + +add_setup(function setup_internalErrorTest() { + const sandbox = sinon.createSandbox(); + sandbox.stub(AppUpdater.prototype, "aus").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "checker").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "um").get(() => { + throw new Error("intentional test error"); + }); + registerCleanupFunction(() => { + sandbox.restore(); + }); +}); + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the "internal error" state doesn't cause a shift to any other state. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_internal_error() { + let params = {}; + await runAboutDialogUpdateTest(params, [ + { + panelId: "internalError", + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "internalError", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js new file mode 100644 index 0000000000..65a52ccc87 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the "noUpdatesFound" state doesn't cause a shift to any other state, such +// as internal error. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_no_update() { + let params = { queryString: "&noUpdates=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + continueFile: CONTINUE_CHECK, + }, + { + panelId: "noUpdatesFound", + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "noUpdatesFound", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js new file mode 100644 index 0000000000..8c9d1f788f --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the "ready for restart" state doesn't cause a shift to any other state, +// such as internal error. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_ready_for_restart() { + let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js new file mode 100644 index 0000000000..dd822e6391 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js @@ -0,0 +1,64 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that while a download is in-progress, calling `AppUpdater.stop()` while +// in the staging state causes the interface to return to the `NEVER_CHECKED` +// state. +// This is less a test of the About dialog than of AppUpdater, but it's easier +// to test it via the About dialog just because there is already a testing +// framework for the About dialog. +add_task(async function aboutDialog_AppUpdater_stop_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + // Don't pass a continue file in order to leave us in the staging state. + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "checkForUpdates", + // The update will still be in the staging state even though AppUpdater + // has stopped because stopping AppUpdater doesn't stop the Application + // Update Service from continuing with the update. + checkActiveUpdate: { state: STATE_PENDING }, + expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_STAGING, + }, + ]); + + // Ideally this would go in a cleanup function. But this needs to happen + // before any other cleanup functions and for some reason cleanup functions + // do not always seem to execute in reverse registration order. + dump("Cleanup: Waiting for staging to finish.\n"); + await continueFileHandler(CONTINUE_STAGING); + if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_STAGING) { + await gAUS.stateTransition; + } +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js new file mode 100644 index 0000000000..02441dea53 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js @@ -0,0 +1,64 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const FIRST_UPDATE_VERSION = "999998.0"; +const SECOND_UPDATE_VERSION = "999999.0"; + +function prepareToDownloadVersion(version) { + setUpdateURL( + URL_HTTP_UPDATE_SJS + + `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}` + ); +} + +add_task(async function aboutDialog_backgroundCheck_multiUpdate() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let params = { + version: FIRST_UPDATE_VERSION, + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + () => { + prepareToDownloadVersion(SECOND_UPDATE_VERSION); + gAUS.checkForBackgroundUpdates(); + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + // Don't pass a continue file in order to leave us in the staging state. + }, + aboutDialog => { + aboutDialog.gAppUpdater._appUpdater.stop(); + }, + { + panelId: "checkForUpdates", + checkActiveUpdate: { state: STATE_PENDING }, + expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_STAGING, + }, + ]); + + // Ideally this would go in a cleanup function. But this needs to happen + // before any other cleanup functions and for some reason cleanup functions + // do not always seem to execute in reverse registration order. + dump("Cleanup: Waiting for staging to finish.\n"); + await continueFileHandler(CONTINUE_STAGING); + if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_STAGING) { + await gAUS.stateTransition; + } +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js new file mode 100644 index 0000000000..0955750c93 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the update downloaded when the About Dialog is opened. +add_task(async function aboutDialog_backgroundCheck_downloaded() { + let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js new file mode 100644 index 0000000000..2d7bd64d05 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the update downloaded and staged when the About Dialog is opened. +add_task(async function aboutDialog_backgroundCheck_downloaded_staged() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&invalidCompleteSize=1", + backgroundUpdate: true, + continueFile: CONTINUE_STAGING, + waitForUpdateState: STATE_APPLIED, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js new file mode 100644 index 0000000000..b60a8f128d --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the update downloaded and the About Dialog opened during staging. +add_task(async function aboutDialog_backgroundCheck_downloaded_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + async aboutDialog => { + // Once the state is applied but langpacks aren't complete the about + // dialog should still be showing applying. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_APPLIED; + }); + + is( + aboutDialog.gAppUpdater.selectedPanel.id, + "applying", + "UI should still show as applying." + ); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js new file mode 100644 index 0000000000..c05b2daa74 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the update downloaded and staging has failed when the About Dialog is +// opened. +add_task( + async function aboutDialog_backgroundCheck_downloaded_stagingFailure() { + Services.env.set("MOZ_TEST_STAGING_ERROR", "1"); + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&completePatchOnly=1", + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); + } +); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js new file mode 100644 index 0000000000..6c2a7486a9 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the About Dialog opened during downloading. +add_task(async function aboutDialog_backgroundCheck_downloading() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_NOTIFYDURINGDOWNLOAD, false]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutDialogUpdateTest(params, [ + async function aboutDialog_downloading() { + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + !PanelUI.menuButton.hasAttribute("badge-status"), + "The window does not have a badge." + ); + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + async aboutDialog => { + // Once the state is pending but langpacks aren't complete the about + // dialog should still be showing downloading. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_PENDING; + }); + + is( + aboutDialog.gAppUpdater.selectedPanel.id, + "downloading", + "UI should still show as downloading." + ); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js new file mode 100644 index 0000000000..cf067efe7d --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates with the +// "notify during download" feature turned on. +add_task(async function aboutDialog_backgroundCheck_downloading_notify() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_NOTIFYDURINGDOWNLOAD, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutDialogUpdateTest(params, [ + async function aboutDialog_downloading_notification() { + await TestUtils.waitForCondition( + () => PanelUI.menuButton.hasAttribute("badge-status"), + "Waiting for update badge", + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test. + logTestInfo(e); + }); + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "The window has a badge." + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-downloading", + "The downloading badge is showing for the background window" + ); + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js new file mode 100644 index 0000000000..3f6b476e1b --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog background check for updates +// with the About Dialog opened during downloading and stages the update. +add_task(async function aboutDialog_backgroundCheck_downloading_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + async aboutDialog => { + // Once the state is applied but langpacks aren't complete the about + // dialog should still be showing applying. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_APPLIED; + }); + + is( + aboutDialog.gAppUpdater.selectedPanel.id, + "applying", + "UI should still show as applying." + ); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js new file mode 100644 index 0000000000..b6c893ea15 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const FIRST_UPDATE_VERSION = "999998.0"; +const SECOND_UPDATE_VERSION = "999999.0"; + +function prepareToDownloadVersion(version) { + setUpdateURL( + URL_HTTP_UPDATE_SJS + + `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}` + ); +} + +add_task(async function aboutDialog_backgroundCheck_multiUpdate() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let params = { + version: FIRST_UPDATE_VERSION, + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + () => { + prepareToDownloadVersion(SECOND_UPDATE_VERSION); + gAUS.checkForBackgroundUpdates(); + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js new file mode 100644 index 0000000000..59171cf781 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js @@ -0,0 +1,94 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { PromptTestUtils } = ChromeUtils.import( + "resource://testing-common/PromptTestUtils.jsm" +); + +const BUILDER_URL = "https://example.com/document-builder.sjs?html="; +const PAGE_MARKUP = ` +<html> +<head> + <script> + window.onbeforeunload = function() { + return true; + }; + </script> +</head> +<body>TEST PAGE</body> +</html> +`; +const TEST_URL = BUILDER_URL + encodeURI(PAGE_MARKUP); + +add_setup(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); +}); + +// Test for About Dialog foreground check for updates +// and apply but restart is blocked by a page. +add_task(async function aboutDialog_foregroundCheck_apply_blocked() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let aboutDialog; + let handlePromise = (async () => { + let dialog = await PromptTestUtils.waitForPrompt(window, { + modalType: Ci.nsIPrompt.MODAL_TYPE_CONTENT, + promptType: "confirmEx", + }); + Assert.equal( + aboutDialog.gAppUpdater.selectedPanel.id, + "restarting", + "The restarting panel should be displayed" + ); + + await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 }); + })(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + async function getAboutDialogHandle(dialog) { + aboutDialog = dialog; + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + forceApply: true, + }, + async function ensureDialogHasBeenCanceled() { + await handlePromise; + }, + // A final check to ensure that we are back in the apply state. + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); + + BrowserTestUtils.removeTab(tab, { skipPermitUnload: true }); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js new file mode 100644 index 0000000000..cd659ef74b --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// without the ability to apply updates. +add_task(async function aboutDialog_foregroundCheck_cantApply() { + lockWriteTestFile(); + + let params = {}; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "manualUpdate", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js new file mode 100644 index 0000000000..529a4c7a63 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a malformed update XML file. +add_task(async function aboutDialog_foregroundCheck_malformedXML() { + let params = { queryString: "&xmlMalformed=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "checkingFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js new file mode 100644 index 0000000000..2bd23cddf0 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with no update available. +add_task(async function aboutDialog_foregroundCheck_noUpdate() { + let params = { queryString: "&noUpdates=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "noUpdatesFound", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js new file mode 100644 index 0000000000..fa1110effd --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with another application instance handling updates. +add_task(async function aboutDialog_foregroundCheck_otherInstance() { + setOtherInstanceHandlingUpdates(); + + let params = {}; + await runAboutDialogUpdateTest(params, [ + { + panelId: "otherInstanceHandlingUpdates", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js new file mode 100644 index 0000000000..22e3425967 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with an unsupported update. +add_task(async function aboutDialog_foregroundCheck_unsupported() { + let params = { queryString: "&unsupported=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "unsupportedSystem", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js new file mode 100644 index 0000000000..a80c9deac8 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with an automatic download. +add_task(async function aboutDialog_foregroundCheck_downloadAuto() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + async function aboutDialog_restart_notification() { + await TestUtils.waitForCondition( + () => PanelUI.menuButton.hasAttribute("badge-status"), + "Waiting for update badge", + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test. + logTestInfo(e); + }); + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "The window has a badge." + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-restart", + "The restart badge is showing for the background window" + ); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js new file mode 100644 index 0000000000..e212b0c611 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with an automatic download and update staging. +add_task(async function aboutDialog_foregroundCheck_downloadAuto_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js new file mode 100644 index 0000000000..5a2ff513ad --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a manual download. +add_task(async function aboutDialog_foregroundCheck_downloadOptIn() { + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js new file mode 100644 index 0000000000..3ed331ec64 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a manual download and update staging. +add_task(async function aboutDialog_foregroundCheck_downloadOptIn_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js new file mode 100644 index 0000000000..8e25b96d7f --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates which fails, because it is +// impossible to connect to the update server +add_task(async function aboutDialog_foregroundCheck_network_failure() { + let params = { + baseURL: "https://localhost:7777", + }; + + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingFailed", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js new file mode 100644 index 0000000000..9f81573e23 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates which fails, because the +// browser is in offline mode and `localhost` cannot be resolved. +add_task(async function aboutDialog_foregroundCheck_network_offline() { + info("[OFFLINE] setting Services.io.offline (do not forget to reset it!)"); + // avoid that real network connectivity changes influence the test execution + Services.io.manageOfflineStatus = false; + Services.io.offline = true; + registerCleanupFunction(() => { + info("[ONLINE] Resetting Services.io.offline"); + Services.io.offline = false; + Services.io.manageOfflineStatus = true; + }); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["network.disable-localhost-when-offline", true], + ["network.dns.offline-localhost", false], + ], + }); + + await runAboutDialogUpdateTest({}, [ + { + panelId: "checkingFailed", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js new file mode 100644 index 0000000000..fdc7d3407e --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a complete bad size patch. +add_task(async function aboutDialog_foregroundCheck_completeBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult }; + } else { + downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult }; + } + + let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js new file mode 100644 index 0000000000..411fb25969 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a partial bad size patch. +add_task(async function aboutDialog_foregroundCheck_partialBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult }; + } + + let params = { queryString: "&partialPatchOnly=1&invalidPartialSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js new file mode 100644 index 0000000000..396be1e930 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a partial bad size patch and a complete patch. +add_task(async function aboutDialog_foregroundCheck_partialBadSize_complete() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult }; + downloadInfo[2] = { patchType: "complete", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "complete", internalResult: "0" }; + } + + let params = { queryString: "&invalidPartialSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js new file mode 100644 index 0000000000..fb10250a49 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for About Dialog foreground check for updates +// with a partial bad size patch and a complete bad size patch. +add_task( + async function aboutDialog_foregroundCheck_partialBadSize_completeBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { + patchType: "partial", + internalResult: gBadSizeResult, + }; + downloadInfo[2] = { patchType: "complete", bitsResult: gBadSizeResult }; + downloadInfo[3] = { + patchType: "complete", + internalResult: gBadSizeResult, + }; + } else { + downloadInfo[0] = { + patchType: "partial", + internalResult: gBadSizeResult, + }; + downloadInfo[1] = { + patchType: "complete", + internalResult: gBadSizeResult, + }; + } + + let params = { queryString: "&invalidPartialSize=1&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); + } +); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js new file mode 100644 index 0000000000..fa59650afb --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +XPCOMUtils.defineLazyModuleGetters(this, { + AppUpdater: "resource://gre/modules/AppUpdater.jsm", + sinon: "resource://testing-common/Sinon.jsm", +}); + +add_setup(function setup_internalErrorTest() { + const sandbox = sinon.createSandbox(); + sandbox.stub(AppUpdater.prototype, "aus").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "checker").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "um").get(() => { + throw new Error("intentional test error"); + }); + registerCleanupFunction(() => { + sandbox.restore(); + }); +}); + +// Test for the About dialog's internal error handling. +add_task(async function aboutDialog_internalError() { + let params = {}; + await runAboutDialogUpdateTest(params, [ + { + panelId: "internalError", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js new file mode 100644 index 0000000000..cd57efe985 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js @@ -0,0 +1,175 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + */ +"use strict"; + +/** + * This file tests the background update UI in about:preferences. + */ + +ChromeUtils.defineESModuleGetters(this, { + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); + +const BACKGROUND_UPDATE_PREF = "app.update.background.enabled"; + +add_task(async function testBackgroundUpdateSettingUI() { + if (!AppConstants.MOZ_UPDATE_AGENT) { + // The element that we are testing in about:preferences is #ifdef'ed out of + // the file if MOZ_UPDATE_AGENT isn't defined. So there is nothing to + // test in that case. + logTestInfo( + ` +=============================================================================== +WARNING! This test involves background update, but background tasks are + disabled. This test will unconditionally pass since the feature it + wants to test isn't available. +=============================================================================== +` + ); + // Some of our testing environments do not consider a test to have passed if + // it didn't make any assertions. + ok(true, "Unconditionally passing test"); + return; + } + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:preferences" + ); + + const originalBackgroundUpdateVal = await UpdateUtils.readUpdateConfigSetting( + BACKGROUND_UPDATE_PREF + ); + const originalUpdateAutoVal = await UpdateUtils.getAppUpdateAutoEnabled(); + registerCleanupFunction(async () => { + await BrowserTestUtils.removeTab(tab); + await UpdateUtils.writeUpdateConfigSetting( + BACKGROUND_UPDATE_PREF, + originalBackgroundUpdateVal + ); + await UpdateUtils.setAppUpdateAutoEnabled(originalUpdateAutoVal); + }); + + // If auto update is disabled, the control for background update should be + // disabled, since we cannot update in the background if we can't update + // automatically. + await UpdateUtils.setAppUpdateAutoEnabled(false); + await SpecialPowers.spawn( + tab.linkedBrowser, + [UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED], + async perInstallationPrefsSupported => { + let backgroundUpdateCheckbox = content.document.getElementById( + "backgroundUpdate" + ); + is( + backgroundUpdateCheckbox.hidden, + !perInstallationPrefsSupported, + `The background update UI should ${ + perInstallationPrefsSupported ? "not" : "" + } be hidden when and perInstallationPrefsSupported is ` + + `${perInstallationPrefsSupported}` + ); + if (perInstallationPrefsSupported) { + is( + backgroundUpdateCheckbox.disabled, + true, + `The background update UI should be disabled when auto update is ` + + `disabled` + ); + } + } + ); + + if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) { + // The remaining tests only make sense on platforms where per-installation + // prefs are supported and the UI will ever actually be displayed + return; + } + + await UpdateUtils.setAppUpdateAutoEnabled(true); + await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, true); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + let backgroundUpdateCheckbox = content.document.getElementById( + "backgroundUpdate" + ); + is( + backgroundUpdateCheckbox.disabled, + false, + `The background update UI should not be disabled when auto update is ` + + `enabled` + ); + + is( + backgroundUpdateCheckbox.checked, + true, + "After enabling background update, the checkbox should be checked" + ); + + // Note that this action results in asynchronous activity. Normally when + // we change the update config, we await on the function to wait for the + // value to be written to the disk. We can't easily await on the UI state + // though. Luckily, we don't have to because reads/writes of the config file + // are serialized. So when we verify the written value by awaiting on + // readUpdateConfigSetting(), that will also wait for the value to be + // written to disk and for this UI to react to that. + backgroundUpdateCheckbox.click(); + }); + + is( + await UpdateUtils.readUpdateConfigSetting(BACKGROUND_UPDATE_PREF), + false, + "Toggling the checkbox should have changed the setting value to false" + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + let backgroundUpdateCheckbox = content.document.getElementById( + "backgroundUpdate" + ); + is( + backgroundUpdateCheckbox.checked, + false, + "After toggling the checked checkbox, it should be unchecked." + ); + + // Like the last call like this one, this initiates asynchronous behavior. + backgroundUpdateCheckbox.click(); + }); + + is( + await UpdateUtils.readUpdateConfigSetting(BACKGROUND_UPDATE_PREF), + true, + "Toggling the checkbox should have changed the setting value to true" + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + is( + content.document.getElementById("backgroundUpdate").checked, + true, + "After toggling the unchecked checkbox, it should be checked" + ); + }); + + // Test that the UI reacts to observed setting changes properly. + await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, false); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + is( + content.document.getElementById("backgroundUpdate").checked, + false, + "Externally disabling background update should uncheck the checkbox" + ); + }); + + await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, true); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + is( + content.document.getElementById("backgroundUpdate").checked, + true, + "Externally enabling background update should check the checkbox" + ); + }); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js new file mode 100644 index 0000000000..6df4ee7ff5 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with the update downloaded when about:preferences is opened. +add_task(async function aboutPrefs_backgroundCheck_downloaded() { + let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js new file mode 100644 index 0000000000..749a8f0b07 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with the update downloaded and staged when about:preferences is opened. +add_task(async function aboutPrefs_backgroundCheck_downloaded_staged() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&invalidCompleteSize=1", + backgroundUpdate: true, + continueFile: CONTINUE_STAGING, + waitForUpdateState: STATE_APPLIED, + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js new file mode 100644 index 0000000000..73804cdf7a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with the update downloaded and about:preferences opened during staging. +add_task(async function aboutPrefs_backgroundCheck_downloaded_staged() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + async tab => { + // Once the state is pending but langpacks aren't complete the about + // dialog should still be showing downloading. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_APPLIED; + }); + + let updateDeckId = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => { + return content.gAppUpdater.selectedPanel.id; + } + ); + + is(updateDeckId, "applying", "UI should still show as applying."); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js new file mode 100644 index 0000000000..9755fe167b --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with the update downloaded and staging has failed when the about:preferences +// is opened. +add_task(async function aboutPrefs_backgroundCheck_downloaded_stagingFailure() { + Services.env.set("MOZ_TEST_STAGING_ERROR", "1"); + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&completePatchOnly=1", + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js new file mode 100644 index 0000000000..d9034296b0 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with about:preferences opened during downloading. +add_task(async function aboutPrefs_backgroundCheck_downloading() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + async tab => { + // Once the state is pending but langpacks aren't complete the about + // dialog should still be showing downloading. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_PENDING; + }); + + let updateDeckId = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => { + return content.gAppUpdater.selectedPanel.id; + } + ); + + is(updateDeckId, "downloading", "UI should still show as downloading."); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js new file mode 100644 index 0000000000..8085ec4c2c --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences background check for updates +// with about:preferences opened during downloading and stages the update. +add_task(async function aboutPrefs_backgroundCheck_downloading_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let lankpackCall = mockLangpackInstall(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { + queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1", + backgroundUpdate: true, + waitForUpdateState: STATE_DOWNLOADING, + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + async tab => { + // Once the state is pending but langpacks aren't complete the about + // dialog should still be showing downloading. + TestUtils.waitForCondition(() => { + return readStatusFile() == STATE_APPLIED; + }); + + let updateDeckId = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => { + return content.gAppUpdater.selectedPanel.id; + } + ); + + is(updateDeckId, "applying", "UI should still show as applying."); + + let { appVersion, resolve } = await lankpackCall; + is( + appVersion, + Services.appinfo.version, + "Should see the right app version." + ); + resolve(); + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js new file mode 100644 index 0000000000..9ccf593db5 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const FIRST_UPDATE_VERSION = "999998.0"; +const SECOND_UPDATE_VERSION = "999999.0"; + +function prepareToDownloadVersion(version) { + setUpdateURL( + URL_HTTP_UPDATE_SJS + + `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}` + ); +} + +add_task(async function aboutPrefs_backgroundCheck_multiUpdate() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let params = { + version: FIRST_UPDATE_VERSION, + backgroundUpdate: true, + waitForUpdateState: STATE_PENDING, + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + () => { + prepareToDownloadVersion(SECOND_UPDATE_VERSION); + gAUS.checkForBackgroundUpdates(); + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js new file mode 100644 index 0000000000..ccf3c06b95 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { PromptTestUtils } = ChromeUtils.import( + "resource://testing-common/PromptTestUtils.jsm" +); + +const BUILDER_URL = "https://example.com/document-builder.sjs?html="; +const PAGE_MARKUP = ` +<html> +<head> + <script> + window.onbeforeunload = function() { + return true; + }; + </script> +</head> +<body>TEST PAGE</body> +</html> +`; +const TEST_URL = BUILDER_URL + encodeURI(PAGE_MARKUP); + +add_setup(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); +}); + +// Test for About Dialog foreground check for updates +// and apply but restart is blocked by a page. +add_task(async function aboutDialog_foregroundCheck_apply_blocked() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + let prefsTab; + let handlePromise = (async () => { + let dialog = await PromptTestUtils.waitForPrompt(window, { + modalType: Ci.nsIPrompt.MODAL_TYPE_CONTENT, + promptType: "confirmEx", + }); + await SpecialPowers.spawn(prefsTab.linkedBrowser, [], async () => { + Assert.equal( + content.gAppUpdater.selectedPanel.id, + "restarting", + "The restarting panel should be displayed" + ); + }); + + await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 }); + })(); + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + async function getPrefsTab(tab) { + prefsTab = tab; + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + forceApply: true, + }, + async function ensureDialogHasBeenCanceled() { + await handlePromise; + }, + // A final check to ensure that we are back in the apply state. + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); + + BrowserTestUtils.removeTab(tab, { skipPermitUnload: true }); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js new file mode 100644 index 0000000000..aac9b33134 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// without the ability to apply updates. +add_task(async function aboutPrefs_foregroundCheck_cantApply() { + lockWriteTestFile(); + + let params = {}; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "manualUpdate", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js new file mode 100644 index 0000000000..6d99a58e86 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a malformed update XML file. +add_task(async function aboutPrefs_foregroundCheck_malformedXML() { + let params = { queryString: "&xmlMalformed=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "checkingFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js new file mode 100644 index 0000000000..a8f6c072f7 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with no update available. +add_task(async function aboutPrefs_foregroundCheck_noUpdate() { + let params = { queryString: "&noUpdates=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "noUpdatesFound", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js new file mode 100644 index 0000000000..271c8f2837 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with another application instance handling updates. +add_task(async function aboutPrefs_foregroundCheck_otherInstance() { + setOtherInstanceHandlingUpdates(); + + let params = {}; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "otherInstanceHandlingUpdates", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js new file mode 100644 index 0000000000..e18ce31bc4 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with an unsupported update. +add_task(async function aboutPrefs_foregroundCheck_unsupported() { + let params = { queryString: "&unsupported=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "unsupportedSystem", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js new file mode 100644 index 0000000000..bd5fd18289 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with an automatic download. +add_task(async function aboutPrefs_foregroundCheck_downloadAuto() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js new file mode 100644 index 0000000000..1d9d082edd --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with an automatic download and update staging. +add_task(async function aboutPrefs_foregroundCheck_downloadAuto_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js new file mode 100644 index 0000000000..115e875b74 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a manual download. +add_task(async function aboutPrefs_foregroundCheck_downloadOptIn() { + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js new file mode 100644 index 0000000000..cff5d74701 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a manual download and update staging. +add_task(async function aboutPrefs_foregroundCheck_downloadOptIn_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: "0" }; + } + + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "applying", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: CONTINUE_STAGING, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_APPLIED }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js new file mode 100644 index 0000000000..fbf91c0e54 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates which fails, +// because it is impossible to connect to the update server. +add_task(async function aboutPrefs_foregroundCheck_network_failure() { + let params = { + baseURL: "https://localhost:7777", + }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingFailed", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js new file mode 100644 index 0000000000..7b5b6899b3 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates which fails because +// the browser is in offline mode and `localhost` cannot be resolved. +add_task(async function aboutPrefs_foregroundCheck_network_offline() { + info("[OFFLINE] Setting Services.io.offline (do not forget to reset it!)"); + // avoid that real network connectivity changes influence the test execution + Services.io.manageOfflineStatus = false; + Services.io.offline = true; + registerCleanupFunction(() => { + info("[ONLINE] Resetting Services.io.offline"); + Services.io.offline = false; + Services.io.manageOfflineStatus = true; + }); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["network.disable-localhost-when-offline", true], + ["network.dns.offline-localhost", false], + ], + }); + + await runAboutPrefsUpdateTest({}, [ + { + panelId: "checkingFailed", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js new file mode 100644 index 0000000000..a36d3d9807 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a complete bad size patch. +add_task(async function aboutPrefs_foregroundCheck_completeBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult }; + } else { + downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult }; + } + + let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js new file mode 100644 index 0000000000..060acd405a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a partial bad size patch. +add_task(async function aboutPrefs_foregroundCheck_partialBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult }; + } + + let params = { queryString: "&partialPatchOnly=1&invalidPartialSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js new file mode 100644 index 0000000000..c6e5b5b20b --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a partial bad size patch and a complete patch. +add_task(async function aboutPrefs_foregroundCheck_partialBadSize_complete() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult }; + downloadInfo[2] = { patchType: "complete", bitsResult: "0" }; + } else { + downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult }; + downloadInfo[1] = { patchType: "complete", internalResult: "0" }; + } + + let params = { queryString: "&invalidPartialSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js new file mode 100644 index 0000000000..62abdc3af5 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for about:preferences foreground check for updates +// with a partial bad size patch and a complete bad size patch. +add_task( + async function aboutPrefs_foregroundCheck_partialBadSize_completeBadSize() { + let downloadInfo = []; + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) { + downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult }; + downloadInfo[1] = { + patchType: "partial", + internalResult: gBadSizeResult, + }; + downloadInfo[2] = { patchType: "complete", bitsResult: gBadSizeResult }; + downloadInfo[3] = { + patchType: "complete", + internalResult: gBadSizeResult, + }; + } else { + downloadInfo[0] = { + patchType: "partial", + internalResult: gBadSizeResult, + }; + downloadInfo[1] = { + patchType: "complete", + internalResult: gBadSizeResult, + }; + } + + let params = { queryString: "&invalidPartialSize=1&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "downloadFailed", + checkActiveUpdate: null, + continueFile: null, + }, + ]); + } +); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js new file mode 100644 index 0000000000..f22b60ae5a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +XPCOMUtils.defineLazyModuleGetters(this, { + AppUpdater: "resource://gre/modules/AppUpdater.jsm", + sinon: "resource://testing-common/Sinon.jsm", +}); + +add_setup(function setup_internalErrorTest() { + const sandbox = sinon.createSandbox(); + sandbox.stub(AppUpdater.prototype, "aus").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "checker").get(() => { + throw new Error("intentional test error"); + }); + sandbox.stub(AppUpdater.prototype, "um").get(() => { + throw new Error("intentional test error"); + }); + registerCleanupFunction(() => { + sandbox.restore(); + }); +}); + +// Test for about:preferences internal error handling. +add_task(async function aboutPrefs_internalError() { + let params = {}; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "internalError", + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js new file mode 100644 index 0000000000..c249d2d73a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js @@ -0,0 +1,151 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + */ + +// Changes, then verifies the value of app.update.auto via the about:preferences +// UI. Requires a tab with about:preferences open to be passed in. +async function changeAndVerifyPref(tab, newConfigValue) { + await SpecialPowers.spawn( + tab.linkedBrowser, + [{ newConfigValue }], + async function({ newConfigValue }) { + let radioId = newConfigValue ? "autoDesktop" : "manualDesktop"; + let radioElement = content.document.getElementById(radioId); + let updateRadioGroup = radioElement.radioGroup; + let promise = ContentTaskUtils.waitForEvent( + updateRadioGroup, + "ProcessedUpdatePrefChange" + ); + radioElement.click(); + await promise; + + is( + updateRadioGroup.value, + `${newConfigValue}`, + "Update preference should match expected" + ); + is( + updateRadioGroup.disabled, + false, + "Update preferences should no longer be disabled" + ); + } + ); + + let configValueRead = await UpdateUtils.getAppUpdateAutoEnabled(); + is( + configValueRead, + newConfigValue, + "Value returned should have matched the expected value" + ); +} + +async function changeAndVerifyUpdateWrites({ + tab, + newConfigValue, + discardUpdate, + expectPrompt, + expectRemainingUpdate, +}) { + // A value of 1 will keep the update and a value of 0 will discard the update + // when the prompt service is called when the value of app.update.auto is + // changed to false. + let confirmExReply = discardUpdate ? 0 : 1; + let didPrompt = false; + let promptService = { + QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]), + confirmEx(...args) { + promptService._confirmExArgs = args; + didPrompt = true; + return confirmExReply; + }, + }; + Services.prompt = promptService; + await changeAndVerifyPref(tab, newConfigValue); + is( + didPrompt, + expectPrompt, + `We should ${expectPrompt ? "" : "not "}be prompted` + ); + is( + !!gUpdateManager.readyUpdate, + expectRemainingUpdate, + `There should ${expectRemainingUpdate ? "" : "not "}be a ready update` + ); +} + +add_task(async function testUpdateAutoPrefUI() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:preferences" + ); + + // Hack: make the test run faster: + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.gMainPane._minUpdatePrefDisableTime = 10; + }); + + info("Enable automatic updates and check that works."); + await changeAndVerifyPref(tab, true); + ok( + !gUpdateManager.downloadingUpdate, + "There should not be a downloading update" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + + info("Disable automatic updates and check that works."); + await changeAndVerifyPref(tab, false); + ok( + !gUpdateManager.downloadingUpdate, + "There should not be a downloading update" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + + let patchProps = { state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + let updateProps = { checkInterval: "1" }; + let updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); + writeStatusFile(STATE_PENDING); + reloadUpdateManagerData(); + ok(!!gUpdateManager.readyUpdate, "There should be a ready update"); + + let { prompt } = Services; + registerCleanupFunction(() => { + Services.prompt = prompt; + }); + + // Setting the value to false will call the prompt service and when we + // don't discard the update there should still be an active update afterwards. + await changeAndVerifyUpdateWrites({ + tab, + newConfigValue: false, + discardUpdate: false, + expectPrompt: true, + expectRemainingUpdate: true, + }); + + // Setting the value to true should not call the prompt service so there + // should still be an active update, even if we indicate we can discard + // the update in a hypothetical prompt. + await changeAndVerifyUpdateWrites({ + tab, + newConfigValue: true, + discardUpdate: true, + expectPrompt: false, + expectRemainingUpdate: true, + }); + + // Setting the value to false will call the prompt service, and we do + // discard the update, so there should not be an active update. + await changeAndVerifyUpdateWrites({ + tab, + newConfigValue: false, + discardUpdate: true, + expectPrompt: true, + expectRemainingUpdate: false, + }); + + await BrowserTestUtils.removeTab(tab); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js new file mode 100644 index 0000000000..90f4c385cc --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_check_cantApply() { + lockWriteTestFile(); + + let params = { checkAttempts: 1, queryString: "&promptWaitTime=0" }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { whatsNew: gDetailsURL, manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js new file mode 100644 index 0000000000..d83bc70b6f --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_check_malformedXML() { + const maxBackgroundErrors = 10; + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]], + }); + + let params = { + checkAttempts: maxBackgroundErrors, + queryString: "&xmlMalformed=1", + }; + await runDoorhangerUpdateTest(params, [ + { + // If the update check fails 10 consecutive attempts then the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { whatsNew: gDetailsURL, manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js new file mode 100644 index 0000000000..02aaab1064 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js @@ -0,0 +1,94 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for doorhanger background check for updates +// with an unsupported update. +add_task(async function doorhanger_bc_check_unsupported() { + let params = { checkAttempts: 1, queryString: "&unsupported=1" }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-unsupported", + button: "button", + pageURLs: { manual: gDetailsURL }, + }, + async function doorhanger_unsupported_persist() { + await TestUtils.waitForCondition( + () => PanelUI.menuButton.hasAttribute("badge-status"), + "Waiting for update badge", + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test. + logTestInfo(e); + }); + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "The window has a badge." + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-unsupported", + "The correct badge is showing for the background window" + ); + + // Test persistence of the badge when the client has restarted by + // resetting the UpdateListener. + UpdateListener.reset(); + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + !PanelUI.menuButton.hasAttribute("badge-status"), + "The window does not have a badge." + ); + UpdateListener.maybeShowUnsupportedNotification(); + is( + PanelUI.notificationPanel.state, + "closed", + "The window's doorhanger is closed." + ); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "The window has a badge." + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-unsupported", + "The correct badge is showing for the background window." + ); + }, + ]); + + params = { + checkAttempts: 1, + queryString: "&invalidCompleteSize=1&promptWaitTime=0", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + async function doorhanger_unsupported_removed() { + // Test that finding an update removes the app.update.unsupported.url + // preference. + let unsupportedURL = Services.prefs.getCharPref( + PREF_APP_UPDATE_UNSUPPORTED_URL, + null + ); + ok( + !unsupportedURL, + "The " + PREF_APP_UPDATE_UNSUPPORTED_URL + " preference was removed." + ); + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js new file mode 100644 index 0000000000..3678a440d2 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloadAutoFailures() { + const maxBackgroundErrors = 5; + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]], + }); + + let params = { checkAttempts: 1, queryString: "&badURL=1" }; + await runDoorhangerUpdateTest(params, [ + { + // If the update download fails maxBackgroundErrors download attempts then + // show the update available prompt. + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js new file mode 100644 index 0000000000..0683e0b532 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloadAutoFailures_bgWin() { + function getBackgroundWindowHandler(destroyWindow) { + return async function() { + await TestUtils.waitForCondition( + () => PanelUI.menuButton.hasAttribute("badge-status"), + "Background window has a badge.", + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test. + logTestInfo(e); + }); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "PanelUI.menuButton should have a 'badge-status' attribute" + ); + is( + PanelUI.notificationPanel.state, + "closed", + "The doorhanger is not showing for the background window" + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-available", + "The badge is showing for the background window" + ); + + let buttonEl = getNotificationButton( + extraWindow, + "update-available", + "button" + ); + buttonEl.click(); + + if (destroyWindow) { + // The next popup may be shown during closeWindow or promiseFocus + // calls. + let waitForPopupShown = new Promise(resolve => { + window.addEventListener( + "popupshown", + () => { + executeSoon(resolve); + }, + { once: true } + ); + }); + await BrowserTestUtils.closeWindow(extraWindow); + await SimpleTest.promiseFocus(window); + await waitForPopupShown; + } + }; + } + + const maxBackgroundErrors = 5; + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]], + }); + + let extraWindow = await BrowserTestUtils.openNewBrowserWindow(); + await SimpleTest.promiseFocus(extraWindow); + + let params = { checkAttempts: 1, queryString: "&badURL=1", popupShown: true }; + await runDoorhangerUpdateTest(params, [ + getBackgroundWindowHandler(false), + getBackgroundWindowHandler(true), + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js new file mode 100644 index 0000000000..2237161863 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloadOptIn() { + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let params = { + checkAttempts: 1, + queryString: "&invalidCompleteSize=1&promptWaitTime=0", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js new file mode 100644 index 0000000000..0975166bd2 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloadOptIn_bgWin() { + function getBackgroundWindowHandler() { + return async function() { + await TestUtils.waitForCondition( + () => PanelUI.menuButton.hasAttribute("badge-status"), + "Background window has a badge.", + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test. + logTestInfo(e); + }); + ok( + PanelUI.menuButton.hasAttribute("badge-status"), + "PanelUI.menuButton should have a 'badge-status' attribute" + ); + is( + PanelUI.notificationPanel.state, + "closed", + "The doorhanger is not showing for the background window" + ); + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-available", + "The badge is showing for the background window" + ); + let popupShownPromise = BrowserTestUtils.waitForEvent( + PanelUI.notificationPanel, + "popupshown" + ); + await BrowserTestUtils.closeWindow(extraWindow); + await SimpleTest.promiseFocus(window); + await popupShownPromise; + + let buttonEl = getNotificationButton( + window, + "update-available", + "button" + ); + buttonEl.click(); + }; + } + + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let extraWindow = await BrowserTestUtils.openNewBrowserWindow(); + await SimpleTest.promiseFocus(extraWindow); + + let params = { checkAttempts: 1, queryString: "&promptWaitTime=0" }; + await runDoorhangerUpdateTest(params, [ + getBackgroundWindowHandler(), + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js new file mode 100644 index 0000000000..7ba2d67964 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloadOptIn_staging() { + await SpecialPowers.pushPrefEnv({ + set: [ + // Tests the app.update.promptWaitTime pref + [PREF_APP_UPDATE_PROMPTWAITTIME, 0], + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + await UpdateUtils.setAppUpdateAutoEnabled(false); + + let params = { checkAttempts: 1, queryString: "&invalidCompleteSize=1" }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_APPLIED }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js new file mode 100644 index 0000000000..e29dad26fa --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloaded() { + let params = { + checkAttempts: 1, + queryString: "&invalidCompleteSize=1&promptWaitTime=0", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js new file mode 100644 index 0000000000..5f89c95322 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloaded_disableBITS() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_BITS_ENABLED, true]], + }); + + let params = { + checkAttempts: 1, + queryString: "&promptWaitTime=0&disableBITS=true", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); + + let patch = getPatchOfType( + "partial", + gUpdateManager.readyUpdate + ).QueryInterface(Ci.nsIWritablePropertyBag); + ok( + !patch.getProperty("bitsId"), + "The selected patch should not have a bitsId property" + ); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js new file mode 100644 index 0000000000..50416608f2 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_downloaded_staged() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let params = { + checkAttempts: 1, + queryString: "&invalidCompleteSize=1&promptWaitTime=0", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_APPLIED }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js new file mode 100644 index 0000000000..07e7bf51fa --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * This test downloads 2 updates sequentially, and ensures that the doorhanger + * and badge do what they are supposed to do: + * First thing after the first download, the doorhanger should be displayed. + * Then download the next update. + * While that update stages, the badge should be hidden to prevent restarting + * to update while the update is staging. + * Once the staging completes, the badge should return. The doorhanger should + * not be shown at this time, because it has already been shown this + * session. + */ + +const FIRST_UPDATE_VERSION = "999998.0"; +const SECOND_UPDATE_VERSION = "999999.0"; + +function prepareToDownloadVersion(version) { + setUpdateURL( + URL_HTTP_UPDATE_SJS + + `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}` + ); +} + +add_task(async function doorhanger_bc_multiUpdate() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let params = { + checkAttempts: 1, + queryString: "&promptWaitTime=0", + version: FIRST_UPDATE_VERSION, + slowStaging: true, + }; + await runDoorhangerUpdateTest(params, [ + () => { + return continueFileHandler(CONTINUE_STAGING); + }, + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_APPLIED }, + }, + async () => { + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-restart", + "Should have restart badge" + ); + + prepareToDownloadVersion(SECOND_UPDATE_VERSION); + let updateSwapped = waitForEvent("update-swap"); + gAUS.checkForBackgroundUpdates(); + await updateSwapped; + // The badge should be hidden while we swap from one update to the other + // to prevent restarting to update while staging is occurring. But since + // it will be waiting on the same event we are waiting on, wait an + // additional tick to let the other update-swap listeners run. + await TestUtils.waitForTick(); + + is( + PanelUI.menuButton.getAttribute("badge-status"), + "", + "Should not have restart badge during staging" + ); + + await continueFileHandler(CONTINUE_STAGING); + + try { + await TestUtils.waitForCondition( + () => + PanelUI.menuButton.getAttribute("badge-status") == "update-restart", + "Waiting for update restart badge to return after staging" + ); + } catch (ex) {} + + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-restart", + "Restart badge should be restored after staging completes" + ); + is( + PanelUI.notificationPanel.state, + "closed", + "Should not open a second doorhanger" + ); + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js new file mode 100644 index 0000000000..00c61bcbb4 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * This test downloads 2 updates sequentially, and ensures that the doorhanger + * and badge do what they are supposed to do. However, the first update has a + * long promptWaitTime, and the second has a short one and the badge wait time + * is set to 0. This should result in this behavior: + * First thing after the first download, the badge should be displayed, but + * not the doorhanger. + * Then download the next update. + * While that update stages, the badge should be hidden to prevent restarting + * to update while the update is staging. + * Once the staging completes, the doorhanger should be shown. Despite the + * long promptWaitTime of the initial update, this patch's short wait time + * means that the doorhanger should be shown soon rather than in a long + * time. + */ + +const FIRST_UPDATE_VERSION = "999998.0"; +const SECOND_UPDATE_VERSION = "999999.0"; +const LONG_PROMPT_WAIT_TIME_SEC = 10 * 60 * 60; // 10 hours + +function prepareToDownloadVersion(version, promptWaitTime) { + setUpdateURL( + URL_HTTP_UPDATE_SJS + + `?detailsURL=${gDetailsURL}&promptWaitTime=${promptWaitTime}&appVersion=${version}` + ); +} + +add_task(async function doorhanger_bc_multiUpdate() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + [PREF_APP_UPDATE_BADGEWAITTIME, 0], + ], + }); + + let params = { + checkAttempts: 1, + queryString: `&promptWaitTime=${LONG_PROMPT_WAIT_TIME_SEC}`, + version: FIRST_UPDATE_VERSION, + slowStaging: true, + }; + await runDoorhangerUpdateTest(params, [ + async () => { + await continueFileHandler(CONTINUE_STAGING); + + try { + await TestUtils.waitForCondition( + () => + PanelUI.menuButton.getAttribute("badge-status") == "update-restart", + "Waiting for update restart badge to return after staging" + ); + } catch (ex) {} + + is( + PanelUI.menuButton.getAttribute("badge-status"), + "update-restart", + "Should have restart badge" + ); + + prepareToDownloadVersion(SECOND_UPDATE_VERSION, 0); + let updateSwapped = waitForEvent("update-swap"); + gAUS.checkForBackgroundUpdates(); + await updateSwapped; + // The badge should be hidden while we swap from one update to the other + // to prevent restarting to update while staging is occurring. But since + // it will be waiting on the same event we are waiting on, wait an + // additional tick to let the other update-swap listeners run. + await TestUtils.waitForTick(); + + is( + PanelUI.menuButton.getAttribute("badge-status"), + "", + "Should not have restart badge during staging" + ); + + await continueFileHandler(CONTINUE_STAGING); + }, + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_APPLIED }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js new file mode 100644 index 0000000000..3c29d6b4b0 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_patch_completeBadSize() { + let params = { + checkAttempts: 1, + queryString: "&completePatchOnly=1&invalidCompleteSize=1", + }; + await runDoorhangerUpdateTest(params, [ + { + // If the update download fails maxBackgroundErrors download attempts then + // show the update available prompt. + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js new file mode 100644 index 0000000000..68854b3f26 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_patch_partialBadSize() { + let params = { + checkAttempts: 1, + queryString: "&partialPatchOnly=1&invalidPartialSize=1", + }; + await runDoorhangerUpdateTest(params, [ + { + // If the update download fails maxBackgroundErrors download attempts then + // show the update available prompt. + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js new file mode 100644 index 0000000000..f3c9b1f51f --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_patch_partialBadSize_complete() { + let params = { + checkAttempts: 1, + queryString: "&invalidPartialSize=1&promptWaitTime=0", + }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js new file mode 100644 index 0000000000..a18e2f6444 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_bc_patch_partialBadSize_completeBadSize() { + let params = { + checkAttempts: 1, + queryString: "&invalidPartialSize=1&invalidCompleteSize=1", + }; + await runDoorhangerUpdateTest(params, [ + { + // If the update download fails maxBackgroundErrors download attempts then + // show the update available prompt. + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + notificationId: "update-available", + button: "button", + checkActiveUpdate: null, + }, + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js new file mode 100644 index 0000000000..5c7c937d81 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_sp_patch_completeApplyFailure() { + let patchProps = { state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + let updateProps = { checkInterval: "1" }; + let updates = getLocalUpdateString(updateProps, patches); + + let params = { updates }; + await runDoorhangerUpdateTest(params, [ + { + // If the update process is unable to install the update show the manual + // update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js new file mode 100644 index 0000000000..45434c8361 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_sp_patch_partialApplyFailure() { + let patchProps = { type: "partial", state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + let updateProps = { isCompleteUpdate: "false", checkInterval: "1" }; + let updates = getLocalUpdateString(updateProps, patches); + + let params = { updates }; + await runDoorhangerUpdateTest(params, [ + { + // If there is only an invalid patch show the manual update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js new file mode 100644 index 0000000000..bf533dab04 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function doorhanger_sp_patch_partialApplyFailure_complete() { + let patchProps = { type: "partial", state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + patchProps = { selected: "false" }; + patches += getLocalPatchString(patchProps); + let updateProps = { isCompleteUpdate: "false", promptWaitTime: "0" }; + let updates = getLocalUpdateString(updateProps, patches); + + let params = { updates }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_PENDING }, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js new file mode 100644 index 0000000000..df17bc1220 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task( + async function doorhanger_sp_patch_partialApplyFailure_completeBadSize() { + // Because of the way the test is simulating failure it has to pretend it has + // already retried. + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 0]], + }); + + let patchProps = { type: "partial", state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + patchProps = { size: "1234", selected: "false" }; + patches += getLocalPatchString(patchProps); + let updateProps = { isCompleteUpdate: "false" }; + let updates = getLocalUpdateString(updateProps, patches); + + let params = { updates }; + await runDoorhangerUpdateTest(params, [ + { + // If there is only an invalid patch show the manual update doorhanger. + notificationId: "update-manual", + button: "button", + checkActiveUpdate: null, + pageURLs: { manual: URL_MANUAL_UPDATE }, + }, + ]); + } +); diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js new file mode 100644 index 0000000000..a99c04b0be --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task( + async function doorhanger_sp_patch_partialApplyFailure_complete_staging() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let patchProps = { type: "partial", state: STATE_PENDING }; + let patches = getLocalPatchString(patchProps); + patchProps = { selected: "false" }; + patches += getLocalPatchString(patchProps); + let updateProps = { isCompleteUpdate: "false", promptWaitTime: "0" }; + let updates = getLocalUpdateString(updateProps, patches); + + let params = { updates }; + await runDoorhangerUpdateTest(params, [ + { + notificationId: "update-restart", + button: "secondaryButton", + checkActiveUpdate: { state: STATE_APPLIED }, + }, + ]); + } +); diff --git a/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js new file mode 100644 index 0000000000..6aa32a7fc9 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js @@ -0,0 +1,139 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function elevation_dialog() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_DISABLEDFORTESTING, false]], + }); + + // Create a mock of nsIAppStartup's quit method so clicking the restart button + // won't restart the application. + let { startup } = Services; + let appStartup = { + QueryInterface: ChromeUtils.generateQI(["nsIAppStartup"]), + quit(mode) { + if (elevationDialog) { + elevationDialog.close(); + elevationDialog = null; + } + }, + }; + Services.startup = appStartup; + registerCleanupFunction(() => { + Services.startup = startup; + }); + + registerCleanupFunction(async () => { + let win = Services.wm.getMostRecentWindow("Update:Elevation"); + if (win) { + win.close(); + await TestUtils.waitForCondition( + () => !Services.wm.getMostRecentWindow("Update:Elevation"), + "The Update Elevation dialog should have closed" + ); + } + }); + + // Test clicking the "Restart Later" button + let elevationDialog = await waitForElevationDialog(); + await TestUtils.waitForTick(); + elevationDialog.document.getElementById("elevateExtra2").click(); + await TestUtils.waitForCondition( + () => !Services.wm.getMostRecentWindow("Update:Elevation"), + "The Update Elevation dialog should have closed" + ); + ok(!!gUpdateManager.readyUpdate, "There should be a ready update"); + is( + gUpdateManager.readyUpdate.state, + STATE_PENDING_ELEVATE, + "The ready update state should equal " + STATE_PENDING_ELEVATE + ); + is( + readStatusFile(), + STATE_PENDING_ELEVATE, + "The status file state should equal " + STATE_PENDING_ELEVATE + ); + + // Test clicking the "No Thanks" button + elevationDialog = await waitForElevationDialog(); + await TestUtils.waitForTick(); + elevationDialog.document.getElementById("elevateExtra1").click(); + await TestUtils.waitForCondition( + () => !Services.wm.getMostRecentWindow("Update:Elevation"), + "The Update Elevation dialog should have closed" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + is( + readStatusFile(), + STATE_NONE, + "The status file state should equal " + STATE_NONE + ); + + // Test clicking the "Restart <brandShortName>" button + elevationDialog = await waitForElevationDialog(); + await TestUtils.waitForTick(); + elevationDialog.document.getElementById("elevateAccept").click(); + await TestUtils.waitForCondition( + () => !Services.wm.getMostRecentWindow("Update:Elevation"), + "The Update Elevation dialog should have closed" + ); + ok(!!gUpdateManager.readyUpdate, "There should be a ready update"); + is( + gUpdateManager.readyUpdate.state, + STATE_PENDING_ELEVATE, + "The active update state should equal " + STATE_PENDING_ELEVATE + ); + is( + readStatusFile(), + STATE_PENDING, + "The status file state should equal " + STATE_PENDING + ); +}); + +/** + * Waits for the Update Elevation Dialog to load. + * + * @return A promise that returns the domWindow for the Update Elevation Dialog + * and resolves when the Update Elevation Dialog loads. + */ +function waitForElevationDialog() { + return new Promise(resolve => { + var listener = { + onOpenWindow: aXULWindow => { + debugDump("Update Elevation dialog shown..."); + Services.wm.removeListener(listener); + + async function elevationDialogOnLoad() { + domwindow.removeEventListener("load", elevationDialogOnLoad, true); + let chromeURI = + "chrome://mozapps/content/update/updateElevation.xhtml"; + is( + domwindow.document.location.href, + chromeURI, + "Update Elevation appeared" + ); + resolve(domwindow); + } + + var domwindow = aXULWindow.docShell.domWindow; + domwindow.addEventListener("load", elevationDialogOnLoad, true); + }, + onCloseWindow: aXULWindow => {}, + }; + + Services.wm.addListener(listener); + // Add the active-update.xml and update.status files used for these tests, + // reload the update manager, and then simulate startup so the Update + // Elevation Dialog is opened. + let patchProps = { state: STATE_PENDING_ELEVATE }; + let patches = getLocalPatchString(patchProps); + let updateProps = { checkInterval: "1" }; + let updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); + writeStatusFile(STATE_PENDING_ELEVATE); + reloadUpdateManagerData(); + testPostUpdateProcessing(); + }); +} diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js new file mode 100644 index 0000000000..eb84e113d1 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { TelemetryArchiveTesting } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryArchiveTesting.sys.mjs" +); + +/** + * Test that UpdatePing telemetry with a payload reason of ready is sent for a + * staged update. + * + * Please note that this is really a Telemetry test, not an + * "update UI" test like the rest of the tests in this directory. + * This test does not live in toolkit/components/telemetry/tests to prevent + * duplicating the code for all the test dependencies. Unfortunately, due + * to a limitation in the build system, we were not able to simply reference + * the dependencies as "support-files" in the test manifest. + */ +add_task(async function telemetry_updatePing_ready() { + let archiveChecker = new TelemetryArchiveTesting.Checker(); + await archiveChecker.promiseInit(); + + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + // We cannot control when the ping will be generated/archived after we trigger + // an update, so let's make sure to have one before moving on with validation. + let updatePing; + await TestUtils.waitForCondition( + async function() { + // Check that the ping made it into the Telemetry archive. + // The test data is defined in ../data/sharedUpdateXML.js + updatePing = await archiveChecker.promiseFindPing("update", [ + [["payload", "reason"], "ready"], + [["payload", "targetBuildId"], "20080811053724"], + ]); + return !!updatePing; + }, + "Make sure the ping is generated before trying to validate it.", + 500, + 100 + ); + + ok(updatePing, "The 'update' ping must be correctly sent."); + + // We don't know the exact value for the other fields, so just check + // that they're available. + for (let f of ["targetVersion", "targetChannel", "targetDisplayVersion"]) { + ok( + f in updatePing.payload, + `${f} must be available in the update ping payload.` + ); + ok( + typeof updatePing.payload[f] == "string", + `${f} must have the correct format.` + ); + } + + // Also make sure that the ping contains both a client id and an + // environment section. + ok("clientId" in updatePing, "The update ping must report a client id."); + ok( + "environment" in updatePing, + "The update ping must report the environment." + ); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js new file mode 100644 index 0000000000..84b39851d4 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { TelemetryArchiveTesting } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryArchiveTesting.sys.mjs" +); + +/** + * Test that UpdatePing telemetry with a payload reason of ready is sent for a + * staged update. + * + * Please note that this is really a Telemetry test, not an + * "update UI" test like the rest of the tests in this directory. + * This test does not live in toolkit/components/telemetry/tests to prevent + * duplicating the code for all the test dependencies. Unfortunately, due + * to a limitation in the build system, we were not able to simply reference + * the dependencies as "support-files" in the test manifest. + */ +add_task(async function telemetry_updatePing_ready() { + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]], + }); + + let archiveChecker = new TelemetryArchiveTesting.Checker(); + await archiveChecker.promiseInit(); + + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-staged"); + + // We cannot control when the ping will be generated/archived after we trigger + // an update, so let's make sure to have one before moving on with validation. + let updatePing; + await TestUtils.waitForCondition( + async function() { + // Check that the ping made it into the Telemetry archive. + // The test data is defined in ../data/sharedUpdateXML.js + updatePing = await archiveChecker.promiseFindPing("update", [ + [["payload", "reason"], "ready"], + [["payload", "targetBuildId"], "20080811053724"], + ]); + return !!updatePing; + }, + "Make sure the ping is generated before trying to validate it.", + 500, + 100 + ); + + ok(updatePing, "The 'update' ping must be correctly sent."); + + // We don't know the exact value for the other fields, so just check + // that they're available. + for (let f of ["targetVersion", "targetChannel", "targetDisplayVersion"]) { + ok( + f in updatePing.payload, + `${f} must be available in the update ping payload.` + ); + ok( + typeof updatePing.payload[f] == "string", + `${f} must have the correct format.` + ); + } + + // Also make sure that the ping contains both a client id and an + // environment section. + ok("clientId" in updatePing, "The update ping must report a client id."); + ok( + "environment" in updatePing, + "The update ping must report the environment." + ); +}); diff --git a/toolkit/mozapps/update/tests/browser/downloadPage.html b/toolkit/mozapps/update/tests/browser/downloadPage.html new file mode 100644 index 0000000000..4810e2e0d6 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/downloadPage.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <title>Download page</title> + <meta charset="utf-8"> +</head> +<body> +<!-- just use simple.mar since we have it available and it will result in a download dialog --> +<a id="download-link" href="http://example.com/browser/browser/base/content/test/appUpdate/simple.mar" data-link-type="download"> + Download +</a> +</body> +</html> diff --git a/toolkit/mozapps/update/tests/browser/head.js b/toolkit/mozapps/update/tests/browser/head.js new file mode 100644 index 0000000000..46409e4397 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/head.js @@ -0,0 +1,1289 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs", +}); +ChromeUtils.defineModuleGetter( + this, + "DownloadUtils", + "resource://gre/modules/DownloadUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "UpdateListener", + "resource://gre/modules/UpdateListener.jsm" +); +const { XPIInstall } = ChromeUtils.import( + "resource://gre/modules/addons/XPIInstall.jsm" +); + +const BIN_SUFFIX = AppConstants.platform == "win" ? ".exe" : ""; +const FILE_UPDATER_BIN = + "updater" + (AppConstants.platform == "macosx" ? ".app" : BIN_SUFFIX); +const FILE_UPDATER_BIN_BAK = FILE_UPDATER_BIN + ".bak"; + +const LOG_FUNCTION = info; + +const MAX_UPDATE_COPY_ATTEMPTS = 10; + +const DATA_URI_SPEC = + "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/"; +/* import-globals-from testConstants.js */ +Services.scriptloader.loadSubScript(DATA_URI_SPEC + "testConstants.js", this); + +var gURLData = URL_HOST + "/" + REL_PATH_DATA; +const URL_MANUAL_UPDATE = gURLData + "downloadPage.html"; + +const gBadSizeResult = Cr.NS_ERROR_UNEXPECTED.toString(); + +/* import-globals-from ../data/shared.js */ +Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this); + +let gOriginalUpdateAutoValue = null; + +// Some elements append a trailing /. After the chrome tests are removed this +// code can be changed so URL_HOST already has a trailing /. +const gDetailsURL = URL_HOST + "/"; + +// Set to true to log additional information for debugging. To log additional +// information for individual tests set gDebugTest to false here and to true +// globally in the test. +gDebugTest = false; + +// This is to accommodate the TV task which runs the tests with --verify. +requestLongerTimeout(10); + +/** + * Common tasks to perform for all tests before each one has started. + */ +add_setup(async function setupTestCommon() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_BADGEWAITTIME, 1800], + [PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0], + [PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 2], + [PREF_APP_UPDATE_LOG, gDebugTest], + [PREF_APP_UPDATE_PROMPTWAITTIME, 3600], + [PREF_APP_UPDATE_SERVICE_ENABLED, false], + ], + }); + + // We need to keep the update sync manager from thinking two instances are + // running because of the mochitest parent instance, which means we need to + // override the directory service with a fake executable path and then reset + // the lock. But leaving the directory service overridden causes problems for + // these tests, so we need to restore the real service immediately after. + // To form the path, we'll use the real executable path with a token appended + // (the path needs to be absolute, but not to point to a real file). + // This block is loosely copied from adjustGeneralPaths() in another update + // test file, xpcshellUtilsAUS.js, but this is a much more limited version; + // it's been copied here both because the full function is overkill and also + // because making it general enough to run in both xpcshell and mochitest + // would have been unreasonably difficult. + let exePath = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile); + let dirProvider = { + getFile: function AGP_DP_getFile(aProp, aPersistent) { + // Set the value of persistent to false so when this directory provider is + // unregistered it will revert back to the original provider. + aPersistent.value = false; + switch (aProp) { + case XRE_EXECUTABLE_FILE: + exePath.append("browser-test"); + return exePath; + } + return null; + }, + QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]), + }; + let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService); + ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); + ds.registerProvider(dirProvider); + + let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( + Ci.nsIUpdateSyncManager + ); + syncManager.resetLock(); + + ds.unregisterProvider(dirProvider); + + setUpdateTimerPrefs(); + reloadUpdateManagerData(true); + removeUpdateFiles(true); + UpdateListener.reset(); + AppMenuNotifications.removeNotification(/.*/); + // Most app update mochitest-browser-chrome tests expect auto update to be + // enabled. Those that don't will explicitly change this. + await setAppUpdateAutoEnabledHelper(true); +}); + +/** + * Common tasks to perform for all tests after each one has finished. + */ +registerCleanupFunction(async () => { + AppMenuNotifications.removeNotification(/.*/); + Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", ""); + Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", ""); + Services.env.set("MOZ_TEST_STAGING_ERROR", ""); + UpdateListener.reset(); + AppMenuNotifications.removeNotification(/.*/); + reloadUpdateManagerData(true); + // Pass false when the log files are needed for troubleshooting the tests. + removeUpdateFiles(true); + // Always try to restore the original updater files. If none of the updater + // backup files are present then this is just a no-op. + await finishTestRestoreUpdaterBackup(); + // Reset the update lock once again so that we know the lock we're + // interested in here will be closed properly (normally that happens during + // XPCOM shutdown, but that isn't consistent during tests). + let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( + Ci.nsIUpdateSyncManager + ); + syncManager.resetLock(); +}); + +/** + * Overrides the add-ons manager language pack staging with a mocked version. + * The returned promise resolves when language pack staging begins returning an + * object with the new appVersion and platformVersion and functions to resolve + * or reject the install. + */ +function mockLangpackInstall() { + let original = XPIInstall.stageLangpacksForAppUpdate; + registerCleanupFunction(() => { + XPIInstall.stageLangpacksForAppUpdate = original; + }); + + let stagingCall = PromiseUtils.defer(); + XPIInstall.stageLangpacksForAppUpdate = (appVersion, platformVersion) => { + let result = PromiseUtils.defer(); + stagingCall.resolve({ + appVersion, + platformVersion, + resolve: result.resolve, + reject: result.reject, + }); + + return result.promise; + }; + + return stagingCall.promise; +} + +/** + * Creates and locks the app update write test file so it is possible to test + * when the user doesn't have write access to update. Since this is only + * possible on Windows the function throws when it is called on other platforms. + * This uses registerCleanupFunction to remove the lock and the file when the + * test completes. + * + * @throws If the function is called on a platform other than Windows. + */ +function lockWriteTestFile() { + if (AppConstants.platform != "win") { + throw new Error("Windows only test function called"); + } + let file = getUpdateDirFile(FILE_UPDATE_TEST).QueryInterface( + Ci.nsILocalFileWin + ); + // Remove the file if it exists just in case. + if (file.exists()) { + file.readOnly = false; + file.remove(false); + } + file.create(file.NORMAL_FILE_TYPE, 0o444); + file.readOnly = true; + registerCleanupFunction(() => { + file.readOnly = false; + file.remove(false); + }); +} + +/** + * Closes the update mutex handle in nsUpdateService.js if it exists and then + * creates a new update mutex handle so the update code thinks there is another + * instance of the application handling updates. + * + * @throws If the function is called on a platform other than Windows. + */ +function setOtherInstanceHandlingUpdates() { + if (AppConstants.platform != "win") { + throw new Error("Windows only test function called"); + } + gAUS.observe(null, "test-close-handle-update-mutex", ""); + let handle = createMutex(getPerInstallationMutexName()); + registerCleanupFunction(() => { + closeHandle(handle); + }); +} + +/** + * Gets the update version info for the update url parameters to send to + * app_update.sjs. + * + * @param aAppVersion (optional) + * The application version for the update snippet. If not specified the + * current application version will be used. + * @return The url parameters for the application and platform version to send + * to app_update.sjs. + */ +function getVersionParams(aAppVersion) { + let appInfo = Services.appinfo; + return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version); +} + +/** + * Prevent nsIUpdateTimerManager from notifying nsIApplicationUpdateService + * to check for updates by setting the app update last update time to the + * current time minus one minute in seconds and the interval time to 12 hours + * in seconds. + */ +function setUpdateTimerPrefs() { + let now = Math.round(Date.now() / 1000) - 60; + Services.prefs.setIntPref(PREF_APP_UPDATE_LASTUPDATETIME, now); + Services.prefs.setIntPref(PREF_APP_UPDATE_INTERVAL, 43200); +} + +/* + * Sets the value of the App Auto Update setting and sets it back to the + * original value at the start of the test when the test finishes. + * + * @param enabled + * The value to set App Auto Update to. + */ +async function setAppUpdateAutoEnabledHelper(enabled) { + if (gOriginalUpdateAutoValue == null) { + gOriginalUpdateAutoValue = await UpdateUtils.getAppUpdateAutoEnabled(); + registerCleanupFunction(async () => { + await UpdateUtils.setAppUpdateAutoEnabled(gOriginalUpdateAutoValue); + }); + } + await UpdateUtils.setAppUpdateAutoEnabled(enabled); +} + +/** + * Gets the specified button for the notification. + * + * @param win + * The window to get the notification button for. + * @param notificationId + * The ID of the notification to get the button for. + * @param button + * The anonid of the button to get. + * @return The button element. + */ +function getNotificationButton(win, notificationId, button) { + let notification = win.document.getElementById( + `appMenu-${notificationId}-notification` + ); + ok(!notification.hidden, `${notificationId} notification is showing`); + return notification[button]; +} + +/** + * For staging tests the test updater must be used and this restores the backed + * up real updater if it exists and tries again on failure since Windows debug + * builds at times leave the file in use. After success moveRealUpdater is + * called to continue the setup of the test updater. + */ +function setupTestUpdater() { + return (async function() { + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) { + try { + restoreUpdaterBackup(); + } catch (e) { + logTestInfo( + "Attempt to restore the backed up updater failed... " + + "will try again, Exception: " + + e + ); + await TestUtils.waitForTick(); + await setupTestUpdater(); + return; + } + await moveRealUpdater(); + } + })(); +} + +/** + * Backs up the real updater and tries again on failure since Windows debug + * builds at times leave the file in use. After success it will call + * copyTestUpdater to continue the setup of the test updater. + */ +function moveRealUpdater() { + return (async function() { + try { + // Move away the real updater + let greBinDir = getGREBinDir(); + let updater = greBinDir.clone(); + updater.append(FILE_UPDATER_BIN); + updater.moveTo(greBinDir, FILE_UPDATER_BIN_BAK); + + let greDir = getGREDir(); + let updateSettingsIni = greDir.clone(); + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); + if (updateSettingsIni.exists()) { + updateSettingsIni.moveTo(greDir, FILE_UPDATE_SETTINGS_INI_BAK); + } + + let precomplete = greDir.clone(); + precomplete.append(FILE_PRECOMPLETE); + if (precomplete.exists()) { + precomplete.moveTo(greDir, FILE_PRECOMPLETE_BAK); + } + } catch (e) { + logTestInfo( + "Attempt to move the real updater out of the way failed... " + + "will try again, Exception: " + + e + ); + await TestUtils.waitForTick(); + await moveRealUpdater(); + return; + } + + await copyTestUpdater(); + })(); +} + +/** + * Copies the test updater and tries again on failure since Windows debug builds + * at times leave the file in use. + */ +function copyTestUpdater(attempt = 0) { + return (async function() { + try { + // Copy the test updater + let greBinDir = getGREBinDir(); + let testUpdaterDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + let relPath = REL_PATH_DATA; + let pathParts = relPath.split("/"); + for (let i = 0; i < pathParts.length; ++i) { + testUpdaterDir.append(pathParts[i]); + } + + let testUpdater = testUpdaterDir.clone(); + testUpdater.append(FILE_UPDATER_BIN); + testUpdater.copyToFollowingLinks(greBinDir, FILE_UPDATER_BIN); + + let greDir = getGREDir(); + let updateSettingsIni = greDir.clone(); + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); + writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS); + + let precomplete = greDir.clone(); + precomplete.append(FILE_PRECOMPLETE); + writeFile(precomplete, PRECOMPLETE_CONTENTS); + } catch (e) { + if (attempt < MAX_UPDATE_COPY_ATTEMPTS) { + logTestInfo( + "Attempt to copy the test updater failed... " + + "will try again, Exception: " + + e + ); + await TestUtils.waitForTick(); + await copyTestUpdater(attempt++); + } + } + })(); +} + +/** + * Restores the updater and updater related file that if there a backup exists. + * This is called in setupTestUpdater before the backup of the real updater is + * done in case the previous test failed to restore the file when a test has + * finished. This is also called in finishTestRestoreUpdaterBackup to restore + * the files when a test finishes. + */ +function restoreUpdaterBackup() { + let greBinDir = getGREBinDir(); + let updater = greBinDir.clone(); + let updaterBackup = greBinDir.clone(); + updater.append(FILE_UPDATER_BIN); + updaterBackup.append(FILE_UPDATER_BIN_BAK); + if (updaterBackup.exists()) { + if (updater.exists()) { + updater.remove(true); + } + updaterBackup.moveTo(greBinDir, FILE_UPDATER_BIN); + } + + let greDir = getGREDir(); + let updateSettingsIniBackup = greDir.clone(); + updateSettingsIniBackup.append(FILE_UPDATE_SETTINGS_INI_BAK); + if (updateSettingsIniBackup.exists()) { + let updateSettingsIni = greDir.clone(); + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); + if (updateSettingsIni.exists()) { + updateSettingsIni.remove(false); + } + updateSettingsIniBackup.moveTo(greDir, FILE_UPDATE_SETTINGS_INI); + } + + let precomplete = greDir.clone(); + let precompleteBackup = greDir.clone(); + precomplete.append(FILE_PRECOMPLETE); + precompleteBackup.append(FILE_PRECOMPLETE_BAK); + if (precompleteBackup.exists()) { + if (precomplete.exists()) { + precomplete.remove(false); + } + precompleteBackup.moveTo(greDir, FILE_PRECOMPLETE); + } else if (precomplete.exists()) { + if (readFile(precomplete) == PRECOMPLETE_CONTENTS) { + precomplete.remove(false); + } + } +} + +/** + * When a test finishes this will repeatedly attempt to restore the real updater + * and the other files for the updater if a backup of the file exists. + */ +function finishTestRestoreUpdaterBackup() { + return (async function() { + try { + // Windows debug builds keep the updater file in use for a short period of + // time after the updater process exits. + restoreUpdaterBackup(); + } catch (e) { + logTestInfo( + "Attempt to restore the backed up updater failed... " + + "will try again, Exception: " + + e + ); + + await TestUtils.waitForTick(); + await finishTestRestoreUpdaterBackup(); + } + })(); +} + +/** + * Waits for the About Dialog to load. + * + * @return A promise that returns the domWindow for the About Dialog and + * resolves when the About Dialog loads. + */ +function waitForAboutDialog() { + return new Promise(resolve => { + var listener = { + onOpenWindow: aXULWindow => { + debugDump("About dialog shown..."); + Services.wm.removeListener(listener); + + async function aboutDialogOnLoad() { + domwindow.removeEventListener("load", aboutDialogOnLoad, true); + let chromeURI = "chrome://browser/content/aboutDialog.xhtml"; + is( + domwindow.document.location.href, + chromeURI, + "About dialog appeared" + ); + resolve(domwindow); + } + + var domwindow = aXULWindow.docShell.domWindow; + domwindow.addEventListener("load", aboutDialogOnLoad, true); + }, + onCloseWindow: aXULWindow => {}, + }; + + Services.wm.addListener(listener); + openAboutDialog(); + }); +} + +/** + * Return the first UpdatePatch with the given type. + * + * @param type + * The type of the patch ("complete" or "partial") + * @param update + * The nsIUpdate to select a patch from. + * @return A nsIUpdatePatch object matching the type specified + */ +function getPatchOfType(type, update) { + if (update) { + for (let i = 0; i < update.patchCount; ++i) { + let patch = update.getPatchAt(i); + if (patch && patch.type == type) { + return patch; + } + } + } + return null; +} + +/** + * Runs a Doorhanger update test. This will set various common prefs for + * updating and runs the provided list of steps. + * + * @param params + * An object containing parameters used to run the test. + * @param steps + * An array of test steps to perform. A step will either be an object + * containing expected conditions and actions or a function to call. + * @return A promise which will resolve once all of the steps have been run. + */ +function runDoorhangerUpdateTest(params, steps) { + function processDoorhangerStep(step) { + if (typeof step == "function") { + return step(); + } + + const { + notificationId, + button, + checkActiveUpdate, + pageURLs, + expectedStateOverride, + } = step; + return (async function() { + if (!params.popupShown && !PanelUI.isNotificationPanelOpen) { + await BrowserTestUtils.waitForEvent( + PanelUI.notificationPanel, + "popupshown" + ); + } + const shownNotificationId = AppMenuNotifications.activeNotification.id; + is( + shownNotificationId, + notificationId, + "The right notification showed up." + ); + + let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE; + if (expectedStateOverride) { + expectedState = expectedStateOverride; + } else if (notificationId == "update-restart") { + expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING; + } + let actualState = gAUS.currentState; + is( + actualState, + expectedState, + `The current update state should be ` + + `"${gAUS.getStateName(expectedState)}". Actual: ` + + `"${gAUS.getStateName(actualState)}"` + ); + + if (checkActiveUpdate) { + let activeUpdate = + checkActiveUpdate.state == STATE_DOWNLOADING + ? gUpdateManager.downloadingUpdate + : gUpdateManager.readyUpdate; + ok(!!activeUpdate, "There should be an active update"); + is( + activeUpdate.state, + checkActiveUpdate.state, + `The active update state should equal ${checkActiveUpdate.state}` + ); + } else { + ok( + !gUpdateManager.downloadingUpdate, + "There should not be a downloading update" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + } + + let buttonEl = getNotificationButton(window, notificationId, button); + buttonEl.click(); + + if (pageURLs && pageURLs.manual !== undefined) { + await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + is( + gBrowser.selectedBrowser.currentURI.spec, + pageURLs.manual, + `The page's url should equal ${pageURLs.manual}` + ); + gBrowser.removeTab(gBrowser.selectedTab); + } + })(); + } + + return (async function() { + if (params.slowStaging) { + Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); + } else { + Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1"); + } + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_DISABLEDFORTESTING, false], + [PREF_APP_UPDATE_URL_DETAILS, gDetailsURL], + [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE], + ], + }); + + await setupTestUpdater(); + + let baseURL = URL_HTTP_UPDATE_SJS; + if (params.baseURL) { + baseURL = params.baseURL; + } + let queryString = params.queryString ? params.queryString : ""; + let updateURL = + baseURL + + "?detailsURL=" + + gDetailsURL + + queryString + + getVersionParams(params.version); + setUpdateURL(updateURL); + if (params.checkAttempts) { + // Perform a background check doorhanger test. + executeSoon(() => { + (async function() { + gAUS.checkForBackgroundUpdates(); + for (var i = 0; i < params.checkAttempts - 1; i++) { + await waitForEvent("update-error", "check-attempt-failed"); + gAUS.checkForBackgroundUpdates(); + } + })(); + }); + } else { + // Perform a startup processing doorhanger test. + writeStatusFile(STATE_FAILED_CRC_ERROR); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(params.updates), true); + reloadUpdateManagerData(); + testPostUpdateProcessing(); + } + + for (let step of steps) { + await processDoorhangerStep(step); + } + })(); +} + +/** + * Runs an About Dialog update test. This will set various common prefs for + * updating and runs the provided list of steps. + * + * @param params + * An object containing parameters used to run the test. + * @param steps + * An array of test steps to perform. A step will either be an object + * containing expected conditions and actions or a function to call. + * @return A promise which will resolve once all of the steps have been run. + */ +function runAboutDialogUpdateTest(params, steps) { + let aboutDialog; + function processAboutDialogStep(step) { + if (typeof step == "function") { + return step(aboutDialog); + } + + const { + panelId, + checkActiveUpdate, + continueFile, + downloadInfo, + forceApply, + noContinue, + expectedStateOverride, + } = step; + return (async function() { + await TestUtils.waitForCondition( + () => + aboutDialog.gAppUpdater && + aboutDialog.gAppUpdater.selectedPanel?.id == panelId, + "Waiting for the expected panel ID: " + panelId, + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the panel + // ID and the expected panel ID is printed in the log. + logTestInfo(e); + }); + let { selectedPanel } = aboutDialog.gAppUpdater; + is(selectedPanel.id, panelId, "The panel ID should equal " + panelId); + ok( + BrowserTestUtils.is_visible(selectedPanel), + "The panel should be visible" + ); + + if ( + panelId == "downloading" && + gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_IDLE + ) { + // Now that `AUS.downloadUpdate` is async, we start showing the + // downloading panel while `AUS.downloadUpdate` is still resolving. + // But the below checks assume that this resolution has already + // happened. So we need to wait for things to actually resolve. + debugDump("Waiting for downloading state to actually start"); + await gAUS.stateTransition; + + // Check that the checks that we made above are still valid. + selectedPanel = aboutDialog.gAppUpdater.selectedPanel; + is(selectedPanel.id, panelId, "The panel ID should equal " + panelId); + ok( + BrowserTestUtils.is_visible(selectedPanel), + "The panel should be visible" + ); + } + + let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE; + if (expectedStateOverride) { + expectedState = expectedStateOverride; + } else if (panelId == "apply") { + expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING; + } else if (panelId == "downloading") { + expectedState = Ci.nsIApplicationUpdateService.STATE_DOWNLOADING; + } else if (panelId == "applying") { + expectedState = Ci.nsIApplicationUpdateService.STATE_STAGING; + } + let actualState = gAUS.currentState; + is( + actualState, + expectedState, + `The current update state should be ` + + `"${gAUS.getStateName(expectedState)}". Actual: ` + + `"${gAUS.getStateName(actualState)}"` + ); + + if (checkActiveUpdate) { + let activeUpdate = + checkActiveUpdate.state == STATE_DOWNLOADING + ? gUpdateManager.downloadingUpdate + : gUpdateManager.readyUpdate; + ok(!!activeUpdate, "There should be an active update"); + is( + activeUpdate.state, + checkActiveUpdate.state, + "The active update state should equal " + checkActiveUpdate.state + ); + } else { + ok( + !gUpdateManager.downloadingUpdate, + "There should not be a downloading update" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + } + + // Some tests just want to stop at the downloading state. These won't + // include a continue file in that state. + if (panelId == "downloading" && continueFile) { + for (let i = 0; i < downloadInfo.length; ++i) { + let data = downloadInfo[i]; + await continueFileHandler(continueFile); + let patch = getPatchOfType( + data.patchType, + gUpdateManager.downloadingUpdate + ); + // The update is removed early when the last download fails so check + // that there is a patch before proceeding. + let isLastPatch = i == downloadInfo.length - 1; + if (!isLastPatch || patch) { + let resultName = data.bitsResult ? "bitsResult" : "internalResult"; + patch.QueryInterface(Ci.nsIWritablePropertyBag); + await TestUtils.waitForCondition( + () => patch.getProperty(resultName) == data[resultName], + "Waiting for expected patch property " + + resultName + + " value: " + + data[resultName], + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the + // property value and the expected property value is printed in + // the log. + logTestInfo(e); + }); + is( + "" + patch.getProperty(resultName), + data[resultName], + "The patch property " + + resultName + + " value should equal " + + data[resultName] + ); + + // Check the download status text. It should be something like, + // "1.4 of 1.4 KB". + let expectedText = DownloadUtils.getTransferTotal( + data[resultName] == gBadSizeResult ? 0 : patch.size, + patch.size + ); + Assert.ok( + expectedText, + "Sanity check: Expected download status text should be non-empty" + ); + Assert.equal( + aboutDialog.document.getElementById("downloadStatus").textContent, + expectedText, + "Download status text should be correct" + ); + } + } + } else if (continueFile) { + await continueFileHandler(continueFile); + } + + let linkPanels = ["downloadFailed", "manualUpdate", "unsupportedSystem"]; + if (linkPanels.includes(panelId)) { + // The unsupportedSystem panel uses the update's detailsURL and the + // downloadFailed and manualUpdate panels use the app.update.url.manual + // preference. + let link = selectedPanel.querySelector("label.text-link"); + is( + link.href, + gDetailsURL, + `The panel's link href should equal ${gDetailsURL}` + ); + } + + // Automatically click the download button unless `noContinue` was passed. + let buttonPanels = ["downloadAndInstall", "apply"]; + if (buttonPanels.includes(panelId) && !noContinue) { + let buttonEl = selectedPanel.querySelector("button"); + await TestUtils.waitForCondition( + () => aboutDialog.document.activeElement == buttonEl, + "The button should receive focus" + ); + ok(!buttonEl.disabled, "The button should be enabled"); + // Don't click the button on the apply panel since this will restart the + // application. + if (panelId != "apply" || forceApply) { + buttonEl.click(); + } + } + })(); + } + + return (async function() { + Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_DISABLEDFORTESTING, false], + [PREF_APP_UPDATE_URL_MANUAL, gDetailsURL], + ], + }); + + await setupTestUpdater(); + + let baseURL = URL_HTTP_UPDATE_SJS; + if (params.baseURL) { + baseURL = params.baseURL; + } + let queryString = params.queryString ? params.queryString : ""; + let updateURL = + baseURL + + "?detailsURL=" + + gDetailsURL + + queryString + + getVersionParams(params.version); + if (params.backgroundUpdate) { + setUpdateURL(updateURL); + gAUS.checkForBackgroundUpdates(); + if (params.continueFile) { + await continueFileHandler(params.continueFile); + } + if (params.waitForUpdateState) { + let whichUpdate = + params.waitForUpdateState == STATE_DOWNLOADING + ? "downloadingUpdate" + : "readyUpdate"; + await TestUtils.waitForCondition( + () => + gUpdateManager[whichUpdate] && + gUpdateManager[whichUpdate].state == params.waitForUpdateState, + "Waiting for update state: " + params.waitForUpdateState, + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the panel + // ID and the expected panel ID is printed in the log. + logTestInfo(e); + }); + // Display the UI after the update state equals the expected value. + is( + gUpdateManager[whichUpdate].state, + params.waitForUpdateState, + "The update state value should equal " + params.waitForUpdateState + ); + } + } else { + updateURL += "&slowUpdateCheck=1&useSlowDownloadMar=1"; + setUpdateURL(updateURL); + } + + aboutDialog = await waitForAboutDialog(); + registerCleanupFunction(() => { + aboutDialog.close(); + }); + + for (let step of steps) { + await processAboutDialogStep(step); + } + })(); +} + +/** + * Runs an about:preferences update test. This will set various common prefs for + * updating and runs the provided list of steps. + * + * @param params + * An object containing parameters used to run the test. + * @param steps + * An array of test steps to perform. A step will either be an object + * containing expected conditions and actions or a function to call. + * @return A promise which will resolve once all of the steps have been run. + */ +function runAboutPrefsUpdateTest(params, steps) { + let tab; + function processAboutPrefsStep(step) { + if (typeof step == "function") { + return step(tab); + } + + const { + panelId, + checkActiveUpdate, + continueFile, + downloadInfo, + forceApply, + expectedStateOverride, + } = step; + return (async function() { + await SpecialPowers.spawn( + tab.linkedBrowser, + [{ panelId }], + async ({ panelId }) => { + await ContentTaskUtils.waitForCondition( + () => content.gAppUpdater.selectedPanel?.id == panelId, + "Waiting for the expected panel ID: " + panelId, + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the panel + // ID and the expected panel ID is printed in the log. Use info here + // instead of logTestInfo since logTestInfo isn't available in the + // content task. + info(e); + }); + is( + content.gAppUpdater.selectedPanel.id, + panelId, + "The panel ID should equal " + panelId + ); + } + ); + + if ( + panelId == "downloading" && + gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_IDLE + ) { + // Now that `AUS.downloadUpdate` is async, we start showing the + // downloading panel while `AUS.downloadUpdate` is still resolving. + // But the below checks assume that this resolution has already + // happened. So we need to wait for things to actually resolve. + debugDump("Waiting for downloading state to actually start"); + await gAUS.stateTransition; + + // Check that the checks that we made above are still valid. + await SpecialPowers.spawn( + tab.linkedBrowser, + [{ panelId }], + ({ panelId }) => { + is( + content.gAppUpdater.selectedPanel.id, + panelId, + "The panel ID should equal " + panelId + ); + } + ); + } + + let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE; + if (expectedStateOverride) { + expectedState = expectedStateOverride; + } else if (panelId == "apply") { + expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING; + } else if (panelId == "downloading") { + expectedState = Ci.nsIApplicationUpdateService.STATE_DOWNLOADING; + } else if (panelId == "applying") { + expectedState = Ci.nsIApplicationUpdateService.STATE_STAGING; + } + let actualState = gAUS.currentState; + is( + actualState, + expectedState, + `The current update state should be ` + + `"${gAUS.getStateName(expectedState)}". Actual: ` + + `"${gAUS.getStateName(actualState)}"` + ); + + if (checkActiveUpdate) { + let activeUpdate = + checkActiveUpdate.state == STATE_DOWNLOADING + ? gUpdateManager.downloadingUpdate + : gUpdateManager.readyUpdate; + ok(!!activeUpdate, "There should be an active update"); + is( + activeUpdate.state, + checkActiveUpdate.state, + "The active update state should equal " + checkActiveUpdate.state + ); + } else { + ok( + !gUpdateManager.downloadingUpdate, + "There should not be a downloading update" + ); + ok(!gUpdateManager.readyUpdate, "There should not be a ready update"); + } + + if (panelId == "downloading") { + if (!downloadInfo) { + logTestInfo("no downloadinfo, possible error?"); + } + for (let i = 0; i < downloadInfo.length; ++i) { + let data = downloadInfo[i]; + // The About Dialog tests always specify a continue file. + await continueFileHandler(continueFile); + let patch = getPatchOfType( + data.patchType, + gUpdateManager.downloadingUpdate + ); + // The update is removed early when the last download fails so check + // that there is a patch before proceeding. + let isLastPatch = i == downloadInfo.length - 1; + if (!isLastPatch || patch) { + let resultName = data.bitsResult ? "bitsResult" : "internalResult"; + patch.QueryInterface(Ci.nsIWritablePropertyBag); + await TestUtils.waitForCondition( + () => patch.getProperty(resultName) == data[resultName], + "Waiting for expected patch property " + + resultName + + " value: " + + data[resultName], + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the + // property value and the expected property value is printed in + // the log. + logTestInfo(e); + }); + is( + "" + patch.getProperty(resultName), + data[resultName], + "The patch property " + + resultName + + " value should equal " + + data[resultName] + ); + + // Check the download status text. It should be something like, + // "Downloading update — 1.4 of 1.4 KB". We check only the second + // part to make sure that the downloaded size is updated correctly. + let actualText = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => content.document.getElementById("downloading").textContent + ); + let expectedSuffix = DownloadUtils.getTransferTotal( + data[resultName] == gBadSizeResult ? 0 : patch.size, + patch.size + ); + Assert.ok( + expectedSuffix, + "Sanity check: Expected download status text should be non-empty" + ); + Assert.ok( + actualText.endsWith(expectedSuffix), + "Download status text should end as expected: " + + JSON.stringify({ actualText, expectedSuffix }) + ); + } + } + } else if (continueFile) { + await continueFileHandler(continueFile); + } + + await SpecialPowers.spawn( + tab.linkedBrowser, + [{ panelId, gDetailsURL, forceApply }], + async ({ panelId, gDetailsURL, forceApply }) => { + let linkPanels = [ + "downloadFailed", + "manualUpdate", + "unsupportedSystem", + ]; + if (linkPanels.includes(panelId)) { + let { selectedPanel } = content.gAppUpdater; + // The unsupportedSystem panel uses the update's detailsURL and the + // downloadFailed and manualUpdate panels use the app.update.url.manual + // preference. + let selector = "label.text-link"; + // The downloadFailed panel in about:preferences uses an anchor + // instead of a label for the link. + if (selectedPanel.id == "downloadFailed") { + selector = "a.text-link"; + } + let link = selectedPanel.querySelector(selector); + is( + link.href, + gDetailsURL, + `The panel's link href should equal ${gDetailsURL}` + ); + } + + let buttonPanels = ["downloadAndInstall", "apply"]; + if (buttonPanels.includes(panelId)) { + let { selectedPanel } = content.gAppUpdater; + let buttonEl = selectedPanel.querySelector("button"); + // Note: The about:preferences doesn't focus the button like the + // About Dialog does. + ok(!buttonEl.disabled, "The button should be enabled"); + // Don't click the button on the apply panel since this will restart + // the application. + if (selectedPanel.id != "apply" || forceApply) { + buttonEl.click(); + } + } + } + ); + })(); + } + + return (async function() { + Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_DISABLEDFORTESTING, false], + [PREF_APP_UPDATE_URL_MANUAL, gDetailsURL], + ], + }); + + await setupTestUpdater(); + + let baseURL = URL_HTTP_UPDATE_SJS; + if (params.baseURL) { + baseURL = params.baseURL; + } + let queryString = params.queryString ? params.queryString : ""; + let updateURL = + baseURL + + "?detailsURL=" + + gDetailsURL + + queryString + + getVersionParams(params.version); + if (params.backgroundUpdate) { + setUpdateURL(updateURL); + gAUS.checkForBackgroundUpdates(); + if (params.continueFile) { + await continueFileHandler(params.continueFile); + } + if (params.waitForUpdateState) { + // Wait until the update state equals the expected value before + // displaying the UI. + let whichUpdate = + params.waitForUpdateState == STATE_DOWNLOADING + ? "downloadingUpdate" + : "readyUpdate"; + await TestUtils.waitForCondition( + () => + gUpdateManager[whichUpdate] && + gUpdateManager[whichUpdate].state == params.waitForUpdateState, + "Waiting for update state: " + params.waitForUpdateState, + undefined, + 200 + ).catch(e => { + // Instead of throwing let the check below fail the test so the panel + // ID and the expected panel ID is printed in the log. + logTestInfo(e); + }); + is( + gUpdateManager[whichUpdate].state, + params.waitForUpdateState, + "The update state value should equal " + params.waitForUpdateState + ); + } + } else { + updateURL += "&slowUpdateCheck=1&useSlowDownloadMar=1"; + setUpdateURL(updateURL); + } + + tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:preferences" + ); + registerCleanupFunction(async () => { + await BrowserTestUtils.removeTab(tab); + }); + + // Scroll the UI into view so it is easier to troubleshoot tests. + await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + content.document.getElementById("updatesCategory").scrollIntoView(); + }); + + for (let step of steps) { + await processAboutPrefsStep(step); + } + })(); +} + +/** + * Removes the modified update-settings.ini file so the updater will fail to + * stage an update. + */ +function removeUpdateSettingsIni() { + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) { + let greDir = getGREDir(); + let updateSettingsIniBak = greDir.clone(); + updateSettingsIniBak.append(FILE_UPDATE_SETTINGS_INI_BAK); + if (updateSettingsIniBak.exists()) { + let updateSettingsIni = greDir.clone(); + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); + updateSettingsIni.remove(false); + } + } +} + +/** + * Runs a telemetry update test. This will set various common prefs for + * updating, checks for an update, and waits for the specified observer + * notification. + * + * @param updateParams + * Params which will be sent to app_update.sjs. + * @param event + * The observer notification to wait for before proceeding. + * @param stageFailure (optional) + * Whether to force a staging failure by removing the modified + * update-settings.ini file. + * @return A promise which will resolve after the . + */ +function runTelemetryUpdateTest(updateParams, event, stageFailure = false) { + return (async function() { + Services.telemetry.clearScalars(); + Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1"); + await SpecialPowers.pushPrefEnv({ + set: [[PREF_APP_UPDATE_DISABLEDFORTESTING, false]], + }); + + await setupTestUpdater(); + + if (stageFailure) { + removeUpdateSettingsIni(); + } + + let updateURL = + URL_HTTP_UPDATE_SJS + + "?detailsURL=" + + gDetailsURL + + updateParams + + getVersionParams(); + setUpdateURL(updateURL); + gAUS.checkForBackgroundUpdates(); + await waitForEvent(event); + })(); +} diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini new file mode 100644 index 0000000000..949ad255c9 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini @@ -0,0 +1,18 @@ +[DEFAULT] +head = head.js +prefs = + app.update.BITS.enabled=false + browser.policies.alternatePath='<test-root>/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json' +support-files = + !/toolkit/mozapps/update/tests/browser/head.js + config_manual_app_update_only.json + ../../data/shared.js + ../../data/app_update.sjs + ../testConstants.js +skip-if = os == 'win' && msix # Updater is disabled in MSIX builds + +[browser_aboutPrefs_fc_autoUpdateTrue.js] +[browser_aboutPrefs_fc_autoUpdateFalse.js] +[browser_aboutDialog_fc_autoUpdateTrue.js] +[browser_aboutDialog_fc_autoUpdateFalse.js] +[browser_noBackgroundUpdate.js] diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js new file mode 100644 index 0000000000..169e66033a --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_manual_app_update_policy() { + await setAppUpdateAutoEnabledHelper(false); + + is( + Services.policies.isAllowed("autoAppUpdateChecking"), + false, + "autoAppUpdateChecking should be disabled by policy" + ); + is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true"); + + let downloadInfo = [{ patchType: "partial", internalResult: "0" }]; + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js new file mode 100644 index 0000000000..0a59f59d71 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_manual_app_update_policy() { + await setAppUpdateAutoEnabledHelper(true); + + is( + Services.policies.isAllowed("autoAppUpdateChecking"), + false, + "autoAppUpdateChecking should be disabled by policy" + ); + is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true"); + + let downloadInfo = [{ patchType: "partial", internalResult: "0" }]; + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutDialogUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js new file mode 100644 index 0000000000..97da951189 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_manual_app_update_policy() { + await setAppUpdateAutoEnabledHelper(false); + + is( + Services.policies.isAllowed("autoAppUpdateChecking"), + false, + "autoAppUpdateChecking should be disabled by policy" + ); + is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true"); + + let downloadInfo = [{ patchType: "partial", internalResult: "0" }]; + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + async tab => { + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + let setting = content.document.getElementById( + "updateSettingsContainer" + ); + is( + setting.hidden, + true, + "Update choices should be disabled when manualUpdateOnly" + ); + }); + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js new file mode 100644 index 0000000000..f716357b0f --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_manual_app_update_policy() { + await setAppUpdateAutoEnabledHelper(true); + + is( + Services.policies.isAllowed("autoAppUpdateChecking"), + false, + "autoAppUpdateChecking should be disabled by policy" + ); + is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true"); + + let downloadInfo = [{ patchType: "partial", internalResult: "0" }]; + // Since the partial should be successful specify an invalid size for the + // complete update. + let params = { queryString: "&invalidCompleteSize=1" }; + await runAboutPrefsUpdateTest(params, [ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "downloadAndInstall", + checkActiveUpdate: null, + continueFile: null, + }, + { + panelId: "downloading", + checkActiveUpdate: { state: STATE_DOWNLOADING }, + continueFile: CONTINUE_DOWNLOAD, + downloadInfo, + }, + { + panelId: "apply", + checkActiveUpdate: { state: STATE_PENDING }, + continueFile: null, + }, + async tab => { + await SpecialPowers.spawn(tab.linkedBrowser, [], async function() { + let setting = content.document.getElementById( + "updateSettingsContainer" + ); + is( + setting.hidden, + true, + "Update choices should be disabled when manualUpdateOnly" + ); + }); + }, + ]); +}); diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js new file mode 100644 index 0000000000..38b27e31ad --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_manual_app_update_policy() { + // Unfortunately, we can't really test the other background update entry + // point, gAUS.notify, because it doesn't return anything and it would be + // a bit overkill to edit the nsITimerCallback interface just for this test. + // But the two entry points just immediately call the same function, so this + // should probably be alright. + is( + gAUS.checkForBackgroundUpdates(), + false, + "gAUS.checkForBackgroundUpdates() should not proceed with update check" + ); +}); diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json b/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json new file mode 100644 index 0000000000..4e7c785bc1 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json @@ -0,0 +1,5 @@ +{ + "policies": { + "ManualAppUpdateOnly": true + } +} diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js new file mode 100644 index 0000000000..a1967394d6 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js @@ -0,0 +1,10 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/head.js", + this +); diff --git a/toolkit/mozapps/update/tests/browser/testConstants.js b/toolkit/mozapps/update/tests/browser/testConstants.js new file mode 100644 index 0000000000..915391054b --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/testConstants.js @@ -0,0 +1,7 @@ +const REL_PATH_DATA = "browser/toolkit/mozapps/update/tests/browser/"; +const URL_HOST = "http://127.0.0.1:8888"; +const URL_PATH_UPDATE_XML = "/" + REL_PATH_DATA + "app_update.sjs"; +const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML; +const CONTINUE_CHECK = "continueCheck"; +const CONTINUE_DOWNLOAD = "continueDownload"; +const CONTINUE_STAGING = "continueStaging"; |