Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
ChromeUtils.defineESModuleGetters(this, {
setTimeout: "resource://gre/modules/Timer.sys.mjs",
BackupError: "resource:///modules/backup/BackupError.mjs",
ERRORS: "chrome://browser/content/backup/backup-constants.mjs",
AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
});
const BACKUP_RETRY_LIMIT_PREF_NAME = "browser.backup.backup-retry-limit";
const DISABLED_ON_IDLE_RETRY_PREF_NAME =
"browser.backup.disabled-on-idle-backup-retry";
const BACKUP_ERROR_CODE_PREF_NAME = "browser.backup.errorCode";
const MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME =
"browser.backup.scheduled.minimum-time-between-backups-seconds";
const SCHEDULED_BACKUPS_ENABLED_PREF_NAME = "browser.backup.scheduled.enabled";
const BACKUP_DEBUG_INFO_PREF_NAME = "browser.backup.backup-debug-info";
const BACKUP_DEFAULT_LOCATION_PREF_NAME = "browser.backup.location";
function bsInProgressStateUpdate(bs, isBackupInProgress) {
return new Promise(resolve => {
bs.addEventListener(
"BackupService:StateUpdate",
() => {
if (bs.state.backupInProgress == isBackupInProgress) {
resolve();
} else {
Assert.ok(false, "Failure in waiting for state updates");
}
},
{ once: true }
);
});
}
add_setup(async () => {
const TEST_PROFILE_PATH = await IOUtils.createUniqueDirectory(
PathUtils.tempDir,
"testBackup"
);
Services.prefs.setStringPref(
BACKUP_DEFAULT_LOCATION_PREF_NAME,
TEST_PROFILE_PATH
);
Services.prefs.setBoolPref(SCHEDULED_BACKUPS_ENABLED_PREF_NAME, true);
Services.prefs.setIntPref(BACKUP_RETRY_LIMIT_PREF_NAME, 2);
Services.prefs.setBoolPref(DISABLED_ON_IDLE_RETRY_PREF_NAME, false);
// Much of this setup is copied from toolkit/profile/xpcshell/head.js. It is
// needed in order to put the xpcshell test environment into the state where
// it thinks its profile is the one pointed at by
// nsIToolkitProfileService.currentProfile.
let gProfD = do_get_profile();
let gDataHome = gProfD.clone();
gDataHome.append("data");
gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
let gDataHomeLocal = gProfD.clone();
gDataHomeLocal.append("local");
gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"].getService(
Ci.nsIXREDirProvider
);
xreDirProvider.setUserDataDirectory(gDataHome, false);
xreDirProvider.setUserDataDirectory(gDataHomeLocal, true);
let profileSvc = Cc["@mozilla.org/toolkit/profile-service;1"].getService(
Ci.nsIToolkitProfileService
);
let createdProfile = {};
let didCreate = profileSvc.selectStartupProfile(
["xpcshell"],
false,
AppConstants.UPDATE_CHANNEL,
"",
{},
{},
createdProfile
);
Assert.ok(didCreate, "Created a testing profile and set it to current.");
Assert.equal(
profileSvc.currentProfile,
createdProfile.value,
"Profile set to current"
);
registerCleanupFunction(async () => {
Services.prefs.clearUserPref(BACKUP_DEFAULT_LOCATION_PREF_NAME);
Services.prefs.clearUserPref(SCHEDULED_BACKUPS_ENABLED_PREF_NAME);
Services.prefs.clearUserPref(BACKUP_RETRY_LIMIT_PREF_NAME);
Services.prefs.clearUserPref(DISABLED_ON_IDLE_RETRY_PREF_NAME);
await IOUtils.remove(TEST_PROFILE_PATH, { recursive: true });
});
});
add_task(async function test_retry_limit() {
let bs = new BackupService();
let sandbox = sinon.createSandbox();
// Make createBackup fail intentionally
const createBackupFailureStub = sandbox
.stub(bs, "resolveArchiveDestFolderPath")
.rejects(new BackupError("forced failure", ERRORS.UNKNOWN));
// stub out idleDispatch
sandbox.stub(ChromeUtils, "idleDispatch").callsFake(callback => callback());
sandbox.spy(bs, "createBackup");
const n = Services.prefs.getIntPref(BACKUP_RETRY_LIMIT_PREF_NAME);
// now that we have an idle service, let's call create backup RETRY_LIMIT times
for (let i = 0; i <= n; i++) {
// ensure that there is no error code set
Services.prefs.setIntPref(BACKUP_ERROR_CODE_PREF_NAME, ERRORS.NONE);
bs.createBackupOnIdleDispatch();
// #backupInProgress is set to true
await bsInProgressStateUpdate(bs, true);
// #backupInProgress is set to false
await bsInProgressStateUpdate(bs, false);
// wait a tick for the prefs to update
await new Promise(executeSoon);
Assert.equal(
bs.createBackup.callCount,
i + 1,
"createBackup was called on idle"
);
Assert.equal(
Services.prefs.getIntPref(BACKUP_ERROR_CODE_PREF_NAME),
ERRORS.UNKNOWN,
"Error code has been set"
);
}
// check if it switched to no longer creating backups on idle
const previousCalls = bs.createBackup.callCount;
bs.createBackupOnIdleDispatch();
// wait a tick for the pref to update
await new Promise(executeSoon);
Assert.equal(
bs.createBackup.callCount,
previousCalls,
"createBackup was not called again after hitting the retry limit"
);
Assert.ok(
Services.prefs.getBoolPref(DISABLED_ON_IDLE_RETRY_PREF_NAME),
"Disable on idle has been enabled"
);
Services.prefs.setIntPref(MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME, 0);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(
MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME
);
});
// add a buffer between lastAttempt and now
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 10));
bs.createBackupOnIdleDispatch();
// #backupInProgress is set to true
await bsInProgressStateUpdate(bs, true);
Assert.equal(
bs.createBackup.callCount,
previousCalls + 1,
"createBackup was called again"
);
// #backupInProgress is set to false
await bsInProgressStateUpdate(bs, false);
// let's restore the failing function in createBackup
createBackupFailureStub.restore();
// Now, on idleDispatch, we don't expect any failures
// add a buffer between lastAttempt and now
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 10));
let testProfilePath = await IOUtils.createUniqueDirectory(
PathUtils.tempDir,
"testBackup_profile"
);
// calling createBackup directly to avoid race conditions with reliably
// awaiting createBackup to finish from a call to idleDispatch. This case covers
// the use case of reseting the prefs if createBackup is called and does not fail.
await bs.createBackup({
profilePath: testProfilePath,
});
// the error states should have reset
Assert.ok(
!Services.prefs.getBoolPref(DISABLED_ON_IDLE_RETRY_PREF_NAME),
"Retry on idle is enabled now"
);
Assert.equal(
Services.prefs.getIntPref(BACKUP_ERROR_CODE_PREF_NAME),
ERRORS.NONE,
"The error code is reset to NONE"
);
Assert.ok(
!Services.prefs.getStringPref(BACKUP_DEBUG_INFO_PREF_NAME, null),
"Error debug info has been cleared"
);
// check if changing the time since last backup calls createBackup
sandbox.restore();
});