/* -*- 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.importESModule( "resource://gre/modules/BackgroundUpdate.sys.mjs" ); let reasons = () => BackgroundUpdate._reasonsToNotUpdateInstallation(); let REASON = BackgroundUpdate.REASON; const { EnterprisePolicyTesting } = ChromeUtils.importESModule( "resource://testing-common/EnterprisePolicyTesting.sys.mjs" ); const { UpdateService } = ChromeUtils.importESModule( "resource://gre/modules/UpdateService.sys.mjs" ); const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); // 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_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_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)); result = await checkGleanPing(); 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)); result = await checkGleanPing(); 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)); result = await checkGleanPing(); 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)); result = await checkGleanPing(); 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)); result = await checkGleanPing(); 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) ); result = await checkGleanPing(); 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) ); result = await checkGleanPing(); 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)); result = await checkGleanPing(); Assert.ok( result.includes(REASON.WINDOWS_CANNOT_USUALLY_USE_BITS), "result : " + result.join("', '") + "']" ); Services.prefs.setBoolPref("app.update.BITS.enabled", true); result = await reasons(); Assert.ok(!result.includes(REASON.WINDOWS_CANNOT_USUALLY_USE_BITS)); result = await checkGleanPing(); 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)); result = await checkGleanPing(); Assert.ok(result.includes(REASON.MANUAL_UPDATE_ONLY)); await setupPolicyEngineWithJson({}); result = await reasons(); Assert.ok(!result.includes(REASON.MANUAL_UPDATE_ONLY)); result = await checkGleanPing(); Assert.ok(!result.includes(REASON.MANUAL_UPDATE_ONLY)); }); add_task( { skip_if: () => AppConstants.platform != "win", }, async function test_unelevated_nimbus_enabled() { // Enable feature. Services.prefs.setBoolPref( "app.update.background.allowUpdatesForUnelevatedInstallations", true ); registerCleanupFunction(() => { Services.prefs.clearUserPref( "app.update.background.allowUpdatesForUnelevatedInstallations" ); }); // execute! let r = await reasons(); Assert.ok( !r.includes(BackgroundUpdate.REASON.SERVICE_REGISTRY_KEY_MISSING), `no SERVICE_REGISTRY_KEY_MISSING in ${JSON.stringify(r)}` ); Assert.ok( !r.includes(BackgroundUpdate.REASON.APPBASEDIR_NOT_WRITABLE), `no APPBASEDIR_NOT_WRITABLE in ${JSON.stringify(r)}` ); // the test directory usually is writable, but we now create a file and keep // it open, so that the test file can neither be deleted nor recreated and // appears to be locked. With that we re-execute the test and expect to find // APPBASEDIR_NOT_WRITABLE in the telemetry data. let appDirTestFile = Services.dirsvc.get( XRE_EXECUTABLE_FILE, Ci.nsIFile ).parent; appDirTestFile.append(FILE_UPDATE_TEST); appDirTestFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); var outputStream = Cc[ "@mozilla.org/network/file-output-stream;1" ].createInstance(Ci.nsIFileOutputStream); // WR_ONLY|CREATE|TRUNC outputStream.init(appDirTestFile, 0x02 | 0x08 | 0x20, 0o644, null); registerCleanupFunction(() => { outputStream.close(); appDirTestFile.remove(false); }); // after the preperation: execute again! r = await reasons(); Assert.ok( r.includes(BackgroundUpdate.REASON.APPBASEDIR_NOT_WRITABLE), `no APPBASEDIR_NOT_WRITABLE in ${JSON.stringify(r)}` ); } ); add_task( { skip_if: () => AppConstants.platform != "win", }, async function test_unelevated_nimbus_disabled() { // Disable feature. Services.prefs.setBoolPref( "app.update.background.allowUpdatesForUnelevatedInstallations", false ); registerCleanupFunction(() => { Services.prefs.clearUserPref( "app.update.background.allowUpdatesForUnelevatedInstallations" ); }); let r = await reasons(); Assert.ok( r.includes(BackgroundUpdate.REASON.SERVICE_REGISTRY_KEY_MISSING), `SERVICE_REGISTRY_KEY_MISSING in ${JSON.stringify(r)}` ); } ); 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(); });