diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /toolkit/mozapps/update/tests/unit_background_update | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/mozapps/update/tests/unit_background_update')
6 files changed, 658 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/tests/unit_background_update/head.js b/toolkit/mozapps/update/tests/unit_background_update/head.js new file mode 100644 index 0000000000..089fe32977 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/head.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* import-globals-from ../data/xpcshellUtilsAUS.js */ +load("xpcshellUtilsAUS.js"); +gIsServiceTest = false; + +const { BackgroundTasksTestUtils } = ChromeUtils.import( + "resource://testing-common/BackgroundTasksTestUtils.jsm" +); +BackgroundTasksTestUtils.init(this); +const do_backgroundtask = BackgroundTasksTestUtils.do_backgroundtask.bind( + BackgroundTasksTestUtils +); +const setupProfileService = BackgroundTasksTestUtils.setupProfileService.bind( + BackgroundTasksTestUtils +); diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_exitcodes.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_exitcodes.js new file mode 100644 index 0000000000..7ce3f27d66 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_exitcodes.js @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=4 ts=4 sts=4 et + * 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 test exercises functionality and also ensures the exit codes, +// which are a public API, do not change over time. +const { EXIT_CODE } = ChromeUtils.import( + "resource://gre/modules/BackgroundUpdate.jsm" +).BackgroundUpdate; + +setupProfileService(); + +// Ensure launched background tasks don't see this xpcshell as a concurrent +// instance. +let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( + Ci.nsIUpdateSyncManager +); +let lockFile = do_get_profile(); +lockFile.append("customExePath"); +lockFile.append("customExe"); +syncManager.resetLock(lockFile); + +add_task(async function test_default_profile_does_not_exist() { + // Pretend there's no default profile. + let exitCode = await do_backgroundtask("backgroundupdate", { + extraEnv: { + MOZ_BACKGROUNDTASKS_NO_DEFAULT_PROFILE: "1", + }, + }); + Assert.equal(EXIT_CODE.DEFAULT_PROFILE_DOES_NOT_EXIST, exitCode); + Assert.equal(11, exitCode); +}); + +add_task(async function test_default_profile_cannot_be_locked() { + // Now, lock the default profile. + let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService( + Ci.nsIToolkitProfileService + ); + + let file = do_get_profile(); + file.append("profile_cannot_be_locked"); + + let profile = profileService.createUniqueProfile( + file, + "test_default_profile" + ); + let lock = profile.lock({}); + + try { + let exitCode = await do_backgroundtask("backgroundupdate", { + extraEnv: { + MOZ_BACKGROUNDTASKS_DEFAULT_PROFILE_PATH: lock.directory.path, + }, + }); + Assert.equal(EXIT_CODE.DEFAULT_PROFILE_CANNOT_BE_LOCKED, exitCode); + Assert.equal(12, exitCode); + } finally { + lock.unlock(); + } +}); + +add_task(async function test_default_profile_cannot_be_read() { + // Finally, provide an empty default profile, one without prefs. + let file = do_get_profile(); + file.append("profile_cannot_be_read"); + + await IOUtils.makeDirectory(file.path); + + let exitCode = await do_backgroundtask("backgroundupdate", { + extraEnv: { + MOZ_BACKGROUNDTASKS_DEFAULT_PROFILE_PATH: file.path, + }, + }); + Assert.equal(EXIT_CODE.DEFAULT_PROFILE_CANNOT_BE_READ, exitCode); + Assert.equal(13, exitCode); +}); diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js new file mode 100644 index 0000000000..4a7d1502a5 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js @@ -0,0 +1,222 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=4 ts=4 sts=4 et + * 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"; + +const { ASRouterTargeting } = ChromeUtils.import( + "resource://activity-stream/lib/ASRouterTargeting.jsm" +); + +const { BackgroundUpdate } = ChromeUtils.import( + "resource://gre/modules/BackgroundUpdate.jsm" +); + +const { maybeSubmitBackgroundUpdatePing } = ChromeUtils.import( + "resource://gre/modules/backgroundtasks/BackgroundTask_backgroundupdate.jsm" +); + +XPCOMUtils.defineLazyServiceGetter( + this, + "UpdateService", + "@mozilla.org/updates/update-service;1", + "nsIApplicationUpdateService" +); + +add_setup(function test_setup() { + // FOG needs a profile directory to put its data in. + do_get_profile(); + + // We need to initialize it once, otherwise operations will be stuck in the pre-init queue. + Services.fog.initializeFOG(); + + setupProfileService(); +}); + +add_task(async function test_record_update_environment() { + await BackgroundUpdate.recordUpdateEnvironment(); + + let pingSubmitted = false; + let appUpdateAutoEnabled = await UpdateUtils.getAppUpdateAutoEnabled(); + let backgroundUpdateEnabled = await UpdateUtils.readUpdateConfigSetting( + "app.update.background.enabled" + ); + GleanPings.backgroundUpdate.testBeforeNextSubmit(reason => { + Assert.equal(reason, "backgroundupdate_task"); + + pingSubmitted = true; + Assert.equal( + Services.prefs.getBoolPref("app.update.service.enabled", false), + Glean.update.serviceEnabled.testGetValue() + ); + + Assert.equal( + appUpdateAutoEnabled, + Glean.update.autoDownload.testGetValue() + ); + + Assert.equal( + backgroundUpdateEnabled, + Glean.update.backgroundUpdate.testGetValue() + ); + + Assert.equal( + UpdateUtils.UpdateChannel, + Glean.update.channel.testGetValue() + ); + Assert.equal( + !Services.policies || Services.policies.isAllowed("appUpdate"), + Glean.update.enabled.testGetValue() + ); + + Assert.equal( + UpdateService.canUsuallyApplyUpdates, + Glean.update.canUsuallyApplyUpdates.testGetValue() + ); + Assert.equal( + UpdateService.canUsuallyCheckForUpdates, + Glean.update.canUsuallyCheckForUpdates.testGetValue() + ); + Assert.equal( + UpdateService.canUsuallyStageUpdates, + Glean.update.canUsuallyStageUpdates.testGetValue() + ); + Assert.equal( + UpdateService.canUsuallyUseBits, + Glean.update.canUsuallyUseBits.testGetValue() + ); + }); + + // There's nothing async in this function atm, but it's annotated async, so.. + await maybeSubmitBackgroundUpdatePing(); + + ok(pingSubmitted, "'background-update' ping was submitted"); +}); + +async function do_readTargeting(content, beforeNextSubmitCallback) { + let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService( + Ci.nsIToolkitProfileService + ); + + let file = do_get_profile(); + file.append("profile_cannot_be_locked"); + + let profile = profileService.createUniqueProfile( + file, + "test_default_profile" + ); + + let targetingSnapshot = profile.rootDir.clone(); + targetingSnapshot.append("targeting.snapshot.json"); + + if (content) { + await IOUtils.writeUTF8(targetingSnapshot.path, content); + } + + let lock = profile.lock({}); + + Services.fog.testResetFOG(); + try { + await BackgroundUpdate.readFirefoxMessagingSystemTargetingSnapshot(lock); + } finally { + lock.unlock(); + } + + let pingSubmitted = false; + GleanPings.backgroundUpdate.testBeforeNextSubmit(reason => { + pingSubmitted = true; + return beforeNextSubmitCallback(reason); + }); + + // There's nothing async in this function atm, but it's annotated async, so.. + await maybeSubmitBackgroundUpdatePing(); + + ok(pingSubmitted, "'background-update' ping was submitted"); +} + +// Missing targeting is anticipated. +add_task(async function test_targeting_missing() { + await do_readTargeting(null, reason => { + Assert.equal(false, Glean.backgroundUpdate.targetingExists.testGetValue()); + + Assert.equal( + false, + Glean.backgroundUpdate.targetingException.testGetValue() + ); + }); +}); + +// Malformed JSON yields an exception. +add_task(async function test_targeting_exception() { + await do_readTargeting("{", reason => { + Assert.equal(false, Glean.backgroundUpdate.targetingExists.testGetValue()); + + Assert.equal( + true, + Glean.backgroundUpdate.targetingException.testGetValue() + ); + }); +}); + +// Well formed targeting values are reflected into the Glean telemetry. +add_task(async function test_targeting_exists() { + // We can't take a full environment snapshot under `xpcshell`; these are just + // the items we need. + let target = { + currentDate: ASRouterTargeting.Environment.currentDate, + profileAgeCreated: ASRouterTargeting.Environment.profileAgeCreated, + firefoxVersion: ASRouterTargeting.Environment.firefoxVersion, + }; + let targetSnapshot = await ASRouterTargeting.getEnvironmentSnapshot(target); + + await do_readTargeting(JSON.stringify(targetSnapshot), reason => { + Assert.equal(true, Glean.backgroundUpdate.targetingExists.testGetValue()); + + Assert.equal( + false, + Glean.backgroundUpdate.targetingException.testGetValue() + ); + + // `environment.firefoxVersion` is a positive integer. + Assert.ok( + Glean.backgroundUpdate.targetingEnvFirefoxVersion.testGetValue() > 0 + ); + + Assert.equal( + targetSnapshot.environment.firefoxVersion, + Glean.backgroundUpdate.targetingEnvFirefoxVersion.testGetValue() + ); + + let profileAge = Glean.backgroundUpdate.targetingEnvProfileAge.testGetValue(); + + Assert.ok(profileAge instanceof Date); + Assert.ok(0 < profileAge.getTime()); + Assert.ok(profileAge.getTime() < Date.now()); + + // `environment.profileAgeCreated` is an integer, milliseconds since the + // Unix epoch. + let targetProfileAge = new Date( + targetSnapshot.environment.profileAgeCreated + ); + // Our `time_unit: day` has Glean round to the nearest day *in the local + // timezone*, so we must do the same. + targetProfileAge.setHours(0, 0, 0, 0); + + Assert.equal(targetProfileAge.toISOString(), profileAge.toISOString()); + + let currentDate = Glean.backgroundUpdate.targetingEnvCurrentDate.testGetValue(); + + Assert.ok(0 < currentDate.getTime()); + Assert.ok(currentDate.getTime() < Date.now()); + + // `environment.currentDate` is in ISO string format. + let targetCurrentDate = new Date(targetSnapshot.environment.currentDate); + // Our `time_unit: day` has Glean round to the nearest day *in the local + // timezone*, so we must do the same. + targetCurrentDate.setHours(0, 0, 0, 0); + + Assert.equal(targetCurrentDate.toISOString(), currentDate.toISOString()); + }); +}); diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_schedule.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_schedule.js new file mode 100644 index 0000000000..61b1f57252 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_schedule.js @@ -0,0 +1,116 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=4 ts=4 sts=4 et + * 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"; + +const { BackgroundUpdate } = ChromeUtils.import( + "resource://gre/modules/BackgroundUpdate.jsm" +); +let reasons = () => BackgroundUpdate._reasonsToNotScheduleUpdates(); +let REASON = BackgroundUpdate.REASON; + +const { AddonTestUtils } = ChromeUtils.import( + "resource://testing-common/AddonTestUtils.jsm" +); +const { ExtensionTestUtils } = ChromeUtils.import( + "resource://testing-common/ExtensionXPCShellUtils.jsm" +); + +setupProfileService(); + +// Setup that allows to install a langpack. +ExtensionTestUtils.init(this); + +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); + +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "42", + "42" +); + +add_task( + { + skip_if: () => !AppConstants.MOZ_BACKGROUNDTASKS, + }, + async function test_reasons_schedule_langpacks() { + await AddonTestUtils.promiseStartupManager(); + + Services.prefs.setBoolPref("app.update.langpack.enabled", true); + + let result = await reasons(); + Assert.ok( + !result.includes(REASON.LANGPACK_INSTALLED), + "Reasons does not include LANGPACK_INSTALLED" + ); + + // Install a langpack. + let langpack = { + "manifest.json": { + name: "test Language Pack", + version: "1.0", + manifest_version: 2, + browser_specific_settings: { + gecko: { + id: "@test-langpack", + strict_min_version: "42.0", + strict_max_version: "42.0", + }, + }, + langpack_id: "fr", + languages: { + fr: { + chrome_resources: { + global: "chrome/fr/locale/fr/global/", + }, + version: "20171001190118", + }, + }, + sources: { + browser: { + base_path: "browser/", + }, + }, + }, + }; + + await Promise.all([ + TestUtils.topicObserved("webextension-langpack-startup"), + AddonTestUtils.promiseInstallXPI(langpack), + ]); + + result = await reasons(); + Assert.ok( + result.includes(REASON.LANGPACK_INSTALLED), + "Reasons include LANGPACK_INSTALLED" + ); + + // Now turn off langpack updating. + Services.prefs.setBoolPref("app.update.langpack.enabled", false); + + result = await reasons(); + Assert.ok( + !result.includes(REASON.LANGPACK_INSTALLED), + "Reasons does not include LANGPACK_INSTALLED" + ); + } +); + +add_task( + { + skip_if: () => !AppConstants.MOZ_BACKGROUNDTASKS, + }, + async function test_reasons_schedule_default_profile() { + // It's difficult to arrange a default profile in a testing environment, so + // this is not as thorough as we'd like. + let result = await reasons(); + + Assert.ok(result.includes(REASON.NO_DEFAULT_PROFILE_EXISTS)); + Assert.ok(result.includes(REASON.NOT_DEFAULT_PROFILE)); + } +); diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_update.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_update.js new file mode 100644 index 0000000000..81b7086401 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_reason_update.js @@ -0,0 +1,199 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=4 ts=4 sts=4 et + * 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"; + +const { BackgroundUpdate } = ChromeUtils.import( + "resource://gre/modules/BackgroundUpdate.jsm" +); +let reasons = () => BackgroundUpdate._reasonsToNotUpdateInstallation(); +let REASON = BackgroundUpdate.REASON; +const { EnterprisePolicyTesting } = ChromeUtils.importESModule( + "resource://testing-common/EnterprisePolicyTesting.sys.mjs" +); +const { UpdateService } = ChromeUtils.import( + "resource://gre/modules/UpdateService.jsm" +); + +const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); + +// We can't reasonably check NO_MOZ_BACKGROUNDTASKS, nor NO_OMNIJAR. + +// These tests use per-installation prefs, and those are a shared resource, so +// they require some non-trivial setup. +setupTestCommon(null); +standardInit(); + +function setup_enterprise_policy_testing() { + // This initializes the policy engine for xpcshell tests + let policies = Cc["@mozilla.org/enterprisepolicies;1"].getService( + Ci.nsIObserver + ); + policies.observe(null, "policies-startup", null); +} +setup_enterprise_policy_testing(); + +async function setupPolicyEngineWithJson(json, customSchema) { + if (typeof json != "object") { + let filePath = do_get_file(json ? json : "non-existing-file.json").path; + return EnterprisePolicyTesting.setupPolicyEngineWithJson( + filePath, + customSchema + ); + } + return EnterprisePolicyTesting.setupPolicyEngineWithJson(json, customSchema); +} + +add_task(async function test_reasons_update_no_app_update_auto() { + let prev = await UpdateUtils.getAppUpdateAutoEnabled(); + try { + await UpdateUtils.setAppUpdateAutoEnabled(false); + let result = await reasons(); + Assert.ok(result.includes(REASON.NO_APP_UPDATE_AUTO)); + + await UpdateUtils.setAppUpdateAutoEnabled(true); + result = await reasons(); + Assert.ok(!result.includes(REASON.NO_APP_UPDATE_AUTO)); + } finally { + await UpdateUtils.setAppUpdateAutoEnabled(prev); + } +}); + +add_task(async function test_reasons_update_no_app_update_background_enabled() { + let prev = await UpdateUtils.readUpdateConfigSetting( + "app.update.background.enabled" + ); + try { + await UpdateUtils.writeUpdateConfigSetting( + "app.update.background.enabled", + false + ); + let result = await reasons(); + Assert.ok(result.includes(REASON.NO_APP_UPDATE_BACKGROUND_ENABLED)); + + await UpdateUtils.writeUpdateConfigSetting( + "app.update.background.enabled", + true + ); + result = await reasons(); + Assert.ok(!result.includes(REASON.NO_APP_UPDATE_BACKGROUND_ENABLED)); + } finally { + await UpdateUtils.writeUpdateConfigSetting( + "app.update.background.enabled", + prev + ); + } +}); + +add_task(async function test_reasons_update_cannot_usually_check() { + // It's difficult to arrange the conditions in a testing environment, so + // we'll use mocks to get a little assurance. + let result = await reasons(); + Assert.ok(!result.includes(REASON.CANNOT_USUALLY_CHECK)); + + let sandbox = sinon.createSandbox(); + try { + sandbox + .stub(UpdateService.prototype, "canUsuallyCheckForUpdates") + .get(() => false); + result = await reasons(); + Assert.ok(result.includes(REASON.CANNOT_USUALLY_CHECK)); + } finally { + sandbox.restore(); + } +}); + +add_task(async function test_reasons_update_can_usually_stage_or_appl() { + // It's difficult to arrange the conditions in a testing environment, so + // we'll use mocks to get a little assurance. + let sandbox = sinon.createSandbox(); + try { + sandbox + .stub(UpdateService.prototype, "canUsuallyStageUpdates") + .get(() => true); + sandbox + .stub(UpdateService.prototype, "canUsuallyApplyUpdates") + .get(() => true); + let result = await reasons(); + Assert.ok( + !result.includes(REASON.CANNOT_USUALLY_STAGE_AND_CANNOT_USUALLY_APPLY) + ); + + sandbox + .stub(UpdateService.prototype, "canUsuallyStageUpdates") + .get(() => false); + sandbox + .stub(UpdateService.prototype, "canUsuallyApplyUpdates") + .get(() => false); + result = await reasons(); + Assert.ok( + result.includes(REASON.CANNOT_USUALLY_STAGE_AND_CANNOT_USUALLY_APPLY) + ); + } finally { + sandbox.restore(); + } +}); + +add_task( + { + skip_if: () => + !AppConstants.MOZ_BITS_DOWNLOAD || AppConstants.platform != "win", + }, + async function test_reasons_update_can_usually_use_bits() { + let prev = Services.prefs.getBoolPref("app.update.BITS.enabled"); + + // Here we use mocks to "get by" preconditions that are not + // satisfied in the testing environment. + let sandbox = sinon.createSandbox(); + try { + sandbox + .stub(UpdateService.prototype, "canUsuallyStageUpdates") + .get(() => true); + sandbox + .stub(UpdateService.prototype, "canUsuallyApplyUpdates") + .get(() => true); + + Services.prefs.setBoolPref("app.update.BITS.enabled", false); + let result = await reasons(); + Assert.ok(result.includes(REASON.WINDOWS_CANNOT_USUALLY_USE_BITS)); + + Services.prefs.setBoolPref("app.update.BITS.enabled", true); + result = await reasons(); + Assert.ok(!result.includes(REASON.WINDOWS_CANNOT_USUALLY_USE_BITS)); + } finally { + sandbox.restore(); + Services.prefs.setBoolPref("app.update.BITS.enabled", prev); + } + } +); + +add_task(async function test_reasons_update_manual_update_only() { + await setupPolicyEngineWithJson({ + policies: { + ManualAppUpdateOnly: true, + }, + }); + Assert.equal( + Services.policies.status, + Ci.nsIEnterprisePolicies.ACTIVE, + "Engine is active" + ); + + let result = await reasons(); + Assert.ok(result.includes(REASON.MANUAL_UPDATE_ONLY)); + + await setupPolicyEngineWithJson({}); + + result = await reasons(); + Assert.ok(!result.includes(REASON.MANUAL_UPDATE_ONLY)); +}); + +add_task(() => { + // `setupTestCommon()` calls `do_test_pending()`; this calls + // `do_test_finish()`. The `add_task` schedules this to run after all the + // other tests have completed. + doTestFinish(); +}); diff --git a/toolkit/mozapps/update/tests/unit_background_update/xpcshell.ini b/toolkit/mozapps/update/tests/unit_background_update/xpcshell.ini new file mode 100644 index 0000000000..dff65608e9 --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_background_update/xpcshell.ini @@ -0,0 +1,23 @@ +# 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/. + +[DEFAULT] +firefox-appdir = browser +skip-if = + toolkit == 'android' + (os == 'win' && msix) # Our updater is disabled in MSIX builds +head = head.js +support-files = + ../data/shared.js + ../data/sharedUpdateXML.js + ../data/xpcshellUtilsAUS.js + +[test_backgroundupdate_exitcodes.js] +run-sequentially = very high failure rate in parallel +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure + +[test_backgroundupdate_glean.js] +[test_backgroundupdate_reason_update.js] +[test_backgroundupdate_reason_schedule.js] |