summaryrefslogtreecommitdiffstats
path: root/services/sync/tests/unit/test_interval_triggers.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/tests/unit/test_interval_triggers.js')
-rw-r--r--services/sync/tests/unit/test_interval_triggers.js462
1 files changed, 462 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_interval_triggers.js b/services/sync/tests/unit/test_interval_triggers.js
new file mode 100644
index 0000000000..09d8987e1a
--- /dev/null
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -0,0 +1,462 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Svc.Prefs.set("registerEngines", "");
+const { Service } = ChromeUtils.importESModule(
+ "resource://services-sync/service.sys.mjs"
+);
+
+let scheduler;
+let clientsEngine;
+
+async function sync_httpd_setup() {
+ let clientsSyncID = await clientsEngine.resetLocalSyncID();
+ let global = new ServerWBO("global", {
+ syncID: Service.syncID,
+ storageVersion: STORAGE_VERSION,
+ engines: {
+ clients: { version: clientsEngine.version, syncID: clientsSyncID },
+ },
+ });
+ let clientsColl = new ServerCollection({}, true);
+
+ // Tracking info/collections.
+ let collectionsHelper = track_collections_helper();
+ let upd = collectionsHelper.with_updated_collection;
+
+ return httpd_setup({
+ "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
+ "/1.1/johndoe/info/collections": collectionsHelper.handler,
+ "/1.1/johndoe/storage/crypto/keys": upd(
+ "crypto",
+ new ServerWBO("keys").handler()
+ ),
+ "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
+ });
+}
+
+async function setUp(server) {
+ syncTestLogging();
+ await configureIdentity({ username: "johndoe" }, server);
+ await generateNewKeys(Service.collectionKeys);
+ let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
+ await serverKeys.encrypt(Service.identity.syncKeyBundle);
+ await serverKeys.upload(Service.resource(Service.cryptoKeysURL));
+}
+
+add_task(async function setup() {
+ scheduler = Service.scheduler;
+ clientsEngine = Service.clientsEngine;
+
+ // Don't remove stale clients when syncing. This is a test-only workaround
+ // that lets us add clients directly to the store, without losing them on
+ // the next sync.
+ clientsEngine._removeRemoteClient = async id => {};
+});
+
+add_task(async function test_successful_sync_adjustSyncInterval() {
+ enableValidationPrefs();
+
+ _("Test successful sync calling adjustSyncInterval");
+ let syncSuccesses = 0;
+ function onSyncFinish() {
+ _("Sync success.");
+ syncSuccesses++;
+ }
+ Svc.Obs.add("weave:service:sync:finish", onSyncFinish);
+
+ let server = await sync_httpd_setup();
+ await setUp(server);
+
+ // Confirm defaults
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ Assert.ok(!scheduler.hasIncomingItems);
+
+ _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
+ // idle == true && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = true;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 1);
+ Assert.ok(scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 2);
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 3);
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == true && numClients <= 1 && hasIncomingItems == true
+ scheduler.idle = true;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 4);
+ Assert.ok(scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ _(
+ "Test as long as idle && numClients > 1 our sync interval is idleInterval."
+ );
+ // idle == true && numClients > 1 && hasIncomingItems == true
+ await Service.clientsEngine._store.create({
+ id: "foo",
+ cleartext: { name: "bar", type: "mobile" },
+ });
+ await Service.sync();
+ Assert.equal(syncSuccesses, 5);
+ Assert.ok(scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.idleInterval);
+
+ // idle == true && numClients > 1 && hasIncomingItems == false
+ scheduler.hasIncomingItems = false;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 6);
+ Assert.ok(scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.idleInterval);
+
+ _("Test non-idle, numClients > 1, no incoming items => activeInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 7);
+ Assert.ok(!scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+
+ _("Test non-idle, numClients > 1, incoming items => immediateInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ await Service.sync();
+ Assert.equal(syncSuccesses, 8);
+ Assert.ok(!scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems); // gets reset to false
+ Assert.equal(scheduler.syncInterval, scheduler.immediateInterval);
+
+ Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
+ await Service.startOver();
+ await promiseStopServer(server);
+});
+
+add_task(async function test_unsuccessful_sync_adjustSyncInterval() {
+ enableValidationPrefs();
+
+ _("Test unsuccessful sync calling adjustSyncInterval");
+
+ let syncFailures = 0;
+ function onSyncError() {
+ _("Sync error.");
+ syncFailures++;
+ }
+ Svc.Obs.add("weave:service:sync:error", onSyncError);
+
+ _("Test unsuccessful sync calls adjustSyncInterval");
+ // Force sync to fail.
+ Svc.Prefs.set("firstSync", "notReady");
+
+ let server = await sync_httpd_setup();
+ await setUp(server);
+
+ // Confirm defaults
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ Assert.ok(!scheduler.hasIncomingItems);
+
+ _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
+ // idle == true && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = true;
+ await Service.sync();
+ Assert.equal(syncFailures, 1);
+ Assert.ok(scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ await Service.sync();
+ Assert.equal(syncFailures, 2);
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ await Service.sync();
+ Assert.equal(syncFailures, 3);
+ Assert.ok(!scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == true && numClients <= 1 && hasIncomingItems == true
+ scheduler.idle = true;
+ await Service.sync();
+ Assert.equal(syncFailures, 4);
+ Assert.ok(scheduler.idle);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ _(
+ "Test as long as idle && numClients > 1 our sync interval is idleInterval."
+ );
+ // idle == true && numClients > 1 && hasIncomingItems == true
+ Svc.Prefs.set("clients.devices.mobile", 2);
+ scheduler.updateClientMode();
+
+ await Service.sync();
+ Assert.equal(syncFailures, 5);
+ Assert.ok(scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.idleInterval);
+
+ // idle == true && numClients > 1 && hasIncomingItems == false
+ scheduler.hasIncomingItems = false;
+ await Service.sync();
+ Assert.equal(syncFailures, 6);
+ Assert.ok(scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.idleInterval);
+
+ _("Test non-idle, numClients > 1, no incoming items => activeInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ await Service.sync();
+ Assert.equal(syncFailures, 7);
+ Assert.ok(!scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems);
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+
+ _("Test non-idle, numClients > 1, incoming items => immediateInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ await Service.sync();
+ Assert.equal(syncFailures, 8);
+ Assert.ok(!scheduler.idle);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.ok(!scheduler.hasIncomingItems); // gets reset to false
+ Assert.equal(scheduler.syncInterval, scheduler.immediateInterval);
+
+ await Service.startOver();
+ Svc.Obs.remove("weave:service:sync:error", onSyncError);
+ await promiseStopServer(server);
+});
+
+add_task(async function test_back_triggers_sync() {
+ enableValidationPrefs();
+
+ let server = await sync_httpd_setup();
+ await setUp(server);
+
+ // Single device: no sync triggered.
+ scheduler.idle = true;
+ scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
+ Assert.ok(!scheduler.idle);
+
+ // Multiple devices: sync is triggered.
+ Svc.Prefs.set("clients.devices.mobile", 2);
+ scheduler.updateClientMode();
+
+ let promiseDone = promiseOneObserver("weave:service:sync:finish");
+
+ scheduler.idle = true;
+ scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
+ Assert.ok(!scheduler.idle);
+ await promiseDone;
+
+ Service.recordManager.clearCache();
+ Svc.Prefs.resetBranch("");
+ scheduler.setDefaults();
+ await clientsEngine.resetClient();
+
+ await Service.startOver();
+ await promiseStopServer(server);
+});
+
+add_task(async function test_adjust_interval_on_sync_error() {
+ enableValidationPrefs();
+
+ let server = await sync_httpd_setup();
+ await setUp(server);
+
+ let syncFailures = 0;
+ function onSyncError() {
+ _("Sync error.");
+ syncFailures++;
+ }
+ Svc.Obs.add("weave:service:sync:error", onSyncError);
+
+ _("Test unsuccessful sync updates client mode & sync intervals");
+ // Force a sync fail.
+ Svc.Prefs.set("firstSync", "notReady");
+
+ Assert.equal(syncFailures, 0);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ Svc.Prefs.set("clients.devices.mobile", 2);
+ await Service.sync();
+
+ Assert.equal(syncFailures, 1);
+ Assert.ok(scheduler.numClients > 1);
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+
+ Svc.Obs.remove("weave:service:sync:error", onSyncError);
+ await Service.startOver();
+ await promiseStopServer(server);
+});
+
+add_task(async function test_bug671378_scenario() {
+ enableValidationPrefs();
+
+ // Test scenario similar to bug 671378. This bug appeared when a score
+ // update occurred that wasn't large enough to trigger a sync so
+ // scheduleNextSync() was called without a time interval parameter,
+ // setting nextSync to a non-zero value and preventing the timer from
+ // being adjusted in the next call to scheduleNextSync().
+ let server = await sync_httpd_setup();
+ await setUp(server);
+
+ let syncSuccesses = 0;
+ function onSyncFinish() {
+ _("Sync success.");
+ syncSuccesses++;
+ }
+ Svc.Obs.add("weave:service:sync:finish", onSyncFinish);
+
+ // After first sync call, syncInterval & syncTimer are singleDeviceInterval.
+ await Service.sync();
+ Assert.equal(syncSuccesses, 1);
+ Assert.equal(false, scheduler.numClients > 1);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+
+ let promiseDone = new Promise(resolve => {
+ // Wrap scheduleNextSync so we are notified when it is finished.
+ scheduler._scheduleNextSync = scheduler.scheduleNextSync;
+ scheduler.scheduleNextSync = function () {
+ scheduler._scheduleNextSync();
+
+ // Check on sync:finish scheduleNextSync sets the appropriate
+ // syncInterval and syncTimer values.
+ if (syncSuccesses == 2) {
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+ Assert.ok(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ scheduler.scheduleNextSync = scheduler._scheduleNextSync;
+ Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
+ Service.startOver().then(() => {
+ server.stop(resolve);
+ });
+ }
+ };
+ });
+
+ // Set nextSync != 0
+ // syncInterval still hasn't been set by call to updateClientMode.
+ // Explicitly trying to invoke scheduleNextSync during a sync
+ // (to immitate a score update that isn't big enough to trigger a sync).
+ Svc.Obs.add("weave:service:sync:start", function onSyncStart() {
+ // Wait for other sync:start observers to be called so that
+ // nextSync is set to 0.
+ CommonUtils.nextTick(function () {
+ Svc.Obs.remove("weave:service:sync:start", onSyncStart);
+
+ scheduler.scheduleNextSync();
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+ });
+ });
+
+ await Service.clientsEngine._store.create({
+ id: "foo",
+ cleartext: { name: "bar", type: "mobile" },
+ });
+ await Service.sync();
+ await promiseDone;
+});
+
+add_task(async function test_adjust_timer_larger_syncInterval() {
+ _(
+ "Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used."
+ );
+ Svc.Prefs.set("clients.devices.mobile", 2);
+ scheduler.updateClientMode();
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure we have a small interval.
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.equal(scheduler.syncTimer.delay, scheduler.activeInterval);
+
+ // Make interval large again
+ await clientsEngine._wipeClient();
+ Svc.Prefs.reset("clients.devices.mobile");
+ scheduler.updateClientMode();
+ Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure timer delay remains as the small interval.
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.ok(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ // SyncSchedule.
+ await Service.startOver();
+});
+
+add_task(async function test_adjust_timer_smaller_syncInterval() {
+ _(
+ "Test current timout > syncInterval period && nextSync != 0, syncInterval is used."
+ );
+ scheduler.scheduleNextSync();
+
+ // Ensure we have a large interval.
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+
+ // Make interval smaller
+ Svc.Prefs.set("clients.devices.mobile", 2);
+ scheduler.updateClientMode();
+ Assert.equal(scheduler.syncInterval, scheduler.activeInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure smaller timer delay is used.
+ Assert.notEqual(scheduler.nextSync, 0);
+ Assert.ok(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ // SyncSchedule.
+ await Service.startOver();
+});