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.js450
1 files changed, 450 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 000000000..eca5ec289
--- /dev/null
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -0,0 +1,450 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/util.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+Svc.DefaultPrefs.set("registerEngines", "");
+Cu.import("resource://services-sync/service.js");
+
+var scheduler = Service.scheduler;
+var 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 = id => {};
+
+function promiseStopServer(server) {
+ let deferred = Promise.defer();
+ server.stop(deferred.resolve);
+ return deferred.promise;
+}
+
+function sync_httpd_setup() {
+ let global = new ServerWBO("global", {
+ syncID: Service.syncID,
+ storageVersion: STORAGE_VERSION,
+ engines: {clients: {version: clientsEngine.version,
+ syncID: clientsEngine.syncID}}
+ });
+ 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())
+ });
+}
+
+function* setUp(server) {
+ yield configureIdentity({username: "johndoe"});
+ Service.serverURL = server.baseURI + "/";
+ Service.clusterURL = server.baseURI + "/";
+ generateNewKeys(Service.collectionKeys);
+ let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
+ serverKeys.encrypt(Service.identity.syncKeyBundle);
+ serverKeys.upload(Service.resource(Service.cryptoKeysURL));
+}
+
+function run_test() {
+ initTestLogging("Trace");
+
+ Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
+ Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
+
+ run_next_test();
+}
+
+add_identity_test(this, function* test_successful_sync_adjustSyncInterval() {
+ _("Test successful sync calling adjustSyncInterval");
+ let syncSuccesses = 0;
+ function onSyncFinish() {
+ _("Sync success.");
+ syncSuccesses++;
+ };
+ Svc.Obs.add("weave:service:sync:finish", onSyncFinish);
+
+ let server = sync_httpd_setup();
+ yield setUp(server);
+
+ // Confirm defaults
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ do_check_false(scheduler.hasIncomingItems);
+
+ _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
+ // idle == true && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = true;
+ Service.sync();
+ do_check_eq(syncSuccesses, 1);
+ do_check_true(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ Service.sync();
+ do_check_eq(syncSuccesses, 2);
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ Service.sync();
+ do_check_eq(syncSuccesses, 3);
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == true && numClients <= 1 && hasIncomingItems == true
+ scheduler.idle = true;
+ Service.sync();
+ do_check_eq(syncSuccesses, 4);
+ do_check_true(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ _("Test as long as idle && numClients > 1 our sync interval is idleInterval.");
+ // idle == true && numClients > 1 && hasIncomingItems == true
+ Service.clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ Service.sync();
+ do_check_eq(syncSuccesses, 5);
+ do_check_true(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
+
+ // idle == true && numClients > 1 && hasIncomingItems == false
+ scheduler.hasIncomingItems = false;
+ Service.sync();
+ do_check_eq(syncSuccesses, 6);
+ do_check_true(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
+
+ _("Test non-idle, numClients > 1, no incoming items => activeInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ Service.sync();
+ do_check_eq(syncSuccesses, 7);
+ do_check_false(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+
+ _("Test non-idle, numClients > 1, incoming items => immediateInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ Service.sync();
+ do_check_eq(syncSuccesses, 8);
+ do_check_false(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems); //gets reset to false
+ do_check_eq(scheduler.syncInterval, scheduler.immediateInterval);
+
+ Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
+ Service.startOver();
+ yield promiseStopServer(server);
+});
+
+add_identity_test(this, function* test_unsuccessful_sync_adjustSyncInterval() {
+ _("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 = sync_httpd_setup();
+ yield setUp(server);
+
+ // Confirm defaults
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ do_check_false(scheduler.hasIncomingItems);
+
+ _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
+ // idle == true && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = true;
+ Service.sync();
+ do_check_eq(syncFailures, 1);
+ do_check_true(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ Service.sync();
+ do_check_eq(syncFailures, 2);
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == false && numClients <= 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ Service.sync();
+ do_check_eq(syncFailures, 3);
+ do_check_false(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ // idle == true && numClients <= 1 && hasIncomingItems == true
+ scheduler.idle = true;
+ Service.sync();
+ do_check_eq(syncFailures, 4);
+ do_check_true(scheduler.idle);
+ do_check_false(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ _("Test as long as idle && numClients > 1 our sync interval is idleInterval.");
+ // idle == true && numClients > 1 && hasIncomingItems == true
+ Service.clientsEngine._store.create({id: "foo", cleartext: "bar"});
+
+ Service.sync();
+ do_check_eq(syncFailures, 5);
+ do_check_true(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_true(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
+
+ // idle == true && numClients > 1 && hasIncomingItems == false
+ scheduler.hasIncomingItems = false;
+ Service.sync();
+ do_check_eq(syncFailures, 6);
+ do_check_true(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
+
+ _("Test non-idle, numClients > 1, no incoming items => activeInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == false
+ scheduler.idle = false;
+ Service.sync();
+ do_check_eq(syncFailures, 7);
+ do_check_false(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems);
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+
+ _("Test non-idle, numClients > 1, incoming items => immediateInterval.");
+ // idle == false && numClients > 1 && hasIncomingItems == true
+ scheduler.hasIncomingItems = true;
+ Service.sync();
+ do_check_eq(syncFailures, 8);
+ do_check_false(scheduler.idle);
+ do_check_true(scheduler.numClients > 1);
+ do_check_false(scheduler.hasIncomingItems); //gets reset to false
+ do_check_eq(scheduler.syncInterval, scheduler.immediateInterval);
+
+ Service.startOver();
+ Svc.Obs.remove("weave:service:sync:error", onSyncError);
+ yield promiseStopServer(server);
+});
+
+add_identity_test(this, function* test_back_triggers_sync() {
+ let server = sync_httpd_setup();
+ yield setUp(server);
+
+ // Single device: no sync triggered.
+ scheduler.idle = true;
+ scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
+ do_check_false(scheduler.idle);
+
+ // Multiple devices: sync is triggered.
+ clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ scheduler.updateClientMode();
+
+ let deferred = Promise.defer();
+ Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
+ Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
+
+ Service.recordManager.clearCache();
+ Svc.Prefs.resetBranch("");
+ scheduler.setDefaults();
+ clientsEngine.resetClient();
+
+ Service.startOver();
+ server.stop(deferred.resolve);
+ });
+
+ scheduler.idle = true;
+ scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
+ do_check_false(scheduler.idle);
+ yield deferred.promise;
+});
+
+add_identity_test(this, function* test_adjust_interval_on_sync_error() {
+ let server = sync_httpd_setup();
+ yield 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");
+
+ do_check_eq(syncFailures, 0);
+ do_check_false(scheduler.numClients > 1);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ Service.sync();
+
+ do_check_eq(syncFailures, 1);
+ do_check_true(scheduler.numClients > 1);
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+
+ Svc.Obs.remove("weave:service:sync:error", onSyncError);
+ Service.startOver();
+ yield promiseStopServer(server);
+});
+
+add_identity_test(this, function* test_bug671378_scenario() {
+ // 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 = sync_httpd_setup();
+ yield 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.
+ Service.sync();
+ do_check_eq(syncSuccesses, 1);
+ do_check_false(scheduler.numClients > 1);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+
+ let deferred = Promise.defer();
+ // 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) {
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+ do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ scheduler.scheduleNextSync = scheduler._scheduleNextSync;
+ Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
+ Service.startOver();
+ server.stop(deferred.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.
+ Utils.nextTick(function() {
+ Svc.Obs.remove("weave:service:sync:start", onSyncStart);
+
+ scheduler.scheduleNextSync();
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+ do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+ });
+ });
+
+ clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ Service.sync();
+ yield deferred.promise;
+});
+
+add_test(function test_adjust_timer_larger_syncInterval() {
+ _("Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used.");
+ clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ scheduler.updateClientMode();
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure we have a small interval.
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_eq(scheduler.syncTimer.delay, scheduler.activeInterval);
+
+ // Make interval large again
+ clientsEngine._wipeClient();
+ scheduler.updateClientMode();
+ do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure timer delay remains as the small interval.
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ //SyncSchedule.
+ Service.startOver();
+ run_next_test();
+});
+
+add_test(function test_adjust_timer_smaller_syncInterval() {
+ _("Test current timout > syncInterval period && nextSync != 0, syncInterval is used.");
+ scheduler.scheduleNextSync();
+
+ // Ensure we have a large interval.
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
+
+ // Make interval smaller
+ clientsEngine._store.create({id: "foo", cleartext: "bar"});
+ scheduler.updateClientMode();
+ do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
+
+ scheduler.scheduleNextSync();
+
+ // Ensure smaller timer delay is used.
+ do_check_neq(scheduler.nextSync, 0);
+ do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
+
+ //SyncSchedule.
+ Service.startOver();
+ run_next_test();
+});