1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tests/unit/test_syncscheduler.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1004 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +Cu.import("resource://services-sync/constants.js"); 1.8 +Cu.import("resource://services-sync/engines.js"); 1.9 +Cu.import("resource://services-sync/engines/clients.js"); 1.10 +Cu.import("resource://services-sync/policies.js"); 1.11 +Cu.import("resource://services-sync/record.js"); 1.12 +Cu.import("resource://services-sync/service.js"); 1.13 +Cu.import("resource://services-sync/status.js"); 1.14 +Cu.import("resource://services-sync/util.js"); 1.15 +Cu.import("resource://testing-common/services/sync/utils.js"); 1.16 + 1.17 +Service.engineManager.clear(); 1.18 + 1.19 +function CatapultEngine() { 1.20 + SyncEngine.call(this, "Catapult", Service); 1.21 +} 1.22 +CatapultEngine.prototype = { 1.23 + __proto__: SyncEngine.prototype, 1.24 + exception: null, // tests fill this in 1.25 + _sync: function _sync() { 1.26 + throw this.exception; 1.27 + } 1.28 +}; 1.29 + 1.30 +Service.engineManager.register(CatapultEngine); 1.31 + 1.32 +let scheduler = new SyncScheduler(Service); 1.33 +let clientsEngine = Service.clientsEngine; 1.34 + 1.35 +function sync_httpd_setup() { 1.36 + let global = new ServerWBO("global", { 1.37 + syncID: Service.syncID, 1.38 + storageVersion: STORAGE_VERSION, 1.39 + engines: {clients: {version: clientsEngine.version, 1.40 + syncID: clientsEngine.syncID}} 1.41 + }); 1.42 + let clientsColl = new ServerCollection({}, true); 1.43 + 1.44 + // Tracking info/collections. 1.45 + let collectionsHelper = track_collections_helper(); 1.46 + let upd = collectionsHelper.with_updated_collection; 1.47 + 1.48 + return httpd_setup({ 1.49 + "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()), 1.50 + "/1.1/johndoe/info/collections": collectionsHelper.handler, 1.51 + "/1.1/johndoe/storage/crypto/keys": 1.52 + upd("crypto", (new ServerWBO("keys")).handler()), 1.53 + "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()), 1.54 + "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "null") 1.55 + }); 1.56 +} 1.57 + 1.58 +function setUp(server) { 1.59 + let deferred = Promise.defer(); 1.60 + configureIdentity({username: "johndoe"}).then(() => { 1.61 + Service.clusterURL = server.baseURI + "/"; 1.62 + 1.63 + generateNewKeys(Service.collectionKeys); 1.64 + let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 1.65 + serverKeys.encrypt(Service.identity.syncKeyBundle); 1.66 + let result = serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success; 1.67 + deferred.resolve(result); 1.68 + }); 1.69 + return deferred.promise; 1.70 +} 1.71 + 1.72 +function cleanUpAndGo(server) { 1.73 + let deferred = Promise.defer(); 1.74 + Utils.nextTick(function () { 1.75 + Service.startOver(); 1.76 + if (server) { 1.77 + server.stop(deferred.resolve); 1.78 + } else { 1.79 + deferred.resolve(); 1.80 + } 1.81 + }); 1.82 + return deferred.promise; 1.83 +} 1.84 + 1.85 +function run_test() { 1.86 + initTestLogging("Trace"); 1.87 + 1.88 + Log.repository.getLogger("Sync.Service").level = Log.Level.Trace; 1.89 + Log.repository.getLogger("Sync.scheduler").level = Log.Level.Trace; 1.90 + 1.91 + // The scheduler checks Weave.fxaEnabled to determine whether to use 1.92 + // FxA defaults or legacy defaults. As .fxaEnabled checks the username, we 1.93 + // set a username here then reset the default to ensure they are used. 1.94 + ensureLegacyIdentityManager(); 1.95 + setBasicCredentials("johndoe"); 1.96 + scheduler.setDefaults(); 1.97 + 1.98 + run_next_test(); 1.99 +} 1.100 + 1.101 +add_test(function test_prefAttributes() { 1.102 + _("Test various attributes corresponding to preferences."); 1.103 + 1.104 + const INTERVAL = 42 * 60 * 1000; // 42 minutes 1.105 + const THRESHOLD = 3142; 1.106 + const SCORE = 2718; 1.107 + const TIMESTAMP1 = 1275493471649; 1.108 + 1.109 + _("The 'nextSync' attribute stores a millisecond timestamp rounded down to the nearest second."); 1.110 + do_check_eq(scheduler.nextSync, 0); 1.111 + scheduler.nextSync = TIMESTAMP1; 1.112 + do_check_eq(scheduler.nextSync, Math.floor(TIMESTAMP1 / 1000) * 1000); 1.113 + 1.114 + _("'syncInterval' defaults to singleDeviceInterval."); 1.115 + do_check_eq(Svc.Prefs.get('syncInterval'), undefined); 1.116 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.117 + 1.118 + _("'syncInterval' corresponds to a preference setting."); 1.119 + scheduler.syncInterval = INTERVAL; 1.120 + do_check_eq(scheduler.syncInterval, INTERVAL); 1.121 + do_check_eq(Svc.Prefs.get('syncInterval'), INTERVAL); 1.122 + 1.123 + _("'syncThreshold' corresponds to preference, defaults to SINGLE_USER_THRESHOLD"); 1.124 + do_check_eq(Svc.Prefs.get('syncThreshold'), undefined); 1.125 + do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD); 1.126 + scheduler.syncThreshold = THRESHOLD; 1.127 + do_check_eq(scheduler.syncThreshold, THRESHOLD); 1.128 + 1.129 + _("'globalScore' corresponds to preference, defaults to zero."); 1.130 + do_check_eq(Svc.Prefs.get('globalScore'), 0); 1.131 + do_check_eq(scheduler.globalScore, 0); 1.132 + scheduler.globalScore = SCORE; 1.133 + do_check_eq(scheduler.globalScore, SCORE); 1.134 + do_check_eq(Svc.Prefs.get('globalScore'), SCORE); 1.135 + 1.136 + _("Intervals correspond to default preferences."); 1.137 + do_check_eq(scheduler.singleDeviceInterval, 1.138 + Svc.Prefs.get("scheduler.sync11.singleDeviceInterval") * 1000); 1.139 + do_check_eq(scheduler.idleInterval, 1.140 + Svc.Prefs.get("scheduler.idleInterval") * 1000); 1.141 + do_check_eq(scheduler.activeInterval, 1.142 + Svc.Prefs.get("scheduler.activeInterval") * 1000); 1.143 + do_check_eq(scheduler.immediateInterval, 1.144 + Svc.Prefs.get("scheduler.immediateInterval") * 1000); 1.145 + 1.146 + _("Custom values for prefs will take effect after a restart."); 1.147 + Svc.Prefs.set("scheduler.sync11.singleDeviceInterval", 42); 1.148 + Svc.Prefs.set("scheduler.idleInterval", 23); 1.149 + Svc.Prefs.set("scheduler.activeInterval", 18); 1.150 + Svc.Prefs.set("scheduler.immediateInterval", 31415); 1.151 + scheduler.setDefaults(); 1.152 + do_check_eq(scheduler.idleInterval, 23000); 1.153 + do_check_eq(scheduler.singleDeviceInterval, 42000); 1.154 + do_check_eq(scheduler.activeInterval, 18000); 1.155 + do_check_eq(scheduler.immediateInterval, 31415000); 1.156 + 1.157 + Svc.Prefs.resetBranch(""); 1.158 + scheduler.setDefaults(); 1.159 + run_next_test(); 1.160 +}); 1.161 + 1.162 +add_identity_test(this, function test_updateClientMode() { 1.163 + _("Test updateClientMode adjusts scheduling attributes based on # of clients appropriately"); 1.164 + do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD); 1.165 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.166 + do_check_false(scheduler.numClients > 1); 1.167 + do_check_false(scheduler.idle); 1.168 + 1.169 + // Trigger a change in interval & threshold by adding a client. 1.170 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.171 + scheduler.updateClientMode(); 1.172 + 1.173 + do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD); 1.174 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.175 + do_check_true(scheduler.numClients > 1); 1.176 + do_check_false(scheduler.idle); 1.177 + 1.178 + // Resets the number of clients to 0. 1.179 + clientsEngine.resetClient(); 1.180 + scheduler.updateClientMode(); 1.181 + 1.182 + // Goes back to single user if # clients is 1. 1.183 + do_check_eq(scheduler.numClients, 1); 1.184 + do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD); 1.185 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.186 + do_check_false(scheduler.numClients > 1); 1.187 + do_check_false(scheduler.idle); 1.188 + 1.189 + yield cleanUpAndGo(); 1.190 +}); 1.191 + 1.192 +add_identity_test(this, function test_masterpassword_locked_retry_interval() { 1.193 + _("Test Status.login = MASTER_PASSWORD_LOCKED results in reschedule at MASTER_PASSWORD interval"); 1.194 + let loginFailed = false; 1.195 + Svc.Obs.add("weave:service:login:error", function onLoginError() { 1.196 + Svc.Obs.remove("weave:service:login:error", onLoginError); 1.197 + loginFailed = true; 1.198 + }); 1.199 + 1.200 + let rescheduleInterval = false; 1.201 + 1.202 + let oldScheduleAtInterval = SyncScheduler.prototype.scheduleAtInterval; 1.203 + SyncScheduler.prototype.scheduleAtInterval = function (interval) { 1.204 + rescheduleInterval = true; 1.205 + do_check_eq(interval, MASTER_PASSWORD_LOCKED_RETRY_INTERVAL); 1.206 + }; 1.207 + 1.208 + let oldVerifyLogin = Service.verifyLogin; 1.209 + Service.verifyLogin = function () { 1.210 + Status.login = MASTER_PASSWORD_LOCKED; 1.211 + return false; 1.212 + }; 1.213 + 1.214 + let server = sync_httpd_setup(); 1.215 + yield setUp(server); 1.216 + 1.217 + Service.sync(); 1.218 + 1.219 + do_check_true(loginFailed); 1.220 + do_check_eq(Status.login, MASTER_PASSWORD_LOCKED); 1.221 + do_check_true(rescheduleInterval); 1.222 + 1.223 + Service.verifyLogin = oldVerifyLogin; 1.224 + SyncScheduler.prototype.scheduleAtInterval = oldScheduleAtInterval; 1.225 + 1.226 + yield cleanUpAndGo(server); 1.227 +}); 1.228 + 1.229 +add_identity_test(this, function test_calculateBackoff() { 1.230 + do_check_eq(Status.backoffInterval, 0); 1.231 + 1.232 + // Test no interval larger than the maximum backoff is used if 1.233 + // Status.backoffInterval is smaller. 1.234 + Status.backoffInterval = 5; 1.235 + let backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL, 1.236 + Status.backoffInterval); 1.237 + 1.238 + do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL); 1.239 + 1.240 + // Test Status.backoffInterval is used if it is 1.241 + // larger than MAXIMUM_BACKOFF_INTERVAL. 1.242 + Status.backoffInterval = MAXIMUM_BACKOFF_INTERVAL + 10; 1.243 + backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL, 1.244 + Status.backoffInterval); 1.245 + 1.246 + do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL + 10); 1.247 + 1.248 + yield cleanUpAndGo(); 1.249 +}); 1.250 + 1.251 +add_identity_test(this, function test_scheduleNextSync_nowOrPast() { 1.252 + let deferred = Promise.defer(); 1.253 + Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { 1.254 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.255 + cleanUpAndGo(server).then(deferred.resolve); 1.256 + }); 1.257 + 1.258 + let server = sync_httpd_setup(); 1.259 + yield setUp(server); 1.260 + 1.261 + // We're late for a sync... 1.262 + scheduler.scheduleNextSync(-1); 1.263 + yield deferred.promise; 1.264 +}); 1.265 + 1.266 +add_identity_test(this, function test_scheduleNextSync_future_noBackoff() { 1.267 + _("scheduleNextSync() uses the current syncInterval if no interval is provided."); 1.268 + // Test backoffInterval is 0 as expected. 1.269 + do_check_eq(Status.backoffInterval, 0); 1.270 + 1.271 + _("Test setting sync interval when nextSync == 0"); 1.272 + scheduler.nextSync = 0; 1.273 + scheduler.scheduleNextSync(); 1.274 + 1.275 + // nextSync - Date.now() might be smaller than expectedInterval 1.276 + // since some time has passed since we called scheduleNextSync(). 1.277 + do_check_true(scheduler.nextSync - Date.now() 1.278 + <= scheduler.syncInterval); 1.279 + do_check_eq(scheduler.syncTimer.delay, scheduler.syncInterval); 1.280 + 1.281 + _("Test setting sync interval when nextSync != 0"); 1.282 + scheduler.nextSync = Date.now() + scheduler.singleDeviceInterval; 1.283 + scheduler.scheduleNextSync(); 1.284 + 1.285 + // nextSync - Date.now() might be smaller than expectedInterval 1.286 + // since some time has passed since we called scheduleNextSync(). 1.287 + do_check_true(scheduler.nextSync - Date.now() 1.288 + <= scheduler.syncInterval); 1.289 + do_check_true(scheduler.syncTimer.delay <= scheduler.syncInterval); 1.290 + 1.291 + _("Scheduling requests for intervals larger than the current one will be ignored."); 1.292 + // Request a sync at a longer interval. The sync that's already scheduled 1.293 + // for sooner takes precedence. 1.294 + let nextSync = scheduler.nextSync; 1.295 + let timerDelay = scheduler.syncTimer.delay; 1.296 + let requestedInterval = scheduler.syncInterval * 10; 1.297 + scheduler.scheduleNextSync(requestedInterval); 1.298 + do_check_eq(scheduler.nextSync, nextSync); 1.299 + do_check_eq(scheduler.syncTimer.delay, timerDelay); 1.300 + 1.301 + // We can schedule anything we want if there isn't a sync scheduled. 1.302 + scheduler.nextSync = 0; 1.303 + scheduler.scheduleNextSync(requestedInterval); 1.304 + do_check_true(scheduler.nextSync <= Date.now() + requestedInterval); 1.305 + do_check_eq(scheduler.syncTimer.delay, requestedInterval); 1.306 + 1.307 + // Request a sync at the smallest possible interval (0 triggers now). 1.308 + scheduler.scheduleNextSync(1); 1.309 + do_check_true(scheduler.nextSync <= Date.now() + 1); 1.310 + do_check_eq(scheduler.syncTimer.delay, 1); 1.311 + 1.312 + yield cleanUpAndGo(); 1.313 +}); 1.314 + 1.315 +add_identity_test(this, function test_scheduleNextSync_future_backoff() { 1.316 + _("scheduleNextSync() will honour backoff in all scheduling requests."); 1.317 + // Let's take a backoff interval that's bigger than the default sync interval. 1.318 + const BACKOFF = 7337; 1.319 + Status.backoffInterval = scheduler.syncInterval + BACKOFF; 1.320 + 1.321 + _("Test setting sync interval when nextSync == 0"); 1.322 + scheduler.nextSync = 0; 1.323 + scheduler.scheduleNextSync(); 1.324 + 1.325 + // nextSync - Date.now() might be smaller than expectedInterval 1.326 + // since some time has passed since we called scheduleNextSync(). 1.327 + do_check_true(scheduler.nextSync - Date.now() 1.328 + <= Status.backoffInterval); 1.329 + do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval); 1.330 + 1.331 + _("Test setting sync interval when nextSync != 0"); 1.332 + scheduler.nextSync = Date.now() + scheduler.singleDeviceInterval; 1.333 + scheduler.scheduleNextSync(); 1.334 + 1.335 + // nextSync - Date.now() might be smaller than expectedInterval 1.336 + // since some time has passed since we called scheduleNextSync(). 1.337 + do_check_true(scheduler.nextSync - Date.now() 1.338 + <= Status.backoffInterval); 1.339 + do_check_true(scheduler.syncTimer.delay <= Status.backoffInterval); 1.340 + 1.341 + // Request a sync at a longer interval. The sync that's already scheduled 1.342 + // for sooner takes precedence. 1.343 + let nextSync = scheduler.nextSync; 1.344 + let timerDelay = scheduler.syncTimer.delay; 1.345 + let requestedInterval = scheduler.syncInterval * 10; 1.346 + do_check_true(requestedInterval > Status.backoffInterval); 1.347 + scheduler.scheduleNextSync(requestedInterval); 1.348 + do_check_eq(scheduler.nextSync, nextSync); 1.349 + do_check_eq(scheduler.syncTimer.delay, timerDelay); 1.350 + 1.351 + // We can schedule anything we want if there isn't a sync scheduled. 1.352 + scheduler.nextSync = 0; 1.353 + scheduler.scheduleNextSync(requestedInterval); 1.354 + do_check_true(scheduler.nextSync <= Date.now() + requestedInterval); 1.355 + do_check_eq(scheduler.syncTimer.delay, requestedInterval); 1.356 + 1.357 + // Request a sync at the smallest possible interval (0 triggers now). 1.358 + scheduler.scheduleNextSync(1); 1.359 + do_check_true(scheduler.nextSync <= Date.now() + Status.backoffInterval); 1.360 + do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval); 1.361 + 1.362 + yield cleanUpAndGo(); 1.363 +}); 1.364 + 1.365 +add_identity_test(this, function test_handleSyncError() { 1.366 + let server = sync_httpd_setup(); 1.367 + yield setUp(server); 1.368 + 1.369 + // Force sync to fail. 1.370 + Svc.Prefs.set("firstSync", "notReady"); 1.371 + 1.372 + _("Ensure expected initial environment."); 1.373 + do_check_eq(scheduler._syncErrors, 0); 1.374 + do_check_false(Status.enforceBackoff); 1.375 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.376 + do_check_eq(Status.backoffInterval, 0); 1.377 + 1.378 + // Trigger sync with an error several times & observe 1.379 + // functionality of handleSyncError() 1.380 + _("Test first error calls scheduleNextSync on default interval"); 1.381 + Service.sync(); 1.382 + do_check_true(scheduler.nextSync <= Date.now() + scheduler.singleDeviceInterval); 1.383 + do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 1.384 + do_check_eq(scheduler._syncErrors, 1); 1.385 + do_check_false(Status.enforceBackoff); 1.386 + scheduler.syncTimer.clear(); 1.387 + 1.388 + _("Test second error still calls scheduleNextSync on default interval"); 1.389 + Service.sync(); 1.390 + do_check_true(scheduler.nextSync <= Date.now() + scheduler.singleDeviceInterval); 1.391 + do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 1.392 + do_check_eq(scheduler._syncErrors, 2); 1.393 + do_check_false(Status.enforceBackoff); 1.394 + scheduler.syncTimer.clear(); 1.395 + 1.396 + _("Test third error sets Status.enforceBackoff and calls scheduleAtInterval"); 1.397 + Service.sync(); 1.398 + let maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL); 1.399 + do_check_eq(Status.backoffInterval, 0); 1.400 + do_check_true(scheduler.nextSync <= (Date.now() + maxInterval)); 1.401 + do_check_true(scheduler.syncTimer.delay <= maxInterval); 1.402 + do_check_eq(scheduler._syncErrors, 3); 1.403 + do_check_true(Status.enforceBackoff); 1.404 + 1.405 + // Status.enforceBackoff is false but there are still errors. 1.406 + Status.resetBackoff(); 1.407 + do_check_false(Status.enforceBackoff); 1.408 + do_check_eq(scheduler._syncErrors, 3); 1.409 + scheduler.syncTimer.clear(); 1.410 + 1.411 + _("Test fourth error still calls scheduleAtInterval even if enforceBackoff was reset"); 1.412 + Service.sync(); 1.413 + maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL); 1.414 + do_check_true(scheduler.nextSync <= Date.now() + maxInterval); 1.415 + do_check_true(scheduler.syncTimer.delay <= maxInterval); 1.416 + do_check_eq(scheduler._syncErrors, 4); 1.417 + do_check_true(Status.enforceBackoff); 1.418 + scheduler.syncTimer.clear(); 1.419 + 1.420 + _("Arrange for a successful sync to reset the scheduler error count"); 1.421 + let deferred = Promise.defer(); 1.422 + Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { 1.423 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.424 + cleanUpAndGo(server).then(deferred.resolve); 1.425 + }); 1.426 + Svc.Prefs.set("firstSync", "wipeRemote"); 1.427 + scheduler.scheduleNextSync(-1); 1.428 + yield deferred.promise; 1.429 +}); 1.430 + 1.431 +add_identity_test(this, function test_client_sync_finish_updateClientMode() { 1.432 + let server = sync_httpd_setup(); 1.433 + yield setUp(server); 1.434 + 1.435 + // Confirm defaults. 1.436 + do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD); 1.437 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.438 + do_check_false(scheduler.idle); 1.439 + 1.440 + // Trigger a change in interval & threshold by adding a client. 1.441 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.442 + do_check_false(scheduler.numClients > 1); 1.443 + scheduler.updateClientMode(); 1.444 + Service.sync(); 1.445 + 1.446 + do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD); 1.447 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.448 + do_check_true(scheduler.numClients > 1); 1.449 + do_check_false(scheduler.idle); 1.450 + 1.451 + // Resets the number of clients to 0. 1.452 + clientsEngine.resetClient(); 1.453 + Service.sync(); 1.454 + 1.455 + // Goes back to single user if # clients is 1. 1.456 + do_check_eq(scheduler.numClients, 1); 1.457 + do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD); 1.458 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.459 + do_check_false(scheduler.numClients > 1); 1.460 + do_check_false(scheduler.idle); 1.461 + 1.462 + yield cleanUpAndGo(server); 1.463 +}); 1.464 + 1.465 +add_identity_test(this, function test_autoconnect_nextSync_past() { 1.466 + let deferred = Promise.defer(); 1.467 + // nextSync will be 0 by default, so it's way in the past. 1.468 + 1.469 + Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { 1.470 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.471 + cleanUpAndGo(server).then(deferred.resolve); 1.472 + }); 1.473 + 1.474 + let server = sync_httpd_setup(); 1.475 + yield setUp(server); 1.476 + 1.477 + scheduler.delayedAutoConnect(0); 1.478 + yield deferred.promise; 1.479 +}); 1.480 + 1.481 +add_identity_test(this, function test_autoconnect_nextSync_future() { 1.482 + let deferred = Promise.defer(); 1.483 + let previousSync = Date.now() + scheduler.syncInterval / 2; 1.484 + scheduler.nextSync = previousSync; 1.485 + // nextSync rounds to the nearest second. 1.486 + let expectedSync = scheduler.nextSync; 1.487 + let expectedInterval = expectedSync - Date.now() - 1000; 1.488 + 1.489 + // Ensure we don't actually try to sync (or log in for that matter). 1.490 + function onLoginStart() { 1.491 + do_throw("Should not get here!"); 1.492 + } 1.493 + Svc.Obs.add("weave:service:login:start", onLoginStart); 1.494 + 1.495 + waitForZeroTimer(function () { 1.496 + do_check_eq(scheduler.nextSync, expectedSync); 1.497 + do_check_true(scheduler.syncTimer.delay >= expectedInterval); 1.498 + 1.499 + Svc.Obs.remove("weave:service:login:start", onLoginStart); 1.500 + cleanUpAndGo().then(deferred.resolve); 1.501 + }); 1.502 + 1.503 + yield configureIdentity({username: "johndoe"}); 1.504 + scheduler.delayedAutoConnect(0); 1.505 + yield deferred.promise; 1.506 +}); 1.507 + 1.508 +// XXX - this test can't be run with the browserid identity as it relies 1.509 +// on the syncKey getter behaving in a certain way... 1.510 +add_task(function test_autoconnect_mp_locked() { 1.511 + let server = sync_httpd_setup(); 1.512 + yield setUp(server); 1.513 + 1.514 + // Pretend user did not unlock master password. 1.515 + let origLocked = Utils.mpLocked; 1.516 + Utils.mpLocked = function() true; 1.517 + 1.518 + let origGetter = Service.identity.__lookupGetter__("syncKey"); 1.519 + let origSetter = Service.identity.__lookupSetter__("syncKey"); 1.520 + delete Service.identity.syncKey; 1.521 + Service.identity.__defineGetter__("syncKey", function() { 1.522 + _("Faking Master Password entry cancelation."); 1.523 + throw "User canceled Master Password entry"; 1.524 + }); 1.525 + 1.526 + let deferred = Promise.defer(); 1.527 + // A locked master password will still trigger a sync, but then we'll hit 1.528 + // MASTER_PASSWORD_LOCKED and hence MASTER_PASSWORD_LOCKED_RETRY_INTERVAL. 1.529 + Svc.Obs.add("weave:service:login:error", function onLoginError() { 1.530 + Svc.Obs.remove("weave:service:login:error", onLoginError); 1.531 + Utils.nextTick(function aLittleBitAfterLoginError() { 1.532 + do_check_eq(Status.login, MASTER_PASSWORD_LOCKED); 1.533 + 1.534 + Utils.mpLocked = origLocked; 1.535 + delete Service.identity.syncKey; 1.536 + Service.identity.__defineGetter__("syncKey", origGetter); 1.537 + Service.identity.__defineSetter__("syncKey", origSetter); 1.538 + 1.539 + cleanUpAndGo(server).then(deferred.resolve); 1.540 + }); 1.541 + }); 1.542 + 1.543 + scheduler.delayedAutoConnect(0); 1.544 + yield deferred.promise; 1.545 +}); 1.546 + 1.547 +add_identity_test(this, function test_no_autoconnect_during_wizard() { 1.548 + let server = sync_httpd_setup(); 1.549 + yield setUp(server); 1.550 + 1.551 + // Simulate the Sync setup wizard. 1.552 + Svc.Prefs.set("firstSync", "notReady"); 1.553 + 1.554 + // Ensure we don't actually try to sync (or log in for that matter). 1.555 + function onLoginStart() { 1.556 + do_throw("Should not get here!"); 1.557 + } 1.558 + Svc.Obs.add("weave:service:login:start", onLoginStart); 1.559 + 1.560 + let deferred = Promise.defer(); 1.561 + waitForZeroTimer(function () { 1.562 + Svc.Obs.remove("weave:service:login:start", onLoginStart); 1.563 + cleanUpAndGo(server).then(deferred.resolve); 1.564 + }); 1.565 + 1.566 + scheduler.delayedAutoConnect(0); 1.567 + yield deferred.promise; 1.568 +}); 1.569 + 1.570 +add_identity_test(this, function test_no_autoconnect_status_not_ok() { 1.571 + let server = sync_httpd_setup(); 1.572 + 1.573 + // Ensure we don't actually try to sync (or log in for that matter). 1.574 + function onLoginStart() { 1.575 + do_throw("Should not get here!"); 1.576 + } 1.577 + Svc.Obs.add("weave:service:login:start", onLoginStart); 1.578 + 1.579 + let deferred = Promise.defer(); 1.580 + waitForZeroTimer(function () { 1.581 + Svc.Obs.remove("weave:service:login:start", onLoginStart); 1.582 + 1.583 + do_check_eq(Status.service, CLIENT_NOT_CONFIGURED); 1.584 + do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME); 1.585 + 1.586 + cleanUpAndGo(server).then(deferred.resolve); 1.587 + }); 1.588 + 1.589 + scheduler.delayedAutoConnect(0); 1.590 + yield deferred.promise; 1.591 +}); 1.592 + 1.593 +add_identity_test(this, function test_autoconnectDelay_pref() { 1.594 + let deferred = Promise.defer(); 1.595 + Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { 1.596 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.597 + cleanUpAndGo(server).then(deferred.resolve); 1.598 + }); 1.599 + 1.600 + Svc.Prefs.set("autoconnectDelay", 1); 1.601 + 1.602 + let server = sync_httpd_setup(); 1.603 + yield setUp(server); 1.604 + 1.605 + Svc.Obs.notify("weave:service:ready"); 1.606 + 1.607 + // autoconnectDelay pref is multiplied by 1000. 1.608 + do_check_eq(scheduler._autoTimer.delay, 1000); 1.609 + do_check_eq(Status.service, STATUS_OK); 1.610 + yield deferred.promise; 1.611 +}); 1.612 + 1.613 +add_identity_test(this, function test_idle_adjustSyncInterval() { 1.614 + // Confirm defaults. 1.615 + do_check_eq(scheduler.idle, false); 1.616 + 1.617 + // Single device: nothing changes. 1.618 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.619 + do_check_eq(scheduler.idle, true); 1.620 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.621 + 1.622 + // Multiple devices: switch to idle interval. 1.623 + scheduler.idle = false; 1.624 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.625 + scheduler.updateClientMode(); 1.626 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.627 + do_check_eq(scheduler.idle, true); 1.628 + do_check_eq(scheduler.syncInterval, scheduler.idleInterval); 1.629 + 1.630 + yield cleanUpAndGo(); 1.631 +}); 1.632 + 1.633 +add_identity_test(this, function test_back_triggersSync() { 1.634 + // Confirm defaults. 1.635 + do_check_false(scheduler.idle); 1.636 + do_check_eq(Status.backoffInterval, 0); 1.637 + 1.638 + // Set up: Define 2 clients and put the system in idle. 1.639 + scheduler.numClients = 2; 1.640 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.641 + do_check_true(scheduler.idle); 1.642 + 1.643 + let deferred = Promise.defer(); 1.644 + // We don't actually expect the sync (or the login, for that matter) to 1.645 + // succeed. We just want to ensure that it was attempted. 1.646 + Svc.Obs.add("weave:service:login:error", function onLoginError() { 1.647 + Svc.Obs.remove("weave:service:login:error", onLoginError); 1.648 + cleanUpAndGo().then(deferred.resolve); 1.649 + }); 1.650 + 1.651 + // Send an 'active' event to trigger sync soonish. 1.652 + scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); 1.653 + yield deferred.promise; 1.654 +}); 1.655 + 1.656 +add_identity_test(this, function test_active_triggersSync_observesBackoff() { 1.657 + // Confirm defaults. 1.658 + do_check_false(scheduler.idle); 1.659 + 1.660 + // Set up: Set backoff, define 2 clients and put the system in idle. 1.661 + const BACKOFF = 7337; 1.662 + Status.backoffInterval = scheduler.idleInterval + BACKOFF; 1.663 + scheduler.numClients = 2; 1.664 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.665 + do_check_eq(scheduler.idle, true); 1.666 + 1.667 + function onLoginStart() { 1.668 + do_throw("Shouldn't have kicked off a sync!"); 1.669 + } 1.670 + Svc.Obs.add("weave:service:login:start", onLoginStart); 1.671 + 1.672 + let deferred = Promise.defer(); 1.673 + timer = Utils.namedTimer(function () { 1.674 + Svc.Obs.remove("weave:service:login:start", onLoginStart); 1.675 + 1.676 + do_check_true(scheduler.nextSync <= Date.now() + Status.backoffInterval); 1.677 + do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval); 1.678 + 1.679 + cleanUpAndGo().then(deferred.resolve); 1.680 + }, IDLE_OBSERVER_BACK_DELAY * 1.5, {}, "timer"); 1.681 + 1.682 + // Send an 'active' event to try to trigger sync soonish. 1.683 + scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); 1.684 + yield deferred.promise; 1.685 +}); 1.686 + 1.687 +add_identity_test(this, function test_back_debouncing() { 1.688 + _("Ensure spurious back-then-idle events, as observed on OS X, don't trigger a sync."); 1.689 + 1.690 + // Confirm defaults. 1.691 + do_check_eq(scheduler.idle, false); 1.692 + 1.693 + // Set up: Define 2 clients and put the system in idle. 1.694 + scheduler.numClients = 2; 1.695 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.696 + do_check_eq(scheduler.idle, true); 1.697 + 1.698 + function onLoginStart() { 1.699 + do_throw("Shouldn't have kicked off a sync!"); 1.700 + } 1.701 + Svc.Obs.add("weave:service:login:start", onLoginStart); 1.702 + 1.703 + // Create spurious back-then-idle events as observed on OS X: 1.704 + scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); 1.705 + scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime")); 1.706 + 1.707 + let deferred = Promise.defer(); 1.708 + timer = Utils.namedTimer(function () { 1.709 + Svc.Obs.remove("weave:service:login:start", onLoginStart); 1.710 + cleanUpAndGo().then(deferred.resolve); 1.711 + }, IDLE_OBSERVER_BACK_DELAY * 1.5, {}, "timer"); 1.712 + yield deferred.promise; 1.713 +}); 1.714 + 1.715 +add_identity_test(this, function test_no_sync_node() { 1.716 + // Test when Status.sync == NO_SYNC_NODE_FOUND 1.717 + // it is not overwritten on sync:finish 1.718 + let server = sync_httpd_setup(); 1.719 + yield setUp(server); 1.720 + 1.721 + Service.serverURL = server.baseURI + "/"; 1.722 + 1.723 + Service.sync(); 1.724 + do_check_eq(Status.sync, NO_SYNC_NODE_FOUND); 1.725 + do_check_eq(scheduler.syncTimer.delay, NO_SYNC_NODE_INTERVAL); 1.726 + 1.727 + yield cleanUpAndGo(server); 1.728 +}); 1.729 + 1.730 +add_identity_test(this, function test_sync_failed_partial_500s() { 1.731 + _("Test a 5xx status calls handleSyncError."); 1.732 + scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF; 1.733 + let server = sync_httpd_setup(); 1.734 + 1.735 + let engine = Service.engineManager.get("catapult"); 1.736 + engine.enabled = true; 1.737 + engine.exception = {status: 500}; 1.738 + 1.739 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.740 + 1.741 + do_check_true(yield setUp(server)); 1.742 + 1.743 + Service.sync(); 1.744 + 1.745 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.746 + 1.747 + let maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL); 1.748 + do_check_eq(Status.backoffInterval, 0); 1.749 + do_check_true(Status.enforceBackoff); 1.750 + do_check_eq(scheduler._syncErrors, 4); 1.751 + do_check_true(scheduler.nextSync <= (Date.now() + maxInterval)); 1.752 + do_check_true(scheduler.syncTimer.delay <= maxInterval); 1.753 + 1.754 + yield cleanUpAndGo(server); 1.755 +}); 1.756 + 1.757 +add_identity_test(this, function test_sync_failed_partial_400s() { 1.758 + _("Test a non-5xx status doesn't call handleSyncError."); 1.759 + scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF; 1.760 + let server = sync_httpd_setup(); 1.761 + 1.762 + let engine = Service.engineManager.get("catapult"); 1.763 + engine.enabled = true; 1.764 + engine.exception = {status: 400}; 1.765 + 1.766 + // Have multiple devices for an active interval. 1.767 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.768 + 1.769 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.770 + 1.771 + do_check_true(yield setUp(server)); 1.772 + 1.773 + Service.sync(); 1.774 + 1.775 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.776 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.777 + 1.778 + do_check_eq(Status.backoffInterval, 0); 1.779 + do_check_false(Status.enforceBackoff); 1.780 + do_check_eq(scheduler._syncErrors, 0); 1.781 + do_check_true(scheduler.nextSync <= (Date.now() + scheduler.activeInterval)); 1.782 + do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); 1.783 + 1.784 + yield cleanUpAndGo(server); 1.785 +}); 1.786 + 1.787 +add_identity_test(this, function test_sync_X_Weave_Backoff() { 1.788 + let server = sync_httpd_setup(); 1.789 + yield setUp(server); 1.790 + 1.791 + // Use an odd value on purpose so that it doesn't happen to coincide with one 1.792 + // of the sync intervals. 1.793 + const BACKOFF = 7337; 1.794 + 1.795 + // Extend info/collections so that we can put it into server maintenance mode. 1.796 + const INFO_COLLECTIONS = "/1.1/johndoe/info/collections"; 1.797 + let infoColl = server._handler._overridePaths[INFO_COLLECTIONS]; 1.798 + let serverBackoff = false; 1.799 + function infoCollWithBackoff(request, response) { 1.800 + if (serverBackoff) { 1.801 + response.setHeader("X-Weave-Backoff", "" + BACKOFF); 1.802 + } 1.803 + infoColl(request, response); 1.804 + } 1.805 + server.registerPathHandler(INFO_COLLECTIONS, infoCollWithBackoff); 1.806 + 1.807 + // Pretend we have two clients so that the regular sync interval is 1.808 + // sufficiently low. 1.809 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.810 + let rec = clientsEngine._store.createRecord("foo", "clients"); 1.811 + rec.encrypt(Service.collectionKeys.keyForCollection("clients")); 1.812 + rec.upload(Service.resource(clientsEngine.engineURL + rec.id)); 1.813 + 1.814 + // Sync once to log in and get everything set up. Let's verify our initial 1.815 + // values. 1.816 + Service.sync(); 1.817 + do_check_eq(Status.backoffInterval, 0); 1.818 + do_check_eq(Status.minimumNextSync, 0); 1.819 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.820 + do_check_true(scheduler.nextSync <= 1.821 + Date.now() + scheduler.syncInterval); 1.822 + // Sanity check that we picked the right value for BACKOFF: 1.823 + do_check_true(scheduler.syncInterval < BACKOFF * 1000); 1.824 + 1.825 + // Turn on server maintenance and sync again. 1.826 + serverBackoff = true; 1.827 + Service.sync(); 1.828 + 1.829 + do_check_true(Status.backoffInterval >= BACKOFF * 1000); 1.830 + // Allowing 1 second worth of of leeway between when Status.minimumNextSync 1.831 + // was set and when this line gets executed. 1.832 + let minimumExpectedDelay = (BACKOFF - 1) * 1000; 1.833 + do_check_true(Status.minimumNextSync >= Date.now() + minimumExpectedDelay); 1.834 + 1.835 + // Verify that the next sync is actually going to wait that long. 1.836 + do_check_true(scheduler.nextSync >= Date.now() + minimumExpectedDelay); 1.837 + do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay); 1.838 + 1.839 + yield cleanUpAndGo(server); 1.840 +}); 1.841 + 1.842 +add_identity_test(this, function test_sync_503_Retry_After() { 1.843 + let server = sync_httpd_setup(); 1.844 + yield setUp(server); 1.845 + 1.846 + // Use an odd value on purpose so that it doesn't happen to coincide with one 1.847 + // of the sync intervals. 1.848 + const BACKOFF = 7337; 1.849 + 1.850 + // Extend info/collections so that we can put it into server maintenance mode. 1.851 + const INFO_COLLECTIONS = "/1.1/johndoe/info/collections"; 1.852 + let infoColl = server._handler._overridePaths[INFO_COLLECTIONS]; 1.853 + let serverMaintenance = false; 1.854 + function infoCollWithMaintenance(request, response) { 1.855 + if (!serverMaintenance) { 1.856 + infoColl(request, response); 1.857 + return; 1.858 + } 1.859 + response.setHeader("Retry-After", "" + BACKOFF); 1.860 + response.setStatusLine(request.httpVersion, 503, "Service Unavailable"); 1.861 + } 1.862 + server.registerPathHandler(INFO_COLLECTIONS, infoCollWithMaintenance); 1.863 + 1.864 + // Pretend we have two clients so that the regular sync interval is 1.865 + // sufficiently low. 1.866 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.867 + let rec = clientsEngine._store.createRecord("foo", "clients"); 1.868 + rec.encrypt(Service.collectionKeys.keyForCollection("clients")); 1.869 + rec.upload(Service.resource(clientsEngine.engineURL + rec.id)); 1.870 + 1.871 + // Sync once to log in and get everything set up. Let's verify our initial 1.872 + // values. 1.873 + Service.sync(); 1.874 + do_check_false(Status.enforceBackoff); 1.875 + do_check_eq(Status.backoffInterval, 0); 1.876 + do_check_eq(Status.minimumNextSync, 0); 1.877 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.878 + do_check_true(scheduler.nextSync <= 1.879 + Date.now() + scheduler.syncInterval); 1.880 + // Sanity check that we picked the right value for BACKOFF: 1.881 + do_check_true(scheduler.syncInterval < BACKOFF * 1000); 1.882 + 1.883 + // Turn on server maintenance and sync again. 1.884 + serverMaintenance = true; 1.885 + Service.sync(); 1.886 + 1.887 + do_check_true(Status.enforceBackoff); 1.888 + do_check_true(Status.backoffInterval >= BACKOFF * 1000); 1.889 + // Allowing 1 second worth of of leeway between when Status.minimumNextSync 1.890 + // was set and when this line gets executed. 1.891 + let minimumExpectedDelay = (BACKOFF - 1) * 1000; 1.892 + do_check_true(Status.minimumNextSync >= Date.now() + minimumExpectedDelay); 1.893 + 1.894 + // Verify that the next sync is actually going to wait that long. 1.895 + do_check_true(scheduler.nextSync >= Date.now() + minimumExpectedDelay); 1.896 + do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay); 1.897 + 1.898 + yield cleanUpAndGo(server); 1.899 +}); 1.900 + 1.901 +add_identity_test(this, function test_loginError_recoverable_reschedules() { 1.902 + _("Verify that a recoverable login error schedules a new sync."); 1.903 + yield configureIdentity({username: "johndoe"}); 1.904 + Service.serverURL = "http://localhost:1234/"; 1.905 + Service.clusterURL = Service.serverURL; 1.906 + Service.persistLogin(); 1.907 + Status.resetSync(); // reset Status.login 1.908 + 1.909 + let deferred = Promise.defer(); 1.910 + Svc.Obs.add("weave:service:login:error", function onLoginError() { 1.911 + Svc.Obs.remove("weave:service:login:error", onLoginError); 1.912 + Utils.nextTick(function aLittleBitAfterLoginError() { 1.913 + do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR); 1.914 + 1.915 + let expectedNextSync = Date.now() + scheduler.syncInterval; 1.916 + do_check_true(scheduler.nextSync > Date.now()); 1.917 + do_check_true(scheduler.nextSync <= expectedNextSync); 1.918 + do_check_true(scheduler.syncTimer.delay > 0); 1.919 + do_check_true(scheduler.syncTimer.delay <= scheduler.syncInterval); 1.920 + 1.921 + Svc.Obs.remove("weave:service:sync:start", onSyncStart); 1.922 + cleanUpAndGo().then(deferred.resolve); 1.923 + }); 1.924 + }); 1.925 + 1.926 + // Let's set it up so that a sync is overdue, both in terms of previously 1.927 + // scheduled syncs and the global score. We still do not expect an immediate 1.928 + // sync because we just tried (duh). 1.929 + scheduler.nextSync = Date.now() - 100000; 1.930 + scheduler.globalScore = SINGLE_USER_THRESHOLD + 1; 1.931 + function onSyncStart() { 1.932 + do_throw("Shouldn't have started a sync!"); 1.933 + } 1.934 + Svc.Obs.add("weave:service:sync:start", onSyncStart); 1.935 + 1.936 + // Sanity check. 1.937 + do_check_eq(scheduler.syncTimer, null); 1.938 + do_check_eq(Status.checkSetup(), STATUS_OK); 1.939 + do_check_eq(Status.login, LOGIN_SUCCEEDED); 1.940 + 1.941 + scheduler.scheduleNextSync(0); 1.942 + yield deferred.promise; 1.943 +}); 1.944 + 1.945 +add_identity_test(this, function test_loginError_fatal_clearsTriggers() { 1.946 + _("Verify that a fatal login error clears sync triggers."); 1.947 + yield configureIdentity({username: "johndoe"}); 1.948 + 1.949 + let server = httpd_setup({ 1.950 + "/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized") 1.951 + }); 1.952 + 1.953 + Service.serverURL = server.baseURI + "/"; 1.954 + Service.clusterURL = Service.serverURL; 1.955 + Service.persistLogin(); 1.956 + Status.resetSync(); // reset Status.login 1.957 + 1.958 + let deferred = Promise.defer(); 1.959 + Svc.Obs.add("weave:service:login:error", function onLoginError() { 1.960 + Svc.Obs.remove("weave:service:login:error", onLoginError); 1.961 + Utils.nextTick(function aLittleBitAfterLoginError() { 1.962 + do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED); 1.963 + 1.964 + do_check_eq(scheduler.nextSync, 0); 1.965 + do_check_eq(scheduler.syncTimer, null); 1.966 + 1.967 + cleanUpAndGo(server).then(deferred.resolve); 1.968 + }); 1.969 + }); 1.970 + 1.971 + // Sanity check. 1.972 + do_check_eq(scheduler.nextSync, 0); 1.973 + do_check_eq(scheduler.syncTimer, null); 1.974 + do_check_eq(Status.checkSetup(), STATUS_OK); 1.975 + do_check_eq(Status.login, LOGIN_SUCCEEDED); 1.976 + 1.977 + scheduler.scheduleNextSync(0); 1.978 + yield deferred.promise; 1.979 +}); 1.980 + 1.981 +add_identity_test(this, function test_proper_interval_on_only_failing() { 1.982 + _("Ensure proper behavior when only failed records are applied."); 1.983 + 1.984 + // If an engine reports that no records succeeded, we shouldn't decrease the 1.985 + // sync interval. 1.986 + do_check_false(scheduler.hasIncomingItems); 1.987 + const INTERVAL = 10000000; 1.988 + scheduler.syncInterval = INTERVAL; 1.989 + 1.990 + Svc.Obs.notify("weave:service:sync:applied", { 1.991 + applied: 2, 1.992 + succeeded: 0, 1.993 + failed: 2, 1.994 + newFailed: 2, 1.995 + reconciled: 0 1.996 + }); 1.997 + 1.998 + let deferred = Promise.defer(); 1.999 + Utils.nextTick(function() { 1.1000 + scheduler.adjustSyncInterval(); 1.1001 + do_check_false(scheduler.hasIncomingItems); 1.1002 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.1003 + 1.1004 + deferred.resolve(); 1.1005 + }); 1.1006 + yield deferred.promise; 1.1007 +});