1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tests/unit/test_interval_triggers.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,445 @@ 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/util.js"); 1.11 +Cu.import("resource://testing-common/services/sync/utils.js"); 1.12 + 1.13 +Svc.DefaultPrefs.set("registerEngines", ""); 1.14 +Cu.import("resource://services-sync/service.js"); 1.15 + 1.16 +let scheduler = Service.scheduler; 1.17 +let clientsEngine = Service.clientsEngine; 1.18 + 1.19 +function promiseStopServer(server) { 1.20 + let deferred = Promise.defer(); 1.21 + server.stop(deferred.resolve); 1.22 + return deferred.promise; 1.23 +} 1.24 + 1.25 +function sync_httpd_setup() { 1.26 + let global = new ServerWBO("global", { 1.27 + syncID: Service.syncID, 1.28 + storageVersion: STORAGE_VERSION, 1.29 + engines: {clients: {version: clientsEngine.version, 1.30 + syncID: clientsEngine.syncID}} 1.31 + }); 1.32 + let clientsColl = new ServerCollection({}, true); 1.33 + 1.34 + // Tracking info/collections. 1.35 + let collectionsHelper = track_collections_helper(); 1.36 + let upd = collectionsHelper.with_updated_collection; 1.37 + 1.38 + return httpd_setup({ 1.39 + "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()), 1.40 + "/1.1/johndoe/info/collections": collectionsHelper.handler, 1.41 + "/1.1/johndoe/storage/crypto/keys": 1.42 + upd("crypto", (new ServerWBO("keys")).handler()), 1.43 + "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()) 1.44 + }); 1.45 +} 1.46 + 1.47 +function setUp(server) { 1.48 + yield configureIdentity({username: "johndoe"}); 1.49 + Service.serverURL = server.baseURI + "/"; 1.50 + Service.clusterURL = server.baseURI + "/"; 1.51 + generateNewKeys(Service.collectionKeys); 1.52 + let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 1.53 + serverKeys.encrypt(Service.identity.syncKeyBundle); 1.54 + serverKeys.upload(Service.resource(Service.cryptoKeysURL)); 1.55 +} 1.56 + 1.57 +function run_test() { 1.58 + initTestLogging("Trace"); 1.59 + 1.60 + Log.repository.getLogger("Sync.Service").level = Log.Level.Trace; 1.61 + Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace; 1.62 + 1.63 + run_next_test(); 1.64 +} 1.65 + 1.66 +add_identity_test(this, function test_successful_sync_adjustSyncInterval() { 1.67 + _("Test successful sync calling adjustSyncInterval"); 1.68 + let syncSuccesses = 0; 1.69 + function onSyncFinish() { 1.70 + _("Sync success."); 1.71 + syncSuccesses++; 1.72 + }; 1.73 + Svc.Obs.add("weave:service:sync:finish", onSyncFinish); 1.74 + 1.75 + let server = sync_httpd_setup(); 1.76 + yield setUp(server); 1.77 + 1.78 + // Confirm defaults 1.79 + do_check_false(scheduler.idle); 1.80 + do_check_false(scheduler.numClients > 1); 1.81 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.82 + do_check_false(scheduler.hasIncomingItems); 1.83 + 1.84 + _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); 1.85 + // idle == true && numClients <= 1 && hasIncomingItems == false 1.86 + scheduler.idle = true; 1.87 + Service.sync(); 1.88 + do_check_eq(syncSuccesses, 1); 1.89 + do_check_true(scheduler.idle); 1.90 + do_check_false(scheduler.numClients > 1); 1.91 + do_check_false(scheduler.hasIncomingItems); 1.92 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.93 + 1.94 + // idle == false && numClients <= 1 && hasIncomingItems == false 1.95 + scheduler.idle = false; 1.96 + Service.sync(); 1.97 + do_check_eq(syncSuccesses, 2); 1.98 + do_check_false(scheduler.idle); 1.99 + do_check_false(scheduler.numClients > 1); 1.100 + do_check_false(scheduler.hasIncomingItems); 1.101 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.102 + 1.103 + // idle == false && numClients <= 1 && hasIncomingItems == true 1.104 + scheduler.hasIncomingItems = true; 1.105 + Service.sync(); 1.106 + do_check_eq(syncSuccesses, 3); 1.107 + do_check_false(scheduler.idle); 1.108 + do_check_false(scheduler.numClients > 1); 1.109 + do_check_true(scheduler.hasIncomingItems); 1.110 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.111 + 1.112 + // idle == true && numClients <= 1 && hasIncomingItems == true 1.113 + scheduler.idle = true; 1.114 + Service.sync(); 1.115 + do_check_eq(syncSuccesses, 4); 1.116 + do_check_true(scheduler.idle); 1.117 + do_check_false(scheduler.numClients > 1); 1.118 + do_check_true(scheduler.hasIncomingItems); 1.119 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.120 + 1.121 + _("Test as long as idle && numClients > 1 our sync interval is idleInterval."); 1.122 + // idle == true && numClients > 1 && hasIncomingItems == true 1.123 + Service.clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.124 + Service.sync(); 1.125 + do_check_eq(syncSuccesses, 5); 1.126 + do_check_true(scheduler.idle); 1.127 + do_check_true(scheduler.numClients > 1); 1.128 + do_check_true(scheduler.hasIncomingItems); 1.129 + do_check_eq(scheduler.syncInterval, scheduler.idleInterval); 1.130 + 1.131 + // idle == true && numClients > 1 && hasIncomingItems == false 1.132 + scheduler.hasIncomingItems = false; 1.133 + Service.sync(); 1.134 + do_check_eq(syncSuccesses, 6); 1.135 + do_check_true(scheduler.idle); 1.136 + do_check_true(scheduler.numClients > 1); 1.137 + do_check_false(scheduler.hasIncomingItems); 1.138 + do_check_eq(scheduler.syncInterval, scheduler.idleInterval); 1.139 + 1.140 + _("Test non-idle, numClients > 1, no incoming items => activeInterval."); 1.141 + // idle == false && numClients > 1 && hasIncomingItems == false 1.142 + scheduler.idle = false; 1.143 + Service.sync(); 1.144 + do_check_eq(syncSuccesses, 7); 1.145 + do_check_false(scheduler.idle); 1.146 + do_check_true(scheduler.numClients > 1); 1.147 + do_check_false(scheduler.hasIncomingItems); 1.148 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.149 + 1.150 + _("Test non-idle, numClients > 1, incoming items => immediateInterval."); 1.151 + // idle == false && numClients > 1 && hasIncomingItems == true 1.152 + scheduler.hasIncomingItems = true; 1.153 + Service.sync(); 1.154 + do_check_eq(syncSuccesses, 8); 1.155 + do_check_false(scheduler.idle); 1.156 + do_check_true(scheduler.numClients > 1); 1.157 + do_check_false(scheduler.hasIncomingItems); //gets reset to false 1.158 + do_check_eq(scheduler.syncInterval, scheduler.immediateInterval); 1.159 + 1.160 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.161 + Service.startOver(); 1.162 + yield promiseStopServer(server); 1.163 +}); 1.164 + 1.165 +add_identity_test(this, function test_unsuccessful_sync_adjustSyncInterval() { 1.166 + _("Test unsuccessful sync calling adjustSyncInterval"); 1.167 + 1.168 + let syncFailures = 0; 1.169 + function onSyncError() { 1.170 + _("Sync error."); 1.171 + syncFailures++; 1.172 + } 1.173 + Svc.Obs.add("weave:service:sync:error", onSyncError); 1.174 + 1.175 + _("Test unsuccessful sync calls adjustSyncInterval"); 1.176 + // Force sync to fail. 1.177 + Svc.Prefs.set("firstSync", "notReady"); 1.178 + 1.179 + let server = sync_httpd_setup(); 1.180 + yield setUp(server); 1.181 + 1.182 + // Confirm defaults 1.183 + do_check_false(scheduler.idle); 1.184 + do_check_false(scheduler.numClients > 1); 1.185 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.186 + do_check_false(scheduler.hasIncomingItems); 1.187 + 1.188 + _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); 1.189 + // idle == true && numClients <= 1 && hasIncomingItems == false 1.190 + scheduler.idle = true; 1.191 + Service.sync(); 1.192 + do_check_eq(syncFailures, 1); 1.193 + do_check_true(scheduler.idle); 1.194 + do_check_false(scheduler.numClients > 1); 1.195 + do_check_false(scheduler.hasIncomingItems); 1.196 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.197 + 1.198 + // idle == false && numClients <= 1 && hasIncomingItems == false 1.199 + scheduler.idle = false; 1.200 + Service.sync(); 1.201 + do_check_eq(syncFailures, 2); 1.202 + do_check_false(scheduler.idle); 1.203 + do_check_false(scheduler.numClients > 1); 1.204 + do_check_false(scheduler.hasIncomingItems); 1.205 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.206 + 1.207 + // idle == false && numClients <= 1 && hasIncomingItems == true 1.208 + scheduler.hasIncomingItems = true; 1.209 + Service.sync(); 1.210 + do_check_eq(syncFailures, 3); 1.211 + do_check_false(scheduler.idle); 1.212 + do_check_false(scheduler.numClients > 1); 1.213 + do_check_true(scheduler.hasIncomingItems); 1.214 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.215 + 1.216 + // idle == true && numClients <= 1 && hasIncomingItems == true 1.217 + scheduler.idle = true; 1.218 + Service.sync(); 1.219 + do_check_eq(syncFailures, 4); 1.220 + do_check_true(scheduler.idle); 1.221 + do_check_false(scheduler.numClients > 1); 1.222 + do_check_true(scheduler.hasIncomingItems); 1.223 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.224 + 1.225 + _("Test as long as idle && numClients > 1 our sync interval is idleInterval."); 1.226 + // idle == true && numClients > 1 && hasIncomingItems == true 1.227 + Service.clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.228 + 1.229 + Service.sync(); 1.230 + do_check_eq(syncFailures, 5); 1.231 + do_check_true(scheduler.idle); 1.232 + do_check_true(scheduler.numClients > 1); 1.233 + do_check_true(scheduler.hasIncomingItems); 1.234 + do_check_eq(scheduler.syncInterval, scheduler.idleInterval); 1.235 + 1.236 + // idle == true && numClients > 1 && hasIncomingItems == false 1.237 + scheduler.hasIncomingItems = false; 1.238 + Service.sync(); 1.239 + do_check_eq(syncFailures, 6); 1.240 + do_check_true(scheduler.idle); 1.241 + do_check_true(scheduler.numClients > 1); 1.242 + do_check_false(scheduler.hasIncomingItems); 1.243 + do_check_eq(scheduler.syncInterval, scheduler.idleInterval); 1.244 + 1.245 + _("Test non-idle, numClients > 1, no incoming items => activeInterval."); 1.246 + // idle == false && numClients > 1 && hasIncomingItems == false 1.247 + scheduler.idle = false; 1.248 + Service.sync(); 1.249 + do_check_eq(syncFailures, 7); 1.250 + do_check_false(scheduler.idle); 1.251 + do_check_true(scheduler.numClients > 1); 1.252 + do_check_false(scheduler.hasIncomingItems); 1.253 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.254 + 1.255 + _("Test non-idle, numClients > 1, incoming items => immediateInterval."); 1.256 + // idle == false && numClients > 1 && hasIncomingItems == true 1.257 + scheduler.hasIncomingItems = true; 1.258 + Service.sync(); 1.259 + do_check_eq(syncFailures, 8); 1.260 + do_check_false(scheduler.idle); 1.261 + do_check_true(scheduler.numClients > 1); 1.262 + do_check_false(scheduler.hasIncomingItems); //gets reset to false 1.263 + do_check_eq(scheduler.syncInterval, scheduler.immediateInterval); 1.264 + 1.265 + Service.startOver(); 1.266 + Svc.Obs.remove("weave:service:sync:error", onSyncError); 1.267 + yield promiseStopServer(server); 1.268 +}); 1.269 + 1.270 +add_identity_test(this, function test_back_triggers_sync() { 1.271 + let server = sync_httpd_setup(); 1.272 + yield setUp(server); 1.273 + 1.274 + // Single device: no sync triggered. 1.275 + scheduler.idle = true; 1.276 + scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); 1.277 + do_check_false(scheduler.idle); 1.278 + 1.279 + // Multiple devices: sync is triggered. 1.280 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.281 + scheduler.updateClientMode(); 1.282 + 1.283 + let deferred = Promise.defer(); 1.284 + Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() { 1.285 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.286 + 1.287 + Service.recordManager.clearCache(); 1.288 + Svc.Prefs.resetBranch(""); 1.289 + scheduler.setDefaults(); 1.290 + clientsEngine.resetClient(); 1.291 + 1.292 + Service.startOver(); 1.293 + server.stop(deferred.resolve); 1.294 + }); 1.295 + 1.296 + scheduler.idle = true; 1.297 + scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime")); 1.298 + do_check_false(scheduler.idle); 1.299 + yield deferred.promise; 1.300 +}); 1.301 + 1.302 +add_identity_test(this, function test_adjust_interval_on_sync_error() { 1.303 + let server = sync_httpd_setup(); 1.304 + yield setUp(server); 1.305 + 1.306 + let syncFailures = 0; 1.307 + function onSyncError() { 1.308 + _("Sync error."); 1.309 + syncFailures++; 1.310 + } 1.311 + Svc.Obs.add("weave:service:sync:error", onSyncError); 1.312 + 1.313 + _("Test unsuccessful sync updates client mode & sync intervals"); 1.314 + // Force a sync fail. 1.315 + Svc.Prefs.set("firstSync", "notReady"); 1.316 + 1.317 + do_check_eq(syncFailures, 0); 1.318 + do_check_false(scheduler.numClients > 1); 1.319 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.320 + 1.321 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.322 + Service.sync(); 1.323 + 1.324 + do_check_eq(syncFailures, 1); 1.325 + do_check_true(scheduler.numClients > 1); 1.326 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.327 + 1.328 + Svc.Obs.remove("weave:service:sync:error", onSyncError); 1.329 + Service.startOver(); 1.330 + yield promiseStopServer(server); 1.331 +}); 1.332 + 1.333 +add_identity_test(this, function test_bug671378_scenario() { 1.334 + // Test scenario similar to bug 671378. This bug appeared when a score 1.335 + // update occurred that wasn't large enough to trigger a sync so 1.336 + // scheduleNextSync() was called without a time interval parameter, 1.337 + // setting nextSync to a non-zero value and preventing the timer from 1.338 + // being adjusted in the next call to scheduleNextSync(). 1.339 + let server = sync_httpd_setup(); 1.340 + yield setUp(server); 1.341 + 1.342 + let syncSuccesses = 0; 1.343 + function onSyncFinish() { 1.344 + _("Sync success."); 1.345 + syncSuccesses++; 1.346 + }; 1.347 + Svc.Obs.add("weave:service:sync:finish", onSyncFinish); 1.348 + 1.349 + // After first sync call, syncInterval & syncTimer are singleDeviceInterval. 1.350 + Service.sync(); 1.351 + do_check_eq(syncSuccesses, 1); 1.352 + do_check_false(scheduler.numClients > 1); 1.353 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.354 + do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 1.355 + 1.356 + let deferred = Promise.defer(); 1.357 + // Wrap scheduleNextSync so we are notified when it is finished. 1.358 + scheduler._scheduleNextSync = scheduler.scheduleNextSync; 1.359 + scheduler.scheduleNextSync = function() { 1.360 + scheduler._scheduleNextSync(); 1.361 + 1.362 + // Check on sync:finish scheduleNextSync sets the appropriate 1.363 + // syncInterval and syncTimer values. 1.364 + if (syncSuccesses == 2) { 1.365 + do_check_neq(scheduler.nextSync, 0); 1.366 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.367 + do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); 1.368 + 1.369 + scheduler.scheduleNextSync = scheduler._scheduleNextSync; 1.370 + Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 1.371 + Service.startOver(); 1.372 + server.stop(deferred.resolve); 1.373 + } 1.374 + }; 1.375 + 1.376 + // Set nextSync != 0 1.377 + // syncInterval still hasn't been set by call to updateClientMode. 1.378 + // Explicitly trying to invoke scheduleNextSync during a sync 1.379 + // (to immitate a score update that isn't big enough to trigger a sync). 1.380 + Svc.Obs.add("weave:service:sync:start", function onSyncStart() { 1.381 + // Wait for other sync:start observers to be called so that 1.382 + // nextSync is set to 0. 1.383 + Utils.nextTick(function() { 1.384 + Svc.Obs.remove("weave:service:sync:start", onSyncStart); 1.385 + 1.386 + scheduler.scheduleNextSync(); 1.387 + do_check_neq(scheduler.nextSync, 0); 1.388 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.389 + do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 1.390 + }); 1.391 + }); 1.392 + 1.393 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.394 + Service.sync(); 1.395 + yield deferred.promise; 1.396 +}); 1.397 + 1.398 +add_test(function test_adjust_timer_larger_syncInterval() { 1.399 + _("Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used."); 1.400 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.401 + scheduler.updateClientMode(); 1.402 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.403 + 1.404 + scheduler.scheduleNextSync(); 1.405 + 1.406 + // Ensure we have a small interval. 1.407 + do_check_neq(scheduler.nextSync, 0); 1.408 + do_check_eq(scheduler.syncTimer.delay, scheduler.activeInterval); 1.409 + 1.410 + // Make interval large again 1.411 + clientsEngine._wipeClient(); 1.412 + scheduler.updateClientMode(); 1.413 + do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval); 1.414 + 1.415 + scheduler.scheduleNextSync(); 1.416 + 1.417 + // Ensure timer delay remains as the small interval. 1.418 + do_check_neq(scheduler.nextSync, 0); 1.419 + do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); 1.420 + 1.421 + //SyncSchedule. 1.422 + Service.startOver(); 1.423 + run_next_test(); 1.424 +}); 1.425 + 1.426 +add_test(function test_adjust_timer_smaller_syncInterval() { 1.427 + _("Test current timout > syncInterval period && nextSync != 0, syncInterval is used."); 1.428 + scheduler.scheduleNextSync(); 1.429 + 1.430 + // Ensure we have a large interval. 1.431 + do_check_neq(scheduler.nextSync, 0); 1.432 + do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 1.433 + 1.434 + // Make interval smaller 1.435 + clientsEngine._store.create({id: "foo", cleartext: "bar"}); 1.436 + scheduler.updateClientMode(); 1.437 + do_check_eq(scheduler.syncInterval, scheduler.activeInterval); 1.438 + 1.439 + scheduler.scheduleNextSync(); 1.440 + 1.441 + // Ensure smaller timer delay is used. 1.442 + do_check_neq(scheduler.nextSync, 0); 1.443 + do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval); 1.444 + 1.445 + //SyncSchedule. 1.446 + Service.startOver(); 1.447 + run_next_test(); 1.448 +});