/* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const SCHEDULED_BACKUPS_ENABLED_PREF_NAME = "browser.backup.scheduled.enabled"; const IDLE_THRESHOLD_SECONDS_PREF_NAME = "browser.backup.scheduled.idle-threshold-seconds"; const LAST_BACKUP_TIMESTAMP_PREF_NAME = "browser.backup.scheduled.last-backup-timestamp"; const MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME = "browser.backup.scheduled.minimum-time-between-backups-seconds"; /** * This is a very thin nsIUserIdleService implementation that doesn't do much, * but with sinon we can stub out some parts of it to make sure that the * BackupService uses it in the way we expect. */ let idleService = { QueryInterface: ChromeUtils.generateQI(["nsIUserIdleService"]), idleTime: 19999, disabled: true, addIdleObserver() {}, removeIdleObserver() {}, }; add_setup(() => { let fakeIdleServiceCID = MockRegistrar.register( "@mozilla.org/widget/useridleservice;1", idleService ); Services.prefs.setBoolPref(SCHEDULED_BACKUPS_ENABLED_PREF_NAME, true); // We'll pretend that our threshold between backups is 20 seconds. Services.prefs.setIntPref(MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME, 20); registerCleanupFunction(() => { MockRegistrar.unregister(fakeIdleServiceCID); Services.prefs.clearUserPref(SCHEDULED_BACKUPS_ENABLED_PREF_NAME); Services.prefs.clearUserPref( MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME ); }); }); /** * Tests that calling initBackupScheduler registers a callback with the * nsIUserIdleService. */ add_task(async function test_init_uninitBackupScheduler() { let bs = new BackupService(); let sandbox = sinon.createSandbox(); sandbox.stub(idleService, "addIdleObserver"); sandbox.stub(idleService, "removeIdleObserver"); await bs.initBackupScheduler(); Assert.ok( idleService.addIdleObserver.calledOnce, "addIdleObserver was called" ); Assert.ok( idleService.addIdleObserver.firstCall.args[0] instanceof Ci.nsIObserver, "The first argument to addIdleObserver was an nsIObserver" ); const THRESHOLD_SECONDS = Services.prefs.getIntPref( IDLE_THRESHOLD_SECONDS_PREF_NAME ); Assert.equal( idleService.addIdleObserver.firstCall.args[1], THRESHOLD_SECONDS, "The idle threshold preference value was passed as the second argument." ); Assert.ok( idleService.removeIdleObserver.notCalled, "removeIdleObserver has not been called yet." ); // Hold a reference to what addIdleObserver was called with as its first // argument, so we can compare it against what's passed to removeIdleObserver. let addObserverArg = idleService.addIdleObserver.firstCall.args[0]; // We want to make sure that uninitBackupScheduler doesn't call this again, // so reset its call history. idleService.addIdleObserver.resetHistory(); // Now, let's pretend that the preference for the idle threshold changed // before we could uninit the backup scheduler. We should ensure that this // change is _not_ reflected whenever deregistration of the idle callback // occurs, since it wouldn't match the registration arguments. Services.prefs.setIntPref( IDLE_THRESHOLD_SECONDS_PREF_NAME, THRESHOLD_SECONDS + 5 ); bs.uninitBackupScheduler(); Assert.ok( idleService.addIdleObserver.notCalled, "addIdleObserver was not called again." ); Assert.ok( idleService.removeIdleObserver.calledOnce, "removeIdleObserver was called once." ); Assert.ok( idleService.removeIdleObserver.firstCall.args[0] instanceof Ci.nsIObserver, "The first argument to addIdleObserver was an nsIObserver" ); Assert.equal( idleService.removeIdleObserver.firstCall.args[0], addObserverArg, "The first argument to addIdleObserver matches the first argument to removeIdleObserver" ); Assert.equal( idleService.removeIdleObserver.firstCall.args[1], THRESHOLD_SECONDS, "The original idle threshold preference value was passed as the second argument." ); sandbox.restore(); Services.prefs.clearUserPref(IDLE_THRESHOLD_SECONDS_PREF_NAME); }); /** * Tests that calling BackupService.onObserve with the "idle" notification * causes the BackupService.onIdle method to be called. */ add_task(async function test_BackupService_onObserve_idle() { let bs = new BackupService(); let sandbox = sinon.createSandbox(); sandbox.stub(bs, "onIdle"); // The subject for the idle notification is always the idle service itself. bs.onObserve(idleService, "idle"); Assert.ok(bs.onIdle.calledOnce, "BackupService.onIdle was called."); sandbox.restore(); }); /** * Tests that calling BackupService.onObserve with the * "quit-application-granted" notification causes the * BackupService.uninitBackupScheduler method to be called. */ add_task( async function test_BackupService_onObserve_quit_application_granted() { let bs = new BackupService(); let sandbox = sinon.createSandbox(); sandbox.stub(bs, "uninitBackupScheduler"); // The subject for the quit-application-granted notification is null. bs.onObserve(null, "quit-application-granted"); Assert.ok( bs.uninitBackupScheduler.calledOnce, "BackupService.uninitBackupScheduler was called." ); sandbox.restore(); } ); /** * Tests that calling onIdle when a backup has never occurred causes a backup to * get scheduled. */ add_task(async function test_BackupService_idle_no_backup_exists() { // Make sure no last backup timestamp is recorded. Services.prefs.clearUserPref(LAST_BACKUP_TIMESTAMP_PREF_NAME); let bs = new BackupService(); let sandbox = sinon.createSandbox(); sandbox.stub(bs, "createBackupOnIdleDispatch"); bs.initBackupScheduler(); Assert.equal( bs.state.lastBackupDate, null, "State should have null for lastBackupDate" ); bs.onIdle(); Assert.ok( bs.createBackupOnIdleDispatch.calledOnce, "BackupService.createBackupOnIdleDispatch was called." ); sandbox.restore(); }); /** * Tests that calling onIdle when a backup has occurred recently does not cause * a backup to get scheduled. */ add_task(async function test_BackupService_idle_not_expired_backup() { // Let's calculate a Date that's five seconds ago. let fiveSecondsAgo = Date.now() - 5000; /* 5 seconds in milliseconds */ let lastBackupPrefValue = Math.floor(fiveSecondsAgo / 1000); Services.prefs.setIntPref( LAST_BACKUP_TIMESTAMP_PREF_NAME, lastBackupPrefValue ); let bs = new BackupService(); let sandbox = sinon.createSandbox(); bs.initBackupScheduler(); Assert.equal( bs.state.lastBackupDate, lastBackupPrefValue, "State should have cached lastBackupDate" ); sandbox.stub(bs, "createBackupOnIdleDispatch"); bs.onIdle(); Assert.ok( bs.createBackupOnIdleDispatch.notCalled, "BackupService.createBackupOnIdleDispatch was not called." ); sandbox.restore(); }); /** * Tests that calling onIdle when a backup has occurred, but after the threshold * does cause a backup to get scheduled */ add_task(async function test_BackupService_idle_expired_backup() { // Let's calculate a Date that's twenty five seconds ago. let twentyFiveSecondsAgo = Date.now() - 25000; /* 25 seconds in milliseconds */ let lastBackupPrefValue = Math.floor(twentyFiveSecondsAgo / 1000); Services.prefs.setIntPref( LAST_BACKUP_TIMESTAMP_PREF_NAME, lastBackupPrefValue ); let bs = new BackupService(); let sandbox = sinon.createSandbox(); bs.initBackupScheduler(); Assert.equal( bs.state.lastBackupDate, lastBackupPrefValue, "State should have cached lastBackupDate" ); sandbox.stub(bs, "createBackupOnIdleDispatch"); bs.onIdle(); Assert.ok( bs.createBackupOnIdleDispatch.calledOnce, "BackupService.createBackupOnIdleDispatch was called." ); sandbox.restore(); }); /** * Tests that calling onIdle when a backup occurred in the future somehow causes * a backup to get scheduled. */ add_task(async function test_BackupService_idle_time_travel() { // Let's calculate a Date that's twenty-five seconds in the future. let twentyFiveSecondsFromNow = Date.now() + 25000; /* 25 seconds in milliseconds */ let lastBackupPrefValue = Math.floor(twentyFiveSecondsFromNow / 1000); Services.prefs.setIntPref( LAST_BACKUP_TIMESTAMP_PREF_NAME, lastBackupPrefValue ); let bs = new BackupService(); let sandbox = sinon.createSandbox(); bs.initBackupScheduler(); Assert.equal( bs.state.lastBackupDate, lastBackupPrefValue, "State should have cached lastBackupDate" ); sandbox.stub(bs, "createBackupOnIdleDispatch"); bs.onIdle(); Assert.ok( bs.createBackupOnIdleDispatch.calledOnce, "BackupService.createBackupOnIdleDispatch was called." ); Assert.equal( bs.state.lastBackupDate, null, "Should have cleared the last backup date." ); sandbox.restore(); });