michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-sync/engines.js"); michael@0: Cu.import("resource://services-sync/engines/clients.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: Svc.DefaultPrefs.set("registerEngines", ""); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: michael@0: let scheduler = Service.scheduler; michael@0: let clientsEngine = Service.clientsEngine; michael@0: michael@0: function promiseStopServer(server) { michael@0: let deferred = Promise.defer(); michael@0: server.stop(deferred.resolve); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function sync_httpd_setup() { michael@0: let global = new ServerWBO("global", { michael@0: syncID: Service.syncID, michael@0: storageVersion: STORAGE_VERSION, michael@0: engines: {clients: {version: clientsEngine.version, michael@0: syncID: clientsEngine.syncID}} michael@0: }); michael@0: let clientsColl = new ServerCollection({}, true); michael@0: michael@0: // Tracking info/collections. michael@0: let collectionsHelper = track_collections_helper(); michael@0: let upd = collectionsHelper.with_updated_collection; michael@0: michael@0: return httpd_setup({ michael@0: "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()), michael@0: "/1.1/johndoe/info/collections": collectionsHelper.handler, michael@0: "/1.1/johndoe/storage/crypto/keys": michael@0: upd("crypto", (new ServerWBO("keys")).handler()), michael@0: "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()) michael@0: }); michael@0: } michael@0: michael@0: function setUp(server) { michael@0: yield configureIdentity({username: "johndoe"}); michael@0: Service.serverURL = server.baseURI + "/"; michael@0: Service.clusterURL = server.baseURI + "/"; michael@0: generateNewKeys(Service.collectionKeys); michael@0: let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); michael@0: serverKeys.encrypt(Service.identity.syncKeyBundle); michael@0: serverKeys.upload(Service.resource(Service.cryptoKeysURL)); michael@0: } michael@0: michael@0: function run_test() { michael@0: initTestLogging("Trace"); michael@0: michael@0: Log.repository.getLogger("Sync.Service").level = Log.Level.Trace; michael@0: Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace; michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_identity_test(this, function test_successful_sync_adjustSyncInterval() { michael@0: _("Test successful sync calling adjustSyncInterval"); michael@0: let syncSuccesses = 0; michael@0: function onSyncFinish() { michael@0: _("Sync success."); michael@0: syncSuccesses++; michael@0: }; michael@0: Svc.Obs.add("weave:service:sync:finish", onSyncFinish); michael@0: michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: // Confirm defaults michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: michael@0: _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); michael@0: // idle == true && numClients <= 1 && hasIncomingItems == false michael@0: scheduler.idle = true; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 1); michael@0: do_check_true(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == false && numClients <= 1 && hasIncomingItems == false michael@0: scheduler.idle = false; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 2); michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == false && numClients <= 1 && hasIncomingItems == true michael@0: scheduler.hasIncomingItems = true; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 3); michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == true && numClients <= 1 && hasIncomingItems == true michael@0: scheduler.idle = true; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 4); michael@0: do_check_true(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: _("Test as long as idle && numClients > 1 our sync interval is idleInterval."); michael@0: // idle == true && numClients > 1 && hasIncomingItems == true michael@0: Service.clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 5); michael@0: do_check_true(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.idleInterval); michael@0: michael@0: // idle == true && numClients > 1 && hasIncomingItems == false michael@0: scheduler.hasIncomingItems = false; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 6); michael@0: do_check_true(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.idleInterval); michael@0: michael@0: _("Test non-idle, numClients > 1, no incoming items => activeInterval."); michael@0: // idle == false && numClients > 1 && hasIncomingItems == false michael@0: scheduler.idle = false; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 7); michael@0: do_check_false(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: michael@0: _("Test non-idle, numClients > 1, incoming items => immediateInterval."); michael@0: // idle == false && numClients > 1 && hasIncomingItems == true michael@0: scheduler.hasIncomingItems = true; michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 8); michael@0: do_check_false(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); //gets reset to false michael@0: do_check_eq(scheduler.syncInterval, scheduler.immediateInterval); michael@0: michael@0: Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); michael@0: Service.startOver(); michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_unsuccessful_sync_adjustSyncInterval() { michael@0: _("Test unsuccessful sync calling adjustSyncInterval"); michael@0: michael@0: let syncFailures = 0; michael@0: function onSyncError() { michael@0: _("Sync error."); michael@0: syncFailures++; michael@0: } michael@0: Svc.Obs.add("weave:service:sync:error", onSyncError); michael@0: michael@0: _("Test unsuccessful sync calls adjustSyncInterval"); michael@0: // Force sync to fail. michael@0: Svc.Prefs.set("firstSync", "notReady"); michael@0: michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: // Confirm defaults michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: michael@0: _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); michael@0: // idle == true && numClients <= 1 && hasIncomingItems == false michael@0: scheduler.idle = true; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 1); michael@0: do_check_true(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == false && numClients <= 1 && hasIncomingItems == false michael@0: scheduler.idle = false; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 2); michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == false && numClients <= 1 && hasIncomingItems == true michael@0: scheduler.hasIncomingItems = true; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 3); michael@0: do_check_false(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: // idle == true && numClients <= 1 && hasIncomingItems == true michael@0: scheduler.idle = true; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 4); michael@0: do_check_true(scheduler.idle); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: _("Test as long as idle && numClients > 1 our sync interval is idleInterval."); michael@0: // idle == true && numClients > 1 && hasIncomingItems == true michael@0: Service.clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 5); michael@0: do_check_true(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_true(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.idleInterval); michael@0: michael@0: // idle == true && numClients > 1 && hasIncomingItems == false michael@0: scheduler.hasIncomingItems = false; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 6); michael@0: do_check_true(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.idleInterval); michael@0: michael@0: _("Test non-idle, numClients > 1, no incoming items => activeInterval."); michael@0: // idle == false && numClients > 1 && hasIncomingItems == false michael@0: scheduler.idle = false; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 7); michael@0: do_check_false(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: michael@0: _("Test non-idle, numClients > 1, incoming items => immediateInterval."); michael@0: // idle == false && numClients > 1 && hasIncomingItems == true michael@0: scheduler.hasIncomingItems = true; michael@0: Service.sync(); michael@0: do_check_eq(syncFailures, 8); michael@0: do_check_false(scheduler.idle); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_false(scheduler.hasIncomingItems); //gets reset to false michael@0: do_check_eq(scheduler.syncInterval, scheduler.immediateInterval); michael@0: michael@0: Service.startOver(); michael@0: Svc.Obs.remove("weave:service:sync:error", onSyncError); michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_back_triggers_sync() { michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: // Single device: no sync triggered. michael@0: scheduler.idle = true; michael@0: scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); michael@0: do_check_false(scheduler.idle); michael@0: michael@0: // Multiple devices: sync is triggered. michael@0: clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: scheduler.updateClientMode(); michael@0: michael@0: let deferred = Promise.defer(); michael@0: Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { michael@0: Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); michael@0: michael@0: Service.recordManager.clearCache(); michael@0: Svc.Prefs.resetBranch(""); michael@0: scheduler.setDefaults(); michael@0: clientsEngine.resetClient(); michael@0: michael@0: Service.startOver(); michael@0: server.stop(deferred.resolve); michael@0: }); michael@0: michael@0: scheduler.idle = true; michael@0: scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); michael@0: do_check_false(scheduler.idle); michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: add_identity_test(this, function test_adjust_interval_on_sync_error() { michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let syncFailures = 0; michael@0: function onSyncError() { michael@0: _("Sync error."); michael@0: syncFailures++; michael@0: } michael@0: Svc.Obs.add("weave:service:sync:error", onSyncError); michael@0: michael@0: _("Test unsuccessful sync updates client mode & sync intervals"); michael@0: // Force a sync fail. michael@0: Svc.Prefs.set("firstSync", "notReady"); michael@0: michael@0: do_check_eq(syncFailures, 0); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: Service.sync(); michael@0: michael@0: do_check_eq(syncFailures, 1); michael@0: do_check_true(scheduler.numClients > 1); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: michael@0: Svc.Obs.remove("weave:service:sync:error", onSyncError); michael@0: Service.startOver(); michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_bug671378_scenario() { michael@0: // Test scenario similar to bug 671378. This bug appeared when a score michael@0: // update occurred that wasn't large enough to trigger a sync so michael@0: // scheduleNextSync() was called without a time interval parameter, michael@0: // setting nextSync to a non-zero value and preventing the timer from michael@0: // being adjusted in the next call to scheduleNextSync(). michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let syncSuccesses = 0; michael@0: function onSyncFinish() { michael@0: _("Sync success."); michael@0: syncSuccesses++; michael@0: }; michael@0: Svc.Obs.add("weave:service:sync:finish", onSyncFinish); michael@0: michael@0: // After first sync call, syncInterval & syncTimer are singleDeviceInterval. michael@0: Service.sync(); michael@0: do_check_eq(syncSuccesses, 1); michael@0: do_check_false(scheduler.numClients > 1); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); michael@0: michael@0: let deferred = Promise.defer(); michael@0: // Wrap scheduleNextSync so we are notified when it is finished. michael@0: scheduler._scheduleNextSync = scheduler.scheduleNextSync; michael@0: scheduler.scheduleNextSync = function() { michael@0: scheduler._scheduleNextSync(); michael@0: michael@0: // Check on sync:finish scheduleNextSync sets the appropriate michael@0: // syncInterval and syncTimer values. michael@0: if (syncSuccesses == 2) { michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); michael@0: michael@0: scheduler.scheduleNextSync = scheduler._scheduleNextSync; michael@0: Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); michael@0: Service.startOver(); michael@0: server.stop(deferred.resolve); michael@0: } michael@0: }; michael@0: michael@0: // Set nextSync != 0 michael@0: // syncInterval still hasn't been set by call to updateClientMode. michael@0: // Explicitly trying to invoke scheduleNextSync during a sync michael@0: // (to immitate a score update that isn't big enough to trigger a sync). michael@0: Svc.Obs.add("weave:service:sync:start", function onSyncStart() { michael@0: // Wait for other sync:start observers to be called so that michael@0: // nextSync is set to 0. michael@0: Utils.nextTick(function() { michael@0: Svc.Obs.remove("weave:service:sync:start", onSyncStart); michael@0: michael@0: scheduler.scheduleNextSync(); michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); michael@0: }); michael@0: }); michael@0: michael@0: clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: Service.sync(); michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: add_test(function test_adjust_timer_larger_syncInterval() { michael@0: _("Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used."); michael@0: clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: scheduler.updateClientMode(); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: michael@0: scheduler.scheduleNextSync(); michael@0: michael@0: // Ensure we have a small interval. michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_eq(scheduler.syncTimer.delay, scheduler.activeInterval); michael@0: michael@0: // Make interval large again michael@0: clientsEngine._wipeClient(); michael@0: scheduler.updateClientMode(); michael@0: do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); michael@0: michael@0: scheduler.scheduleNextSync(); michael@0: michael@0: // Ensure timer delay remains as the small interval. michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); michael@0: michael@0: //SyncSchedule. michael@0: Service.startOver(); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_test(function test_adjust_timer_smaller_syncInterval() { michael@0: _("Test current timout > syncInterval period && nextSync != 0, syncInterval is used."); michael@0: scheduler.scheduleNextSync(); michael@0: michael@0: // Ensure we have a large interval. michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); michael@0: michael@0: // Make interval smaller michael@0: clientsEngine._store.create({id: "foo", cleartext: "bar"}); michael@0: scheduler.updateClientMode(); michael@0: do_check_eq(scheduler.syncInterval, scheduler.activeInterval); michael@0: michael@0: scheduler.scheduleNextSync(); michael@0: michael@0: // Ensure smaller timer delay is used. michael@0: do_check_neq(scheduler.nextSync, 0); michael@0: do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); michael@0: michael@0: //SyncSchedule. michael@0: Service.startOver(); michael@0: run_next_test(); michael@0: });