diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js new file mode 100644 index 0000000000..2cd479ccf9 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js @@ -0,0 +1,485 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This verifies that delaying a system add-on update works. + +PromiseTestUtils.allowMatchingRejectionsGlobally( + /Message manager disconnected/ +); + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +const IGNORE_ID = "system_delay_ignore@tests.mozilla.org"; +const COMPLETE_ID = "system_delay_complete@tests.mozilla.org"; +const DEFER_ID = "system_delay_defer@tests.mozilla.org"; +const DEFER2_ID = "system_delay_defer2@tests.mozilla.org"; +const DEFER_ALSO_ID = "system_delay_defer_also@tests.mozilla.org"; +const NORMAL_ID = "system1@tests.mozilla.org"; + +const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true); +registerDirectory("XREAppFeat", distroDir); + +registerCleanupFunction(() => { + distroDir.remove(true); +}); + +AddonTestUtils.usePrivilegedSignatures = id => "system"; + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42"); + +function promiseInstallPostponed(addonID1, addonID2) { + return new Promise((resolve, reject) => { + let seen = []; + let listener = { + onInstallFailed: () => { + AddonManager.removeInstallListener(listener); + reject("extension installation should not have failed"); + }, + onInstallEnded: install => { + AddonManager.removeInstallListener(listener); + reject( + `extension installation should not have ended for ${install.addon.id}` + ); + }, + onInstallPostponed: install => { + seen.push(install.addon.id); + if (seen.includes(addonID1) && seen.includes(addonID2)) { + AddonManager.removeInstallListener(listener); + resolve(); + } + }, + }; + + AddonManager.addInstallListener(listener); + }); +} + +function promiseInstallResumed(addonID1, addonID2) { + return new Promise((resolve, reject) => { + let seenPostponed = []; + let seenEnded = []; + let listener = { + onInstallFailed: () => { + AddonManager.removeInstallListener(listener); + reject("extension installation should not have failed"); + }, + onInstallEnded: install => { + seenEnded.push(install.addon.id); + if ( + seenEnded.includes(addonID1) && + seenEnded.includes(addonID2) && + seenPostponed.includes(addonID1) && + seenPostponed.includes(addonID2) + ) { + AddonManager.removeInstallListener(listener); + resolve(); + } + }, + onInstallPostponed: install => { + seenPostponed.push(install.addon.id); + }, + }; + + AddonManager.addInstallListener(listener); + }); +} + +function promiseInstallDeferred(addonID1, addonID2) { + return new Promise((resolve, reject) => { + let seenEnded = []; + let listener = { + onInstallFailed: () => { + AddonManager.removeInstallListener(listener); + reject("extension installation should not have failed"); + }, + onInstallEnded: install => { + seenEnded.push(install.addon.id); + if (seenEnded.includes(addonID1) && seenEnded.includes(addonID2)) { + AddonManager.removeInstallListener(listener); + resolve(); + } + }, + }; + + AddonManager.addInstallListener(listener); + }); +} + +async function checkAddon(addonID, { version }) { + let addon = await promiseAddonByID(addonID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, version); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); +} + +// Tests below have webextension background scripts inline. +/* globals browser */ + +// add-on registers upgrade listener, and ignores update. +add_task(async function test_addon_upgrade_on_restart() { + // discard system addon updates + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, ""); + + let xpi = await getSystemAddonXPI(1, "1.0"); + xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`); + + // Version 1.0 of an extension that ignores updates. + function background() { + browser.runtime.onUpdateAvailable.addListener(() => { + browser.test.sendMessage("got-update"); + }); + } + + xpi = await createTempWebExtensionFile({ + background, + + manifest: { + version: "1.0", + browser_specific_settings: { gecko: { id: IGNORE_ID } }, + }, + }); + xpi.copyTo(distroDir, `${IGNORE_ID}.xpi`); + + // Version 2.0 of the same extension. + let xpi2 = await createTempWebExtensionFile({ + manifest: { + version: "2.0", + browser_specific_settings: { gecko: { id: IGNORE_ID } }, + }, + }); + + await overrideBuiltIns({ system: [IGNORE_ID, NORMAL_ID] }); + + let extension = ExtensionTestUtils.expectExtension(IGNORE_ID); + + await Promise.all([promiseStartupManager(), extension.awaitStartup()]); + + let updateList = [ + { + id: IGNORE_ID, + version: "2.0", + path: "system_delay_ignore_2.xpi", + xpi: xpi2, + }, + { + id: NORMAL_ID, + version: "2.0", + path: "system1_2.xpi", + xpi: await getSystemAddonXPI(1, "2.0"), + }, + ]; + + await Promise.all([ + promiseInstallPostponed(IGNORE_ID, NORMAL_ID), + installSystemAddons(buildSystemAddonUpdates(updateList)), + extension.awaitMessage("got-update"), + ]); + + // addon upgrade has been delayed. + await checkAddon(IGNORE_ID, { version: "1.0" }); + // other addons in the set are delayed as well. + await checkAddon(NORMAL_ID, { version: "1.0" }); + + // restarting allows upgrades to proceed + await Promise.all([promiseRestartManager(), extension.awaitStartup()]); + + await checkAddon(IGNORE_ID, { version: "2.0" }); + await checkAddon(NORMAL_ID, { version: "2.0" }); + + await promiseShutdownManager(); +}); + +// add-on registers upgrade listener, and allows update. +add_task(async function test_addon_upgrade_on_reload() { + // discard system addon updates + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, ""); + + let xpi = await getSystemAddonXPI(1, "1.0"); + xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`); + + // Version 1.0 of an extension that listens for and immediately + // applies updates. + function background() { + browser.runtime.onUpdateAvailable.addListener(function listener() { + browser.runtime.onUpdateAvailable.removeListener(listener); + browser.test.sendMessage("got-update"); + browser.runtime.reload(); + }); + } + + xpi = await createTempWebExtensionFile({ + background, + + manifest: { + version: "1.0", + browser_specific_settings: { gecko: { id: COMPLETE_ID } }, + }, + }); + xpi.copyTo(distroDir, `${COMPLETE_ID}.xpi`); + + // Version 2.0 of the same extension. + let xpi2 = await createTempWebExtensionFile({ + manifest: { + version: "2.0", + browser_specific_settings: { gecko: { id: COMPLETE_ID } }, + }, + }); + + await overrideBuiltIns({ system: [COMPLETE_ID, NORMAL_ID] }); + + let extension = ExtensionTestUtils.expectExtension(COMPLETE_ID); + + await Promise.all([promiseStartupManager(), extension.awaitStartup()]); + + let updateList = [ + { + id: COMPLETE_ID, + version: "2.0", + path: "system_delay_complete_2.xpi", + xpi: xpi2, + }, + { + id: NORMAL_ID, + version: "2.0", + path: "system1_2.xpi", + xpi: await getSystemAddonXPI(1, "2.0"), + }, + ]; + + // initial state + await checkAddon(COMPLETE_ID, { version: "1.0" }); + await checkAddon(NORMAL_ID, { version: "1.0" }); + + // We should see that the onUpdateListener executed, then see the + // update resume. + await Promise.all([ + extension.awaitMessage("got-update"), + promiseInstallResumed(COMPLETE_ID, NORMAL_ID), + installSystemAddons(buildSystemAddonUpdates(updateList)), + ]); + await extension.awaitStartup(); + + // addon upgrade has been allowed + await checkAddon(COMPLETE_ID, { version: "2.0" }); + // other upgrades in the set are allowed as well + await checkAddon(NORMAL_ID, { version: "2.0" }); + + // restarting changes nothing + await Promise.all([promiseRestartManager(), extension.awaitStartup()]); + + await checkAddon(COMPLETE_ID, { version: "2.0" }); + await checkAddon(NORMAL_ID, { version: "2.0" }); + + await promiseShutdownManager(); +}); + +function delayBackground() { + browser.test.onMessage.addListener(msg => { + if (msg !== "reload") { + browser.test.fail(`Got unexpected test message: ${msg}`); + } + browser.runtime.reload(); + }); + browser.runtime.onUpdateAvailable.addListener(async function listener() { + browser.runtime.onUpdateAvailable.removeListener(listener); + browser.test.sendMessage("got-update"); + }); +} + +// Upgrade listener initially defers then proceeds after a pause. +add_task(async function test_addon_upgrade_after_pause() { + // discard system addon updates + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, ""); + + let xpi = await getSystemAddonXPI(1, "1.0"); + xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`); + + // Version 1.0 of an extension that delays upgrades. + xpi = await createTempWebExtensionFile({ + background: delayBackground, + manifest: { + version: "1.0", + browser_specific_settings: { gecko: { id: DEFER_ID } }, + }, + }); + xpi.copyTo(distroDir, `${DEFER_ID}.xpi`); + + // Version 2.0 of the same xtension. + let xpi2 = await createTempWebExtensionFile({ + manifest: { + version: "2.0", + browser_specific_settings: { gecko: { id: DEFER_ID } }, + }, + }); + + await overrideBuiltIns({ system: [DEFER_ID, NORMAL_ID] }); + + let extension = ExtensionTestUtils.expectExtension(DEFER_ID); + + await Promise.all([promiseStartupManager(), extension.awaitStartup()]); + + let updateList = [ + { + id: DEFER_ID, + version: "2.0", + path: "system_delay_defer_2.xpi", + xpi: xpi2, + }, + { + id: NORMAL_ID, + version: "2.0", + path: "system1_2.xpi", + xpi: await getSystemAddonXPI(1, "2.0"), + }, + ]; + + await Promise.all([ + promiseInstallPostponed(DEFER_ID, NORMAL_ID), + installSystemAddons(buildSystemAddonUpdates(updateList)), + extension.awaitMessage("got-update"), + ]); + + // upgrade is initially postponed + await checkAddon(DEFER_ID, { version: "1.0" }); + // other addons in the set are postponed as well. + await checkAddon(NORMAL_ID, { version: "1.0" }); + + let deferred = promiseInstallDeferred(DEFER_ID, NORMAL_ID); + + // Tell the extension to proceed with the update. + extension.setRestarting(); + extension.sendMessage("reload"); + + await Promise.all([deferred, extension.awaitStartup()]); + + // addon upgrade has been allowed + await checkAddon(DEFER_ID, { version: "2.0" }); + // other addons in the set are allowed as well. + await checkAddon(NORMAL_ID, { version: "2.0" }); + + // restarting changes nothing + await promiseRestartManager(); + await extension.awaitStartup(); + + await checkAddon(DEFER_ID, { version: "2.0" }); + await checkAddon(NORMAL_ID, { version: "2.0" }); + + await promiseShutdownManager(); +}); + +// Multiple add-ons register update listeners, initially defers then +// each unblock in turn. +add_task(async function test_multiple_addon_upgrade_postpone() { + // discard system addon updates. + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, ""); + + let updateList = []; + + let xpi = await createTempWebExtensionFile({ + background: delayBackground, + manifest: { + version: "1.0", + browser_specific_settings: { gecko: { id: DEFER2_ID } }, + }, + }); + xpi.copyTo(distroDir, `${DEFER2_ID}.xpi`); + + xpi = await createTempWebExtensionFile({ + manifest: { + version: "2.0", + browser_specific_settings: { gecko: { id: DEFER2_ID } }, + }, + }); + updateList.push({ + id: DEFER2_ID, + version: "2.0", + path: "system_delay_defer_2.xpi", + xpi, + }); + + xpi = await createTempWebExtensionFile({ + background: delayBackground, + manifest: { + version: "1.0", + browser_specific_settings: { gecko: { id: DEFER_ALSO_ID } }, + }, + }); + xpi.copyTo(distroDir, `${DEFER_ALSO_ID}.xpi`); + + xpi = await createTempWebExtensionFile({ + manifest: { + version: "2.0", + browser_specific_settings: { gecko: { id: DEFER_ALSO_ID } }, + }, + }); + updateList.push({ + id: DEFER_ALSO_ID, + version: "2.0", + path: "system_delay_defer_also_2.xpi", + xpi, + }); + + await overrideBuiltIns({ system: [DEFER2_ID, DEFER_ALSO_ID] }); + + let extension1 = ExtensionTestUtils.expectExtension(DEFER2_ID); + let extension2 = ExtensionTestUtils.expectExtension(DEFER_ALSO_ID); + + await Promise.all([ + promiseStartupManager(), + extension1.awaitStartup(), + extension2.awaitStartup(), + ]); + + await Promise.all([ + promiseInstallPostponed(DEFER2_ID, DEFER_ALSO_ID), + installSystemAddons(buildSystemAddonUpdates(updateList)), + extension1.awaitMessage("got-update"), + extension2.awaitMessage("got-update"), + ]); + + // upgrade is initially postponed + await checkAddon(DEFER2_ID, { version: "1.0" }); + // other addons in the set are postponed as well. + await checkAddon(DEFER_ALSO_ID, { version: "1.0" }); + + let deferred = promiseInstallDeferred(DEFER2_ID, DEFER_ALSO_ID); + + // Let one extension request that the update proceed. + extension1.setRestarting(); + extension1.sendMessage("reload"); + + // Upgrade blockers still present. + await checkAddon(DEFER2_ID, { version: "1.0" }); + await checkAddon(DEFER_ALSO_ID, { version: "1.0" }); + + // Let the second extension allow the update to proceed. + extension2.setRestarting(); + extension2.sendMessage("reload"); + + await Promise.all([ + deferred, + extension1.awaitStartup(), + extension2.awaitStartup(), + ]); + + // addon upgrade has been allowed + await checkAddon(DEFER2_ID, { version: "2.0" }); + await checkAddon(DEFER_ALSO_ID, { version: "2.0" }); + + // restarting changes nothing + await Promise.all([ + promiseRestartManager(), + extension1.awaitStartup(), + extension2.awaitStartup(), + ]); + + await checkAddon(DEFER2_ID, { version: "2.0" }); + await checkAddon(DEFER_ALSO_ID, { version: "2.0" }); + + await promiseShutdownManager(); +}); |