test_interval_triggers.js (16183B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 Svc.PrefBranch.setStringPref("registerEngines", ""); 5 const { Service } = ChromeUtils.importESModule( 6 "resource://services-sync/service.sys.mjs" 7 ); 8 9 let scheduler; 10 let clientsEngine; 11 12 async function sync_httpd_setup() { 13 let clientsSyncID = await clientsEngine.resetLocalSyncID(); 14 let global = new ServerWBO("global", { 15 syncID: Service.syncID, 16 storageVersion: STORAGE_VERSION, 17 engines: { 18 clients: { version: clientsEngine.version, syncID: clientsSyncID }, 19 }, 20 }); 21 let clientsColl = new ServerCollection({}, true); 22 23 // Tracking info/collections. 24 let collectionsHelper = track_collections_helper(); 25 let upd = collectionsHelper.with_updated_collection; 26 27 return httpd_setup({ 28 "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()), 29 "/1.1/johndoe/info/collections": collectionsHelper.handler, 30 "/1.1/johndoe/storage/crypto/keys": upd( 31 "crypto", 32 new ServerWBO("keys").handler() 33 ), 34 "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()), 35 }); 36 } 37 38 async function setUp(server) { 39 syncTestLogging(); 40 await configureIdentity({ username: "johndoe" }, server); 41 await generateNewKeys(Service.collectionKeys); 42 let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 43 await serverKeys.encrypt(Service.identity.syncKeyBundle); 44 await serverKeys.upload(Service.resource(Service.cryptoKeysURL)); 45 } 46 47 add_task(async function setup() { 48 scheduler = Service.scheduler; 49 clientsEngine = Service.clientsEngine; 50 51 // Don't remove stale clients when syncing. This is a test-only workaround 52 // that lets us add clients directly to the store, without losing them on 53 // the next sync. 54 clientsEngine._removeRemoteClient = async () => {}; 55 }); 56 57 add_task(async function test_successful_sync_adjustSyncInterval() { 58 enableValidationPrefs(); 59 60 _("Test successful sync calling adjustSyncInterval"); 61 let syncSuccesses = 0; 62 function onSyncFinish() { 63 _("Sync success."); 64 syncSuccesses++; 65 } 66 Svc.Obs.add("weave:service:sync:finish", onSyncFinish); 67 68 let server = await sync_httpd_setup(); 69 await setUp(server); 70 71 // Confirm defaults 72 Assert.ok(!scheduler.idle); 73 Assert.equal(false, scheduler.numClients > 1); 74 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 75 Assert.ok(!scheduler.hasIncomingItems); 76 77 _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); 78 // idle == true && numClients <= 1 && hasIncomingItems == false 79 scheduler.idle = true; 80 await Service.sync(); 81 Assert.equal(syncSuccesses, 1); 82 Assert.ok(scheduler.idle); 83 Assert.equal(false, scheduler.numClients > 1); 84 Assert.ok(!scheduler.hasIncomingItems); 85 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 86 87 // idle == false && numClients <= 1 && hasIncomingItems == false 88 scheduler.idle = false; 89 await Service.sync(); 90 Assert.equal(syncSuccesses, 2); 91 Assert.ok(!scheduler.idle); 92 Assert.equal(false, scheduler.numClients > 1); 93 Assert.ok(!scheduler.hasIncomingItems); 94 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 95 96 // idle == false && numClients <= 1 && hasIncomingItems == true 97 scheduler.hasIncomingItems = true; 98 await Service.sync(); 99 Assert.equal(syncSuccesses, 3); 100 Assert.ok(!scheduler.idle); 101 Assert.equal(false, scheduler.numClients > 1); 102 Assert.ok(scheduler.hasIncomingItems); 103 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 104 105 // idle == true && numClients <= 1 && hasIncomingItems == true 106 scheduler.idle = true; 107 await Service.sync(); 108 Assert.equal(syncSuccesses, 4); 109 Assert.ok(scheduler.idle); 110 Assert.equal(false, scheduler.numClients > 1); 111 Assert.ok(scheduler.hasIncomingItems); 112 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 113 114 _( 115 "Test as long as idle && numClients > 1 our sync interval is idleInterval." 116 ); 117 // idle == true && numClients > 1 && hasIncomingItems == true 118 await Service.clientsEngine._store.create({ 119 id: "foo", 120 cleartext: { name: "bar", type: "mobile" }, 121 }); 122 await Service.sync(); 123 Assert.equal(syncSuccesses, 5); 124 Assert.ok(scheduler.idle); 125 Assert.greater(scheduler.numClients, 1); 126 Assert.ok(scheduler.hasIncomingItems); 127 Assert.equal(scheduler.syncInterval, scheduler.idleInterval); 128 129 // idle == true && numClients > 1 && hasIncomingItems == false 130 scheduler.hasIncomingItems = false; 131 await Service.sync(); 132 Assert.equal(syncSuccesses, 6); 133 Assert.ok(scheduler.idle); 134 Assert.greater(scheduler.numClients, 1); 135 Assert.ok(!scheduler.hasIncomingItems); 136 Assert.equal(scheduler.syncInterval, scheduler.idleInterval); 137 138 _("Test non-idle, numClients > 1, no incoming items => activeInterval."); 139 // idle == false && numClients > 1 && hasIncomingItems == false 140 scheduler.idle = false; 141 await Service.sync(); 142 Assert.equal(syncSuccesses, 7); 143 Assert.ok(!scheduler.idle); 144 Assert.greater(scheduler.numClients, 1); 145 Assert.ok(!scheduler.hasIncomingItems); 146 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 147 148 _("Test non-idle, numClients > 1, incoming items => immediateInterval."); 149 // idle == false && numClients > 1 && hasIncomingItems == true 150 scheduler.hasIncomingItems = true; 151 await Service.sync(); 152 Assert.equal(syncSuccesses, 8); 153 Assert.ok(!scheduler.idle); 154 Assert.greater(scheduler.numClients, 1); 155 Assert.ok(!scheduler.hasIncomingItems); // gets reset to false 156 Assert.equal(scheduler.syncInterval, scheduler.immediateInterval); 157 158 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 159 await Service.startOver(); 160 await promiseStopServer(server); 161 }); 162 163 add_task(async function test_unsuccessful_sync_adjustSyncInterval() { 164 enableValidationPrefs(); 165 166 _("Test unsuccessful sync calling adjustSyncInterval"); 167 168 let syncFailures = 0; 169 function onSyncError() { 170 _("Sync error."); 171 syncFailures++; 172 } 173 Svc.Obs.add("weave:service:sync:error", onSyncError); 174 175 _("Test unsuccessful sync calls adjustSyncInterval"); 176 // Force sync to fail. 177 Svc.PrefBranch.setStringPref("firstSync", "notReady"); 178 179 let server = await sync_httpd_setup(); 180 await setUp(server); 181 182 // Confirm defaults 183 Assert.ok(!scheduler.idle); 184 Assert.equal(false, scheduler.numClients > 1); 185 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 186 Assert.ok(!scheduler.hasIncomingItems); 187 188 _("Test as long as numClients <= 1 our sync interval is SINGLE_USER."); 189 // idle == true && numClients <= 1 && hasIncomingItems == false 190 scheduler.idle = true; 191 await Service.sync(); 192 Assert.equal(syncFailures, 1); 193 Assert.ok(scheduler.idle); 194 Assert.equal(false, scheduler.numClients > 1); 195 Assert.ok(!scheduler.hasIncomingItems); 196 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 197 198 // idle == false && numClients <= 1 && hasIncomingItems == false 199 scheduler.idle = false; 200 await Service.sync(); 201 Assert.equal(syncFailures, 2); 202 Assert.ok(!scheduler.idle); 203 Assert.equal(false, scheduler.numClients > 1); 204 Assert.ok(!scheduler.hasIncomingItems); 205 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 206 207 // idle == false && numClients <= 1 && hasIncomingItems == true 208 scheduler.hasIncomingItems = true; 209 await Service.sync(); 210 Assert.equal(syncFailures, 3); 211 Assert.ok(!scheduler.idle); 212 Assert.equal(false, scheduler.numClients > 1); 213 Assert.ok(scheduler.hasIncomingItems); 214 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 215 216 // idle == true && numClients <= 1 && hasIncomingItems == true 217 scheduler.idle = true; 218 await Service.sync(); 219 Assert.equal(syncFailures, 4); 220 Assert.ok(scheduler.idle); 221 Assert.equal(false, scheduler.numClients > 1); 222 Assert.ok(scheduler.hasIncomingItems); 223 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 224 225 _( 226 "Test as long as idle && numClients > 1 our sync interval is idleInterval." 227 ); 228 // idle == true && numClients > 1 && hasIncomingItems == true 229 Svc.PrefBranch.setIntPref("clients.devices.mobile", 2); 230 scheduler.updateClientMode(); 231 232 await Service.sync(); 233 Assert.equal(syncFailures, 5); 234 Assert.ok(scheduler.idle); 235 Assert.greater(scheduler.numClients, 1); 236 Assert.ok(scheduler.hasIncomingItems); 237 Assert.equal(scheduler.syncInterval, scheduler.idleInterval); 238 239 // idle == true && numClients > 1 && hasIncomingItems == false 240 scheduler.hasIncomingItems = false; 241 await Service.sync(); 242 Assert.equal(syncFailures, 6); 243 Assert.ok(scheduler.idle); 244 Assert.greater(scheduler.numClients, 1); 245 Assert.ok(!scheduler.hasIncomingItems); 246 Assert.equal(scheduler.syncInterval, scheduler.idleInterval); 247 248 _("Test non-idle, numClients > 1, no incoming items => activeInterval."); 249 // idle == false && numClients > 1 && hasIncomingItems == false 250 scheduler.idle = false; 251 await Service.sync(); 252 Assert.equal(syncFailures, 7); 253 Assert.ok(!scheduler.idle); 254 Assert.greater(scheduler.numClients, 1); 255 Assert.ok(!scheduler.hasIncomingItems); 256 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 257 258 _("Test non-idle, numClients > 1, incoming items => immediateInterval."); 259 // idle == false && numClients > 1 && hasIncomingItems == true 260 scheduler.hasIncomingItems = true; 261 await Service.sync(); 262 Assert.equal(syncFailures, 8); 263 Assert.ok(!scheduler.idle); 264 Assert.greater(scheduler.numClients, 1); 265 Assert.ok(!scheduler.hasIncomingItems); // gets reset to false 266 Assert.equal(scheduler.syncInterval, scheduler.immediateInterval); 267 268 await Service.startOver(); 269 Svc.Obs.remove("weave:service:sync:error", onSyncError); 270 await promiseStopServer(server); 271 }); 272 273 add_task(async function test_back_triggers_sync() { 274 enableValidationPrefs(); 275 276 let server = await sync_httpd_setup(); 277 await setUp(server); 278 279 // Single device: no sync triggered. 280 scheduler.idle = true; 281 scheduler.observe( 282 null, 283 "active", 284 Svc.PrefBranch.getIntPref("scheduler.idleTime") 285 ); 286 Assert.ok(!scheduler.idle); 287 288 // Multiple devices: sync is triggered. 289 Svc.PrefBranch.setIntPref("clients.devices.mobile", 2); 290 scheduler.updateClientMode(); 291 292 let promiseDone = promiseOneObserver("weave:service:sync:finish"); 293 294 scheduler.idle = true; 295 scheduler.observe( 296 null, 297 "active", 298 Svc.PrefBranch.getIntPref("scheduler.idleTime") 299 ); 300 Assert.ok(!scheduler.idle); 301 await promiseDone; 302 303 Service.recordManager.clearCache(); 304 for (const pref of Svc.PrefBranch.getChildList("")) { 305 Svc.PrefBranch.clearUserPref(pref); 306 } 307 scheduler.setDefaults(); 308 await clientsEngine.resetClient(); 309 310 await Service.startOver(); 311 await promiseStopServer(server); 312 }); 313 314 add_task(async function test_adjust_interval_on_sync_error() { 315 enableValidationPrefs(); 316 317 let server = await sync_httpd_setup(); 318 await setUp(server); 319 320 let syncFailures = 0; 321 function onSyncError() { 322 _("Sync error."); 323 syncFailures++; 324 } 325 Svc.Obs.add("weave:service:sync:error", onSyncError); 326 327 _("Test unsuccessful sync updates client mode & sync intervals"); 328 // Force a sync fail. 329 Svc.PrefBranch.setStringPref("firstSync", "notReady"); 330 331 Assert.equal(syncFailures, 0); 332 Assert.equal(false, scheduler.numClients > 1); 333 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 334 335 Svc.PrefBranch.setIntPref("clients.devices.mobile", 2); 336 await Service.sync(); 337 338 Assert.equal(syncFailures, 1); 339 Assert.greater(scheduler.numClients, 1); 340 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 341 342 Svc.Obs.remove("weave:service:sync:error", onSyncError); 343 await Service.startOver(); 344 await promiseStopServer(server); 345 }); 346 347 add_task(async function test_bug671378_scenario() { 348 enableValidationPrefs(); 349 350 // Test scenario similar to bug 671378. This bug appeared when a score 351 // update occurred that wasn't large enough to trigger a sync so 352 // scheduleNextSync() was called without a time interval parameter, 353 // setting nextSync to a non-zero value and preventing the timer from 354 // being adjusted in the next call to scheduleNextSync(). 355 let server = await sync_httpd_setup(); 356 await setUp(server); 357 358 let syncSuccesses = 0; 359 function onSyncFinish() { 360 _("Sync success."); 361 syncSuccesses++; 362 } 363 Svc.Obs.add("weave:service:sync:finish", onSyncFinish); 364 365 // After first sync call, syncInterval & syncTimer are singleDeviceInterval. 366 await Service.sync(); 367 Assert.equal(syncSuccesses, 1); 368 Assert.equal(false, scheduler.numClients > 1); 369 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 370 Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 371 372 let promiseDone = new Promise(resolve => { 373 // Wrap scheduleNextSync so we are notified when it is finished. 374 scheduler._scheduleNextSync = scheduler.scheduleNextSync; 375 scheduler.scheduleNextSync = function () { 376 scheduler._scheduleNextSync(); 377 378 // Check on sync:finish scheduleNextSync sets the appropriate 379 // syncInterval and syncTimer values. 380 if (syncSuccesses == 2) { 381 Assert.notEqual(scheduler.nextSync, 0); 382 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 383 Assert.lessOrEqual(scheduler.syncTimer.delay, scheduler.activeInterval); 384 385 scheduler.scheduleNextSync = scheduler._scheduleNextSync; 386 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish); 387 Service.startOver().then(() => { 388 server.stop(resolve); 389 }); 390 } 391 }; 392 }); 393 394 // Set nextSync != 0 395 // syncInterval still hasn't been set by call to updateClientMode. 396 // Explicitly trying to invoke scheduleNextSync during a sync 397 // (to immitate a score update that isn't big enough to trigger a sync). 398 Svc.Obs.add("weave:service:sync:start", function onSyncStart() { 399 // Wait for other sync:start observers to be called so that 400 // nextSync is set to 0. 401 CommonUtils.nextTick(function () { 402 Svc.Obs.remove("weave:service:sync:start", onSyncStart); 403 404 scheduler.scheduleNextSync(); 405 Assert.notEqual(scheduler.nextSync, 0); 406 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 407 Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 408 }); 409 }); 410 411 await Service.clientsEngine._store.create({ 412 id: "foo", 413 cleartext: { name: "bar", type: "mobile" }, 414 }); 415 await Service.sync(); 416 await promiseDone; 417 }); 418 419 add_task(async function test_adjust_timer_larger_syncInterval() { 420 _( 421 "Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used." 422 ); 423 Svc.PrefBranch.setIntPref("clients.devices.mobile", 2); 424 scheduler.updateClientMode(); 425 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 426 427 scheduler.scheduleNextSync(); 428 429 // Ensure we have a small interval. 430 Assert.notEqual(scheduler.nextSync, 0); 431 Assert.equal(scheduler.syncTimer.delay, scheduler.activeInterval); 432 433 // Make interval large again 434 await clientsEngine._wipeClient(); 435 Svc.PrefBranch.clearUserPref("clients.devices.mobile"); 436 scheduler.updateClientMode(); 437 Assert.equal(scheduler.syncInterval, scheduler.singleDeviceInterval); 438 439 scheduler.scheduleNextSync(); 440 441 // Ensure timer delay remains as the small interval. 442 Assert.notEqual(scheduler.nextSync, 0); 443 Assert.lessOrEqual(scheduler.syncTimer.delay, scheduler.activeInterval); 444 445 // SyncSchedule. 446 await Service.startOver(); 447 }); 448 449 add_task(async function test_adjust_timer_smaller_syncInterval() { 450 _( 451 "Test current timout > syncInterval period && nextSync != 0, syncInterval is used." 452 ); 453 scheduler.scheduleNextSync(); 454 455 // Ensure we have a large interval. 456 Assert.notEqual(scheduler.nextSync, 0); 457 Assert.equal(scheduler.syncTimer.delay, scheduler.singleDeviceInterval); 458 459 // Make interval smaller 460 Svc.PrefBranch.setIntPref("clients.devices.mobile", 2); 461 scheduler.updateClientMode(); 462 Assert.equal(scheduler.syncInterval, scheduler.activeInterval); 463 464 scheduler.scheduleNextSync(); 465 466 // Ensure smaller timer delay is used. 467 Assert.notEqual(scheduler.nextSync, 0); 468 Assert.lessOrEqual(scheduler.syncTimer.delay, scheduler.activeInterval); 469 470 // SyncSchedule. 471 await Service.startOver(); 472 });