| OLD | NEW |
| 1 <!DOCTYPE HTML> | 1 (function() |
| 2 <html> | 2 { |
| 3 <head> | 3 let testRunner = null; |
| 4 <title>Subscription synchronizer tests</title> | 4 let server = null; |
| 5 | 5 let randomResult = 0.5; |
| 6 <link rel="stylesheet" type="text/css" href="/content/tests/SimpleTest/test.cs
s" /> | 6 |
| 7 | 7 const MILLIS_IN_SECOND = 1000; |
| 8 <script type="text/javascript" src="/content/MochiKit/MochiKit.js"></script> | 8 const MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; |
| 9 <script type="application/x-javascript;version=1.7" src="../httpd.js"></script
> | 9 const MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; |
| 10 <script type="text/javascript; version=1.7" src="/content/tests/SimpleTest/spe
cialpowersAPI.js"></script> | 10 const MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; |
| 11 <script type="text/javascript; version=1.7" src="/content/tests/SimpleTest/Spe
cialPowersObserverAPI.js"></script> | 11 |
| 12 <script type="text/javascript; version=1.7" src="/content/tests/SimpleTest/Chr
omePowers.js"></script> | 12 module("Synchronizer", { |
| 13 <script type="text/javascript" src="/content/tests/SimpleTest/SimpleTest.js"><
/script> | 13 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRef
erence]), |
| 14 | 14 |
| 15 <script type="application/x-javascript;version=1.7" src="common.js"></script> | 15 setup: function() |
| 16 | 16 { |
| 17 </head> | 17 testRunner = this; |
| 18 <body> | 18 |
| 19 <p id="display"></p> | 19 prepareFilterComponents.call(this); |
| 20 <div id="content" style="display: none"> | 20 preparePrefs.call(this); |
| 21 | 21 |
| 22 </div> | 22 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); |
| 23 | 23 let SynchronizerModule = getModuleGlobal("synchronizer"); |
| 24 <pre id="test"> | 24 let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.downloader
); |
| 25 <script type="application/x-javascript;version=1.7"> | 25 |
| 26 let {Synchronizer} = require("synchronizer"); | 26 server = new nsHttpServer(); |
| 27 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 27 server.start(1234); |
| 28 prepareFilterComponents(); | 28 |
| 29 preparePrefs(); | 29 let currentTime = 100000 * MILLIS_IN_HOUR; |
| 30 | 30 let startTime = currentTime; |
| 31 let currentTime = 20000 * 24 * 60 * 60 * 1000; | 31 let scheduledTasks = []; |
| 32 let startTime = 0; | 32 |
| 33 let scheduledTasks = []; | 33 // Replace Date.now() function |
| 34 | 34 this._origNow = SynchronizerGlobal.Date.now; |
| 35 let oldRandom = SynchronizerGlobal.Math.random; | 35 SynchronizerGlobal.Date.now = DownloaderGlobal.Date.now = function() curre
ntTime; |
| 36 let oldNow = SynchronizerGlobal.Date.now; | 36 |
| 37 SynchronizerGlobal.Date.now = function() | 37 // Replace Math.random() function |
| 38 { | 38 this._origRandom = DownloaderGlobal.Math.random; |
| 39 return currentTime; | 39 DownloaderGlobal.Math.random = function() randomResult; |
| 40 }; | 40 |
| 41 Date.now = SynchronizerGlobal.Date.now; // Override for httpd Date header | 41 // Replace global timer variable |
| 42 | 42 let timer = {__proto__: SynchronizerModule.downloader._timer, delay: 0.1 *
MILLIS_IN_HOUR}; |
| 43 let outstandingRequests = 0; | |
| 44 | |
| 45 function runScheduledTasks(maxHours, noExecution) | |
| 46 { | |
| 47 startTime = currentTime; | |
| 48 let maxTime = maxHours * 60 * 60 * 1000; | |
| 49 let endTime = currentTime + maxTime; | |
| 50 while (true) | |
| 51 { | |
| 52 let nextTask = null; | |
| 53 for each (let task in scheduledTasks) | |
| 54 { | |
| 55 if (!nextTask || nextTask.nextExecution > task.nextExecution) | |
| 56 nextTask = task; | |
| 57 } | |
| 58 if (!nextTask || nextTask.nextExecution > endTime) | |
| 59 break; | |
| 60 | |
| 61 currentTime = nextTask.nextExecution; | |
| 62 if (!noExecution) | |
| 63 nextTask.handler(); | |
| 64 | |
| 65 // Let all asynchronous actions finish | |
| 66 let thread = Services.tm.currentThread; | |
| 67 let loopStartTime = Date.now(); | |
| 68 | |
| 69 while (outstandingRequests > 0 || thread.hasPendingEvents()) | |
| 70 { | |
| 71 thread.processNextEvent(true); | |
| 72 | |
| 73 if (Date.now() - loopStartTime > 5000) | |
| 74 { | |
| 75 ok(false, "Synchronizer stuck downloading subscriptions"); | |
| 76 return; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 if (nextTask.type == Components.interfaces.nsITimer.TYPE_ONE_SHOT) | |
| 81 scheduledTasks = scheduledTasks.filter(function(task) task != nextTask
); | |
| 82 else | |
| 83 nextTask.nextExecution = currentTime + nextTask.delay; | |
| 84 } | |
| 85 | |
| 86 currentTime = endTime; | |
| 87 } | |
| 88 | |
| 89 Prefs.subscriptions_fallbackerrors = 7; | |
| 90 Prefs.subscriptions_fallbackurl = "http://127.0.0.1:1234/fallback?%SUBSCRIPT
ION%&%URL%&%CHANNELSTATUS%&%RESPONSESTATUS%"; | |
| 91 | |
| 92 { | |
| 93 let timer = {__proto__: SynchronizerGlobal.timer}; | |
| 94 let callback = timer.callback; | 43 let callback = timer.callback; |
| 95 timer.handler = function() { callback.notify(timer); }; | 44 timer.handler = function() { callback.notify(timer); }; |
| 96 timer.nextExecution = currentTime + timer.delay; | 45 timer.nextExecution = currentTime + timer.delay; |
| 97 | |
| 98 scheduledTasks.push(timer); | 46 scheduledTasks.push(timer); |
| 99 | 47 SynchronizerModule.downloader._timer.cancel(); |
| 100 SynchronizerGlobal.timer.cancel(); | 48 SynchronizerModule.downloader._timer = timer; |
| 101 SynchronizerGlobal.timer = timer; | 49 |
| 102 } | 50 // Register observer to track outstanding requests |
| 103 | 51 this._outstandingRequests = 0; |
| 104 // Track requests initiated by Synchronizer object by hooking its | 52 Services.obs.addObserver(this, "http-on-modify-request", true); |
| 105 // XMLHttpRequest constructor. | 53 Services.obs.addObserver(this, "http-on-examine-response", true); |
| 106 let oldXMLHttp = SynchronizerGlobal.XMLHttpRequest; | 54 |
| 107 SynchronizerGlobal.XMLHttpRequest = function() | 55 this.runScheduledTasks = function(maxHours, initial, skip) |
| 108 { | 56 { |
| 109 let inner = new oldXMLHttp(); | 57 if (typeof maxHours != "number") |
| 110 | 58 throw new Error("Numerical parameter expected"); |
| 111 return { | 59 if (typeof initial != "number") |
| 112 __proto__: inner, | 60 initial = 0; |
| 113 send: function() | 61 if (typeof skip != "number") |
| 62 skip = 0; |
| 63 |
| 64 startTime = currentTime; |
| 65 if (initial >= 0) |
| 114 { | 66 { |
| 115 outstandingRequests++; | 67 this._runScheduledTasks(initial); |
| 116 function finished() | 68 maxHours -= initial; |
| 69 } |
| 70 if (skip) |
| 71 { |
| 72 this._skipTasks(skip); |
| 73 maxHours -= skip; |
| 74 } |
| 75 this._runScheduledTasks(maxHours); |
| 76 } |
| 77 |
| 78 this._runScheduledTasks = function(maxHours) |
| 79 { |
| 80 let endTime = currentTime + maxHours * MILLIS_IN_HOUR; |
| 81 while (true) |
| 82 { |
| 83 let nextTask = null; |
| 84 for each (let task in scheduledTasks) |
| 117 { | 85 { |
| 118 outstandingRequests--; | 86 if (!nextTask || nextTask.nextExecution > task.nextExecution) |
| 87 nextTask = task; |
| 119 } | 88 } |
| 120 inner.addEventListener("load", finished, false); | 89 if (!nextTask || nextTask.nextExecution > endTime) |
| 121 inner.addEventListener("error", finished, false); | 90 break; |
| 122 | 91 |
| 123 inner.send.apply(inner, arguments); | 92 currentTime = nextTask.nextExecution; |
| 93 nextTask.handler(); |
| 94 |
| 95 // Let all asynchronous actions finish |
| 96 let thread = Services.tm.currentThread; |
| 97 let loopStartTime = Date.now(); |
| 98 |
| 99 while (this._outstandingRequests > 0 || thread.hasPendingEvents()) |
| 100 { |
| 101 thread.processNextEvent(true); |
| 102 |
| 103 if (Date.now() - loopStartTime > 5000) |
| 104 throw new Error("Synchronizer stuck downloading subscriptions"); |
| 105 } |
| 106 |
| 107 if (nextTask.type == Components.interfaces.nsITimer.TYPE_ONE_SHOT) |
| 108 scheduledTasks = scheduledTasks.filter(function(task) task != nextTa
sk); |
| 109 else |
| 110 nextTask.nextExecution = currentTime + nextTask.delay; |
| 124 } | 111 } |
| 112 |
| 113 currentTime = endTime; |
| 125 } | 114 } |
| 126 } | 115 |
| 127 | 116 this._skipTasks = function(hours) |
| 128 // Make sure to restore everything when this document unloads | 117 { |
| 129 window.addEventListener("unload", function() | 118 let newTasks = []; |
| 130 { | 119 currentTime += hours * MILLIS_IN_HOUR; |
| 131 SynchronizerGlobal.Date.now = oldNow; | 120 for each (let task in scheduledTasks) |
| 132 SynchronizerGlobal.XMLHttpRequest = oldXMLHttp; | 121 { |
| 133 SynchronizerGlobal.Math.random = oldRandom; | 122 if (task.nextExecution >= currentTime) |
| 134 Synchronizer.startup(); | 123 newTasks.push(task); |
| 135 }, false); | 124 else if (task.type != Components.interfaces.nsITimer.TYPE_ONE_SHOT) |
| 136 | 125 { |
| 137 let server = new nsHttpServer(); | 126 task.nextExecution = currentTime; |
| 127 newTasks.push(task); |
| 128 } |
| 129 } |
| 130 scheduledTasks = newTasks; |
| 131 } |
| 132 |
| 133 this.getTimeOffset = function() (currentTime - startTime) / MILLIS_IN_HOUR
; |
| 134 |
| 135 this.__defineGetter__("currentTime", function() currentTime); |
| 136 }, |
| 137 |
| 138 observe: function(subject, topic, data) |
| 139 { |
| 140 let orig = this._outstandingRequests; |
| 141 if (topic == "http-on-modify-request") |
| 142 this._outstandingRequests++; |
| 143 else if (topic == "http-on-examine-response") |
| 144 this._outstandingRequests--; |
| 145 }, |
| 146 |
| 147 teardown: function() |
| 148 { |
| 149 restoreFilterComponents.call(this); |
| 150 restorePrefs.call(this); |
| 151 |
| 152 stop(); |
| 153 server.stop(function() |
| 154 { |
| 155 server = null; |
| 156 start(); |
| 157 }); |
| 158 |
| 159 if (this._origNow) |
| 160 { |
| 161 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); |
| 162 let SynchronizerModule = getModuleGlobal("synchronizer"); |
| 163 let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.download
er); |
| 164 SynchronizerGlobal.Date.now = DownloaderGlobal.Date.now = this._origNow; |
| 165 delete this._origNow; |
| 166 } |
| 167 |
| 168 if (this._origRandom) |
| 169 { |
| 170 let SynchronizerModule = getModuleGlobal("synchronizer"); |
| 171 let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.download
er); |
| 172 DownloaderGlobal.Math.random = this._origRandom; |
| 173 delete this._origRandom; |
| 174 } |
| 175 |
| 176 Synchronizer.init(); |
| 177 } |
| 178 }); |
| 179 |
| 180 function resetSubscription(subscription) |
| 181 { |
| 182 FilterStorage.updateSubscriptionFilters(subscription, []); |
| 183 subscription.lastCheck = subscription.lastDownload = |
| 184 subscription.version = subscription.lastSuccess = |
| 185 subscription.expires = subscription.softExpiration = 0; |
| 186 subscription.title = ""; |
| 187 subscription.homepage = null; |
| 188 subscription.errors = 0; |
| 189 subscription.downloadStatus = null; |
| 190 subscription.requiredVersion = null; |
| 191 } |
| 192 |
| 193 test("Downloads of one subscription", function() |
| 194 { |
| 195 // Always use average download interval |
| 196 randomResult = 0.5; |
| 197 |
| 198 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 199 FilterStorage.addSubscription(subscription); |
| 138 | 200 |
| 139 let requests = []; | 201 let requests = []; |
| 202 function handler(metadata, response) |
| 203 { |
| 204 requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
); |
| 205 |
| 206 response.setStatusLine("1.1", "200", "OK"); |
| 207 response.setHeader("Content-Type", "text/plain"); |
| 208 |
| 209 let result = "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"; |
| 210 response.bodyOutputStream.write(result, result.length); |
| 211 } |
| 212 |
| 213 server.registerPathHandler("/subscription", handler); |
| 214 |
| 215 testRunner.runScheduledTasks(50); |
| 216 deepEqual(requests, [ |
| 217 [0.1, "GET", "/subscription"], |
| 218 [24.1, "GET", "/subscription"], |
| 219 [48.1, "GET", "/subscription"], |
| 220 ], "Requests after 50 hours"); |
| 221 }); |
| 222 |
| 223 test("Downloads of two subscriptions", function() |
| 224 { |
| 225 // Always use average download interval |
| 226 randomResult = 0.5; |
| 140 | 227 |
| 141 let subscription1 = Subscription.fromURL("http://127.0.0.1:1234/subscription
1"); | 228 let subscription1 = Subscription.fromURL("http://127.0.0.1:1234/subscription
1"); |
| 229 FilterStorage.addSubscription(subscription1); |
| 230 |
| 142 let subscription2 = Subscription.fromURL("http://127.0.0.1:1234/subscription
2"); | 231 let subscription2 = Subscription.fromURL("http://127.0.0.1:1234/subscription
2"); |
| 143 let subscription3 = Subscription.fromURL("http://127.0.0.1:1234/subscription
3"); | 232 subscription2.expires = |
| 144 | 233 subscription2.softExpiration = |
| 145 let subscriptionStatus = [200, "OK"]; | 234 (testRunner.currentTime + 2 * MILLIS_IN_HOUR) / MILLIS_IN_SECOND; |
| 146 let subscriptionExtraHeaders = null; | 235 FilterStorage.addSubscription(subscription2); |
| 147 let subscriptionBody = "[Adblock]\nfoo\nbar"; | 236 |
| 148 function getSubscription(metadata, response) | 237 let requests = []; |
| 149 { | 238 function handler(metadata, response) |
| 150 requests.push((currentTime - startTime) / 3600000 + ": " + metadata.method
+ " " + metadata.path); | 239 { |
| 151 | 240 requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
); |
| 152 response.setStatusLine("1.1", subscriptionStatus[0], subscriptionStatus[1]
); | 241 |
| 153 // Return wrong MIME type, client should be able to handle it | 242 response.setStatusLine("1.1", "200", "OK"); |
| 243 response.setHeader("Content-Type", "text/plain"); |
| 244 |
| 245 let result = "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"; |
| 246 response.bodyOutputStream.write(result, result.length); |
| 247 } |
| 248 |
| 249 server.registerPathHandler("/subscription1", handler); |
| 250 server.registerPathHandler("/subscription2", handler); |
| 251 |
| 252 testRunner.runScheduledTasks(55); |
| 253 deepEqual(requests, [ |
| 254 [0.1, "GET", "/subscription1"], |
| 255 [2.1, "GET", "/subscription2"], |
| 256 [24.1, "GET", "/subscription1"], |
| 257 [26.1, "GET", "/subscription2"], |
| 258 [48.1, "GET", "/subscription1"], |
| 259 [50.1, "GET", "/subscription2"], |
| 260 ], "Requests after 55 hours"); |
| 261 }); |
| 262 |
| 263 test("Download result, various subscription headers", function() |
| 264 { |
| 265 // Always use average download interval |
| 266 randomResult = 0.5; |
| 267 |
| 268 let test; |
| 269 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 270 FilterStorage.addSubscription(subscription); |
| 271 |
| 272 function handler(metadata, response) |
| 273 { |
| 274 response.setStatusLine("1.1", "200", "OK"); |
| 275 |
| 276 // Wrong content type shouldn't matter |
| 154 response.setHeader("Content-Type", "text/xml"); | 277 response.setHeader("Content-Type", "text/xml"); |
| 155 | 278 |
| 156 if (subscriptionExtraHeaders) | 279 let result = test.header + "\n!Expires: 8 hours\nfoo\n!bar\n\n@@bas\n#bam"
; |
| 157 { | 280 response.bodyOutputStream.write(result, result.length); |
| 158 for each (let [header, value] in subscriptionExtraHeaders(metadata)) | 281 } |
| 159 response.setHeader(header, value); | 282 server.registerPathHandler("/subscription", handler); |
| 283 |
| 284 let tests = [ |
| 285 {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: n
ull}, |
| 286 {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersi
on: null}, |
| 287 {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", require
dVersion: null}, |
| 288 {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", require
dVersion: "0.0.1"}, |
| 289 {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", required
Version: "99.9"}, |
| 290 {header: "[Foo]", downloadStatus: "synchronize_invalid_data", requiredVers
ion: null} |
| 291 ]; |
| 292 for each (test in tests) |
| 293 { |
| 294 resetSubscription(subscription) |
| 295 testRunner.runScheduledTasks(2); |
| 296 |
| 297 equal(subscription.downloadStatus, test.downloadStatus, "Download status f
or " + test.header) |
| 298 equal(subscription.requiredVersion, test.requiredVersion, "Required versio
n for " + test.header) |
| 299 |
| 300 if (test.downloadStatus == "synchronize_ok") |
| 301 { |
| 302 deepEqual(subscription.filters, [ |
| 303 Filter.fromText("foo"), |
| 304 Filter.fromText("!bar"), |
| 305 Filter.fromText("@@bas"), |
| 306 Filter.fromText("#bam"), |
| 307 ], "Resulting subscription filters for " + test.header); |
| 160 } | 308 } |
| 309 else |
| 310 { |
| 311 deepEqual(subscription.filters, [ |
| 312 ], "Resulting subscription filters for " + test.header); |
| 313 } |
| 314 } |
| 315 }) |
| 316 |
| 317 test("Automatic updates disabled", function() |
| 318 { |
| 319 Prefs.subscriptions_autoupdate = false; |
| 320 |
| 321 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 322 FilterStorage.addSubscription(subscription); |
| 323 |
| 324 let requests = 0; |
| 325 function handler(metadata, response) |
| 326 { |
| 327 requests++; |
| 328 throw new Error("Unexpected request"); |
| 329 } |
| 330 |
| 331 server.registerPathHandler("/subscription", handler); |
| 332 |
| 333 testRunner.runScheduledTasks(50); |
| 334 equal(requests, 0, "Request count"); |
| 335 }); |
| 336 |
| 337 test("Expiration time", function() |
| 338 { |
| 339 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 340 FilterStorage.addSubscription(subscription); |
| 341 |
| 342 let test; |
| 343 let requests = []; |
| 344 function handler(metadata, response) |
| 345 { |
| 346 requests.push(testRunner.getTimeOffset()); |
| 347 |
| 348 response.setStatusLine("1.1", "200", "OK"); |
| 349 response.setHeader("Content-Type", "text/plain"); |
| 350 |
| 351 let result = "[Adblock]\nfoo\n!Expires: " + test.expiration + "\nbar"; |
| 352 response.bodyOutputStream.write(result, result.length); |
| 353 } |
| 354 server.registerPathHandler("/subscription", handler); |
| 355 |
| 356 let tests = [ |
| 357 { |
| 358 expiration: "default", |
| 359 randomResult: 0.5, |
| 360 requests: [0.1, 5 * 24 + 0.1] |
| 361 }, |
| 362 { |
| 363 expiration: "1 hours", // Minimal expiration interval |
| 364 randomResult: 0.5, |
| 365 requests: [0.1, 1.1, 2.1, 3.1] |
| 366 }, |
| 367 { |
| 368 expiration: "26 hours", |
| 369 randomResult: 0.5, |
| 370 requests: [0.1, 26.1] |
| 371 }, |
| 372 { |
| 373 expiration: "2 days", |
| 374 randomResult: 0.5, |
| 375 requests: [0.1, 48.1] |
| 376 }, |
| 377 { |
| 378 expiration: "20 days", // Too large, will be corrected |
| 379 randomResult: 0.5, |
| 380 requests: [0.1, 14 * 24 + 0.1] |
| 381 }, |
| 382 { |
| 383 expiration: "35 hours", |
| 384 randomResult: 0, // Changes interval by factor 0.8 |
| 385 requests: [0.1, 28.1] |
| 386 }, |
| 387 { |
| 388 expiration: "35 hours", |
| 389 randomResult: 1, // Changes interval by factor 1.2 |
| 390 requests: [0.1, 42.1] |
| 391 }, |
| 392 { |
| 393 expiration: "35 hours", |
| 394 randomResult: 0.25, // Changes interval by factor 0.9 |
| 395 requests: [0.1, 32.1] |
| 396 }, |
| 397 { |
| 398 expiration: "40 hours", |
| 399 randomResult: 0.5, |
| 400 skipAfter: 5.1, |
| 401 skip: 10, // Short break should not increase soft expirati
on |
| 402 requests: [0.1, 40.1] |
| 403 }, |
| 404 { |
| 405 expiration: "40 hours", |
| 406 randomResult: 0.5, |
| 407 skipAfter: 5.1, |
| 408 skip: 30, // Long break should increase soft expiration |
| 409 requests: [0.1, 70.1] |
| 410 }, |
| 411 { |
| 412 expiration: "40 hours", |
| 413 randomResult: 0.5, |
| 414 skipAfter: 5.1, |
| 415 skip: 80, // Hitting hard expiration, immediate download |
| 416 requests: [0.1, 85.1] |
| 417 } |
| 418 ] |
| 419 |
| 420 for each (test in tests) |
| 421 { |
| 422 requests = []; |
| 423 randomResult = test.randomResult; |
| 424 resetSubscription(subscription); |
| 425 |
| 426 let maxHours = Math.round(Math.max.apply(null, test.requests)) + 1; |
| 427 testRunner.runScheduledTasks(maxHours, test.skipAfter, test.skip); |
| 428 |
| 429 let randomAddendum = (randomResult == 0.5 ? "" : " with Math.random() retu
rning " + randomResult); |
| 430 let skipAddendum = (typeof test.skip != "number" ? "" : " skipping " + tes
t.skip + " hours after " + test.skipAfter + " hours"); |
| 431 deepEqual(requests, test.requests, "Requests for \"" + test.expiration + "
\"" + randomAddendum + skipAddendum); |
| 432 } |
| 433 }); |
| 434 |
| 435 test("Checksum verification", function() |
| 436 { |
| 437 // Always use average download interval |
| 438 randomResult = 0.5; |
| 439 |
| 440 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 441 FilterStorage.addSubscription(subscription); |
| 442 |
| 443 let testName, subscriptionBody, expectedResult; |
| 444 let tests = [ |
| 445 ["Correct checksum", "[Adblock]\n! Checksum: e/JCmqXny6Fn24b7JHsq/A\nfoo\n
bar\n", true], |
| 446 ["Wrong checksum", "[Adblock]\n! Checksum: wrongggny6Fn24b7JHsq/A\nfoo\nba
r\n", false], |
| 447 ["Empty lines ignored", "[Adblock]\n! Checksum: e/JCmqXny6Fn24b7JHsq/A\n\n
foo\n\nbar\n\n", true], |
| 448 ["CR LF line breaks treated like LR", "[Adblock]\n! Checksum: e/JCmqXny6Fn
24b7JHsq/A\nfoo\r\nbar\r\n", true], |
| 449 ["CR line breaks treated like LR", "[Adblock]\n! Checksum: e/JCmqXny6Fn24b
7JHsq/A\nfoo\rbar\r", true], |
| 450 ["Trailing line break not ignored", "[Adblock]\n! Checksum: e/JCmqXny6Fn24
b7JHsq/A\nfoo\nbar", false], |
| 451 ["Line breaks between lines not ignored", "[Adblock]\n! Checksum: e/JCmqXn
y6Fn24b7JHsq/A\nfoobar", false], |
| 452 ["Lines with spaces not ignored", "[Adblock]\n! Checksum: e/JCmqXny6Fn24b7
JHsq/A\n \nfoo\n\nbar\n", false], |
| 453 ["Extra content in checksum line is part of the checksum", "[Adblock]\n! C
hecksum: e/JCmqXny6Fn24b7JHsq/A foobar\nfoo\nbar\n", false], |
| 454 ["= symbols after checksum are ignored", "[Adblock]\n! Checksum: e/JCmqXny
6Fn24b7JHsq/A===\nfoo\nbar\n", true], |
| 455 ["Header line is part of the checksum", "[Adblock Plus]\n! Checksum: e/JCm
qXny6Fn24b7JHsq/A\nfoo\nbar\n", false], |
| 456 ["Special comments are part of the checksum", "[Adblock]\n! Checksum: e/JC
mqXny6Fn24b7JHsq/A\n! Expires: 1\nfoo\nbar\n", false], |
| 457 ]; |
| 458 |
| 459 function handler(metadata, response) |
| 460 { |
| 461 response.setStatusLine("1.1", "200", "OK"); |
| 462 response.setHeader("Content-Type", "text/plain"); |
| 161 | 463 |
| 162 response.bodyOutputStream.write(subscriptionBody, subscriptionBody.length)
; | 464 response.bodyOutputStream.write(subscriptionBody, subscriptionBody.length)
; |
| 163 } | 465 } |
| 164 | 466 server.registerPathHandler("/subscription", handler); |
| 165 let redirectPermanent = null; | 467 |
| 166 let redirectURL = null; | 468 for each ([testName, subscriptionBody, expectedResult] in tests) |
| 167 let redirectExtraHeaders = null; | 469 { |
| 168 function redirectHandler(metadata, response) | 470 resetSubscription(subscription); |
| 169 { | 471 testRunner.runScheduledTasks(2); |
| 170 response.setStatusLine("1.1", redirectPermanent ? 301 : 302, redirectPerma
nent ? "Moved Permanently" : "Moved Temporarily"); | 472 equal(subscription.downloadStatus, expectedResult ? "synchronize_ok" : "sy
nchronize_checksum_mismatch", testName); |
| 171 response.setHeader("Location", redirectURL); | 473 } |
| 172 | 474 }); |
| 173 if (redirectExtraHeaders) | 475 |
| 174 { | 476 test("Special comments", function() |
| 175 for each (let [header, value] in redirectExtraHeaders(metadata)) | 477 { |
| 176 response.setHeader(header, value); | 478 // Always use average download interval |
| 177 } | 479 randomResult = 0.5; |
| 178 } | 480 |
| 179 function commentRedirectHandler(metadata, response) | 481 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 180 { | 482 FilterStorage.addSubscription(subscription); |
| 181 getSubscription(metadata, response); | 483 |
| 182 | 484 let comment, check; |
| 183 if (redirectExtraHeaders) | 485 let tests = [ |
| 184 { | 486 ["! Homepage: http://example.com/", function() equal(subscription.homepage
, "http://example.com/", "Valid homepage comment")], |
| 185 for each (let [header, value] in redirectExtraHeaders(metadata)) | 487 ["! Homepage: ssh://example.com/", function() equal(subscription.homepage,
null, "Invalid homepage comment")], |
| 186 response.setHeader(header, value); | 488 ["! Title: foo", function() |
| 187 } | 489 { |
| 188 | 490 equal(subscription.title, "foo", "Title comment"); |
| 189 var comment = "\n! Redirect: " + redirectURL; | 491 equal(subscription.fixedTitle, true, "Fixed title"); |
| 190 response.bodyOutputStream.write(comment, comment.length); | 492 }], |
| 191 } | 493 ["! Version: 1234", function() equal(subscription.version, 1234, "Version
comment")] |
| 192 | 494 ]; |
| 193 var fallbackResult = ""; | 495 |
| 194 function fallbackHandler(metadata, response) | 496 function handler(metadata, response) |
| 195 { | 497 { |
| 196 requests.push((currentTime - startTime) / 3600000 + ": " + metadata.method
+ " " + metadata.path + " " + decodeURIComponent(metadata.queryString)); | 498 response.setStatusLine("1.1", "200", "OK"); |
| 197 | 499 response.setHeader("Content-Type", "text/plain"); |
| 198 response.setStatusLine("1.1", 200, "OK"); | 500 |
| 199 // Return wrong MIME type, client should be able to handle it | 501 let result = "[Adblock]\n" + comment + "\nfoo\nbar"; |
| 200 response.setHeader("Content-Type", "application/x-foo-bar"); | 502 response.bodyOutputStream.write(result, result.length); |
| 201 | 503 } |
| 202 if (subscriptionExtraHeaders) | 504 server.registerPathHandler("/subscription", handler); |
| 203 { | 505 |
| 204 for each (let [header, value] in subscriptionExtraHeaders()) | 506 for each([comment, check] in tests) |
| 205 response.setHeader(header, value); | 507 { |
| 206 } | 508 resetSubscription(subscription); |
| 207 | 509 testRunner.runScheduledTasks(2); |
| 208 response.bodyOutputStream.write(fallbackResult, fallbackResult.length); | 510 check(); |
| 209 } | 511 deepEqual(subscription.filters, [Filter.fromText("foo"), Filter.fromText("
bar")], "Special comment not added to filters"); |
| 210 | 512 } |
| 211 function compareRequests(test, expected) | 513 }); |
| 212 { | 514 |
| 213 is(requests.join("\n"), expected.join("\n"), test); | 515 test("Redirects", function() |
| 214 requests = []; | 516 { |
| 215 } | 517 // Always use average download interval |
| 216 | 518 randomResult = 0.5; |
| 217 function compareFilters(test, expected, expectedStatus, expectedVersion) | 519 |
| 218 { | 520 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 219 let result = subscription1.filters.map(function(filter) filter.text).join(
"\n"); | 521 FilterStorage.addSubscription(subscription); |
| 220 is(result, expected, test); | 522 |
| 221 is(subscription1.downloadStatus, expectedStatus, "Subscription status afte
r previous test"); | 523 function redirect_handler(metadata, response) |
| 222 is(subscription1.requiredVersion, expectedVersion, "Required version after
previous test"); | 524 { |
| 223 requests = []; | 525 response.setStatusLine("1.1", "200", "OK"); |
| 224 } | 526 response.setHeader("Content-Type", "text/plain"); |
| 225 | 527 |
| 226 function resetSubscriptions() | 528 let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/redirected\
nbar"; |
| 227 { | 529 response.bodyOutputStream.write(result, result.length); |
| 228 FilterStorage.removeSubscription(subscription1); | 530 } |
| 229 FilterStorage.removeSubscription(subscription2); | 531 server.registerPathHandler("/subscription", redirect_handler); |
| 230 FilterStorage.removeSubscription(subscription3); | 532 |
| 231 FilterStorage.addSubscription(subscription1); | 533 testRunner.runScheduledTasks(30); |
| 232 FilterStorage.addSubscription(subscription2); | 534 equal(FilterStorage.subscriptions[0], subscription, "Invalid redirect ignore
d"); |
| 233 subscription2.autoDownload = false; | 535 equal(subscription.downloadStatus, "synchronize_connection_error", "Connecti
on error recorded"); |
| 234 } | 536 equal(subscription.errors, 2, "Number of download errors"); |
| 235 | 537 |
| 236 function compareSubscriptions(test, expectedSubscriptions) | 538 let requests = []; |
| 237 { | 539 function handler(metadata, response) |
| 238 let result = FilterStorage.subscriptions.map(function(subscription) subscr
iption.url).join("\n"); | 540 { |
| 239 let expected = expectedSubscriptions.map(function(subscription) subscripti
on.url).join("\n"); | 541 requests.push(testRunner.getTimeOffset()); |
| 240 is(result, expected, test); | 542 |
| 241 requests = []; | 543 response.setStatusLine("1.1", "200", "OK"); |
| 242 resetSubscriptions(); | 544 response.setHeader("Content-Type", "text/plain"); |
| 243 } | 545 |
| 244 | 546 let result = "[Adblock]\nfoo\n! Expires: 8 hours\nbar"; |
| 245 function runTests() | 547 response.bodyOutputStream.write(result, result.length); |
| 246 { | 548 } |
| 247 is(typeof Synchronizer, "object", "typeof Synchronizer"); | 549 server.registerPathHandler("/redirected", handler); |
| 248 | 550 |
| 249 server.registerPathHandler("/subscription1", getSubscription); | 551 resetSubscription(subscription); |
| 250 server.registerPathHandler("/subscription2", getSubscription); | 552 testRunner.runScheduledTasks(15); |
| 251 server.registerPathHandler("/subscription3", getSubscription); | 553 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
, "Redirect followed"); |
| 252 server.registerPathHandler("/fallback", fallbackHandler); | 554 deepEqual(requests, [0.1, 8.1], "Resulting requests"); |
| 253 | 555 |
| 254 FilterStorage.addSubscription(subscription1); | 556 server.registerPathHandler("/redirected", function(metadata, response) |
| 255 | 557 { |
| 256 subscription2.autoDownload = false; | 558 response.setStatusLine("1.1", "200", "OK"); |
| 257 FilterStorage.addSubscription(subscription2); | 559 response.setHeader("Content-Type", "text/plain"); |
| 258 | 560 |
| 259 // | 561 let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/subscriptio
n\nbar"; |
| 260 // General subscription download testing | 562 response.bodyOutputStream.write(result, result.length); |
| 261 // | 563 }) |
| 262 | 564 |
| 263 SynchronizerGlobal.Math.random = function() 0.5; | 565 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 264 | 566 resetSubscription(subscription); |
| 265 runScheduledTasks(50); | 567 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); |
| 266 compareRequests("Downloads of one subscription (50 hours)", [ | 568 FilterStorage.addSubscription(subscription); |
| 267 "0.1: GET /subscription1", | 569 |
| 268 "24.1: GET /subscription1", | 570 testRunner.runScheduledTasks(2); |
| 269 "48.1: GET /subscription1" | 571 equal(FilterStorage.subscriptions[0], subscription, "Redirect not followed o
n redirect loop"); |
| 270 ]); | 572 equal(subscription.downloadStatus, "synchronize_connection_error", "Download
status after redirect loop"); |
| 271 | 573 }); |
| 272 subscription2.autoDownload = true; | 574 |
| 273 runScheduledTasks(70); | 575 test("Fallback", function() |
| 274 compareRequests("Downloads with second subscription switched on (48 hours)
", [ | 576 { |
| 275 "0.1: GET /subscription2", | 577 // Always use average download interval |
| 276 "22.1: GET /subscription1", | 578 randomResult = 0.5; |
| 277 "24.1: GET /subscription2", | 579 |
| 278 "46.1: GET /subscription1", | 580 Prefs.subscriptions_fallbackerrors = 3; |
| 279 "48.1: GET /subscription2" | 581 Prefs.subscriptions_fallbackurl = "http://127.0.0.1:1234/fallback?%SUBSCRIPT
ION%&%CHANNELSTATUS%&%RESPONSESTATUS%"; |
| 280 ]); | 582 |
| 281 subscription2.autoDownload = false; | 583 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 282 | 584 FilterStorage.addSubscription(subscription); |
| 283 // | 585 |
| 284 // Header variations testing | 586 // No valid response from fallback |
| 285 // | 587 |
| 286 | 588 let requests = []; |
| 287 subscriptionBody = "[Adblock]\nfoo\n!bar\n\n\n@@bas\n#bam"; | 589 function handler(metadata, response) |
| 288 runScheduledTasks(24); | 590 { |
| 289 compareFilters("Filters of downloaded subscription", "foo\n!bar\n@@bas\n#b
am", "synchronize_ok", null); | 591 requests.push(testRunner.getTimeOffset()); |
| 290 | 592 |
| 291 subscriptionBody = "[Adblock Plus]\nfoo2\n!bar2\n@@bas2\n#bam2"; | 593 response.setStatusLine("1.1", "404", "Not found"); |
| 292 runScheduledTasks(24); | 594 } |
| 293 compareFilters("Filters of downloaded subscription with [Adblock Plus] hea
der", "foo2\n!bar2\n@@bas2\n#bam2", "synchronize_ok", null); | 595 server.registerPathHandler("/subscription", handler); |
| 294 | 596 |
| 295 subscriptionBody = "[Adblock Plus 0.0.1]\nfoo3\n!bar3\n@@bas3\n#bam3"; | 597 testRunner.runScheduledTasks(100); |
| 296 runScheduledTasks(24); | 598 deepEqual(requests, [0.1, 24.1, 48.1, 72.1, 96.1], "Continue trying if the f
allback doesn't respond"); |
| 297 compareFilters("Filters of downloaded subscription with [Adblock Plus 0.0.
1] header", "foo3\n!bar3\n@@bas3\n#bam3", "synchronize_ok", "0.0.1"); | 599 |
| 298 | 600 // Fallback giving "Gone" response |
| 299 subscriptionBody = "(something)[Adblock]\nfoo4\n!bar4\n@@bas4\n#bam4"; | 601 |
| 300 runScheduledTasks(24); | 602 resetSubscription(subscription); |
| 301 compareFilters("Filters of downloaded subscription with (something)[Adbloc
k] header", "foo4\n!bar4\n@@bas4\n#bam4", "synchronize_ok", null); | 603 requests = []; |
| 302 | 604 fallbackParams = null; |
| 303 subscriptionBody = "[Foo]\nthis should not be accepted"; | 605 server.registerPathHandler("/fallback", function(metadata, response) |
| 304 runScheduledTasks(24); | 606 { |
| 305 compareFilters("Filters of downloaded subscription with [Foo] header", "fo
o4\n!bar4\n@@bas4\n#bam4", "synchronize_invalid_data", null); | 607 response.setStatusLine("1.1", "200", "OK"); |
| 306 | 608 fallbackParams = decodeURIComponent(metadata.queryString); |
| 307 subscriptionBody = "[Adblock Plus 99.9]\nsome_new_syntax"; | 609 |
| 308 runScheduledTasks(24); | 610 let result = "410 Gone"; |
| 309 compareFilters("Filters of downloaded subscription with [Adblock Plus 99.9
] header", "some_new_syntax", "synchronize_ok", "99.9"); | 611 response.bodyOutputStream.write(result, result.length); |
| 310 | |
| 311 // | |
| 312 // Expiration testing | |
| 313 // | |
| 314 | |
| 315 // Expiration time too small - should be changed into 24 hours | |
| 316 subscriptionBody = "[Adblock]\n! Expires after 1 hour\nfoo"; | |
| 317 runScheduledTasks(36); | |
| 318 compareRequests("Expiration comment with less than default update interval
(25 hours)", [ | |
| 319 "0.1: GET /subscription1", | |
| 320 "24.1: GET /subscription1" | |
| 321 ]); | |
| 322 | |
| 323 subscriptionBody = "[Adblock]\n! Expires after 26 hours\nfoo"; | |
| 324 runScheduledTasks(48); | |
| 325 compareRequests("Downloads with 'Expires after 26 hours' comment (48 hours
)", [ | |
| 326 "12.1: GET /subscription1", | |
| 327 "38.1: GET /subscription1" | |
| 328 ]); | |
| 329 | |
| 330 subscriptionBody = "[Adblock]\n! Expires: 2 days\nfoo"; | |
| 331 runScheduledTasks(70); | |
| 332 compareRequests("Downloads with 'Expires: 2 days' comment (70 hours)", [ | |
| 333 "16.1: GET /subscription1", | |
| 334 "64.1: GET /subscription1" | |
| 335 ]); | |
| 336 | |
| 337 subscriptionBody = "[Adblock]\nfoo"; | |
| 338 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
30 * 60 * 60 * 1000).toGMTString()]]; | |
| 339 runScheduledTasks(80); | |
| 340 compareRequests("Downloads with 'Expires: +30h' HTTP header (80 hours)", [ | |
| 341 "42.1: GET /subscription1", | |
| 342 "72.1: GET /subscription1" | |
| 343 ]); | |
| 344 | |
| 345 // Expiration time too small, should be changed into 24 hours | |
| 346 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
20 * 60 * 60 * 1000).toGMTString()]]; | |
| 347 runScheduledTasks(48); | |
| 348 compareRequests("Expiration header with less than default update interval
(48 hours)", [ | |
| 349 "22.1: GET /subscription1", | |
| 350 "46.1: GET /subscription1" | |
| 351 ]); | |
| 352 | |
| 353 // Expiration time too large, should be changed into 14 days | |
| 354 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
504 * 60 * 60 * 1000).toGMTString()]]; | |
| 355 runScheduledTasks(692); | |
| 356 compareRequests("Expiration header more than two weeks in future (692 hour
s)", [ | |
| 357 "22.1: GET /subscription1", | |
| 358 "358.1: GET /subscription1" | |
| 359 ]); | |
| 360 | |
| 361 // Soft expiration interval should be randomized - random returning 0 mean
s factor 0.8 | |
| 362 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
35 * 60 * 60 * 1000).toGMTString()]]; | |
| 363 SynchronizerGlobal.Math.random = function() 0; | |
| 364 runScheduledTasks(56); | |
| 365 compareRequests("Soft expiration should be multiplied with 0.8 if Math.ran
dom() returns 0 (48 hours)", [ | |
| 366 "2.1: GET /subscription1", | |
| 367 "30.1: GET /subscription1" | |
| 368 ]); | |
| 369 | |
| 370 // Soft expiration interval should be randomized - random returning 0.9 me
ans factor 1.16 | |
| 371 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
35 * 60 * 60 * 1000).toGMTString()]]; | |
| 372 SynchronizerGlobal.Math.random = function() 0.9; | |
| 373 runScheduledTasks(82); | |
| 374 compareRequests("Soft expiration should be multiplied with 1.16 if Math.ra
ndom() returns 0.9 (82 hours)", [ | |
| 375 "2.1: GET /subscription1", | |
| 376 "43.1: GET /subscription1" | |
| 377 ]); | |
| 378 SynchronizerGlobal.Math.random = function() 0.5; | |
| 379 | |
| 380 // Soft expiration interval should increase if the user is off-line more t
han a day | |
| 381 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
35 * 60 * 60 * 1000).toGMTString()]]; | |
| 382 runScheduledTasks(4); | |
| 383 requests = []; | |
| 384 runScheduledTasks(26, true); // Skip the next 26 hours | |
| 385 runScheduledTasks(104); | |
| 386 compareRequests("Soft expiration interval should increase if user is offli
ne for more than a day (104 hours)", [ | |
| 387 "34.1: GET /subscription1", | |
| 388 "69.1: GET /subscription1" | |
| 389 ]); | |
| 390 | |
| 391 // Soft expiration interval should *not* increase if the user was off-line
for a short period | |
| 392 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
35 * 60 * 60 * 1000).toGMTString()]]; | |
| 393 runScheduledTasks(2); | |
| 394 requests = []; | |
| 395 runScheduledTasks(10, true); // Skip the next 10 hours | |
| 396 runScheduledTasks(93); | |
| 397 compareRequests("Soft expiration interval should not increase if user is o
ffline for a few hours (93 hours)", [ | |
| 398 "23.1: GET /subscription1", | |
| 399 "58.1: GET /subscription1" | |
| 400 ]); | |
| 401 | |
| 402 // Hard expiration interval: if the user was away too long the download sh
ould happen immediately | |
| 403 subscriptionExtraHeaders = function() [["Expires", new Date(currentTime +
35 * 60 * 60 * 1000).toGMTString()]]; | |
| 404 runScheduledTasks(4); | |
| 405 requests = []; | |
| 406 runScheduledTasks(80, true); // Skip the next 80 hours, more than twice th
e expiration time | |
| 407 runScheduledTasks(70); | |
| 408 compareRequests("Download should happen immediately if hard expiration int
erval is hit (70 hours)", [ | |
| 409 "0.1: GET /subscription1", | |
| 410 "35.1: GET /subscription1" | |
| 411 ]); | |
| 412 | |
| 413 subscriptionExtraHeaders = null; | |
| 414 | |
| 415 // | |
| 416 // Redirect testing | |
| 417 // | |
| 418 | |
| 419 server.registerPathHandler("/subscription1", commentRedirectHandler); | |
| 420 | |
| 421 redirectURL = subscription2.url; | |
| 422 runScheduledTasks(48); | |
| 423 compareSubscriptions("Subscriptions after comment redirect to /subscriptio
n2", [subscription2]); | |
| 424 | |
| 425 redirectURL = subscription2.url.replace("subscription2", "invalid_url"); | |
| 426 runScheduledTasks(48); | |
| 427 compareSubscriptions("Subscriptions after redirect to /invalid_url", [subs
cription1, subscription2]); | |
| 428 | |
| 429 server.registerPathHandler("/subscription1", redirectHandler); | |
| 430 | |
| 431 redirectURL = subscription2.url; | |
| 432 redirectPermanent = false; | |
| 433 runScheduledTasks(48); | |
| 434 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion2", [subscription1, subscription2]); | |
| 435 | |
| 436 redirectPermanent = true; | |
| 437 runScheduledTasks(48); | |
| 438 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion2", [subscription2]); | |
| 439 | |
| 440 redirectURL = subscription3.url; | |
| 441 redirectPermanent = false; | |
| 442 runScheduledTasks(48); | |
| 443 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion3", [subscription1, subscription2]); | |
| 444 | |
| 445 redirectPermanent = true; | |
| 446 runScheduledTasks(48); | |
| 447 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion3", [subscription2, subscription3]); | |
| 448 | |
| 449 redirectURL = subscription2.url.replace("subscription2", "invalid_url"); | |
| 450 redirectPermanent = false; | |
| 451 runScheduledTasks(48); | |
| 452 compareSubscriptions("Subscriptions after temporary redirect to /invalid_u
rl", [subscription1, subscription2]); | |
| 453 | |
| 454 redirectPermanent = true; | |
| 455 runScheduledTasks(48); | |
| 456 compareSubscriptions("Subscriptions after permanent redirect to /invalid_u
rl", [subscription1, subscription2]); | |
| 457 | |
| 458 server.registerPathHandler("/subscription3", redirectHandler); | |
| 459 | |
| 460 server.registerPathHandler("/subscription1", function redirectHandler(meta
data, response) | |
| 461 { | |
| 462 response.setStatusLine("1.1", 302, "Moved Temporarily"); | |
| 463 response.setHeader("Location", subscription3.url); | |
| 464 }); | |
| 465 | |
| 466 redirectURL = subscription2.url; | |
| 467 redirectPermanent = false; | |
| 468 runScheduledTasks(48); | |
| 469 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion3 followed by temporary redirect to /subscription2", [subscription1, subscrip
tion2]); | |
| 470 | |
| 471 redirectPermanent = true; | |
| 472 runScheduledTasks(48); | |
| 473 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion3 followed by permanent redirect to /subscription2", [subscription1, subscrip
tion2]); | |
| 474 | |
| 475 redirectURL = subscription2.url.replace("subscription2", "invalid_url");; | |
| 476 redirectPermanent = false; | |
| 477 runScheduledTasks(48); | |
| 478 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion3 followed by temporary redirect to /invalid_url", [subscription1, subscripti
on2]); | |
| 479 | |
| 480 redirectPermanent = true; | |
| 481 runScheduledTasks(48); | |
| 482 compareSubscriptions("Subscriptions after temporary redirect to /subscript
ion3 followed by permanent redirect to /invalid_url", [subscription1, subscripti
on2]); | |
| 483 | |
| 484 server.registerPathHandler("/subscription1", function redirectHandler(meta
data, response) | |
| 485 { | |
| 486 response.setStatusLine("1.1", 301, "Moved Permanently"); | |
| 487 response.setHeader("Location", subscription3.url); | |
| 488 }); | |
| 489 | |
| 490 redirectURL = subscription2.url; | |
| 491 redirectPermanent = false; | |
| 492 runScheduledTasks(48); | |
| 493 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion3 followed by temporary redirect to /subscription2", [subscription2, subscrip
tion3]); | |
| 494 | |
| 495 redirectPermanent = true; | |
| 496 runScheduledTasks(48); | |
| 497 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion3 followed by permanent redirect to /subscription2", [subscription2]); | |
| 498 | |
| 499 redirectURL = subscription2.url.replace("subscription2", "invalid_url");; | |
| 500 redirectPermanent = false; | |
| 501 runScheduledTasks(48); | |
| 502 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion3 followed by temporary redirect to /invalid_url", [subscription1, subscripti
on2]); | |
| 503 | |
| 504 redirectPermanent = true; | |
| 505 runScheduledTasks(48); | |
| 506 compareSubscriptions("Subscriptions after permanent redirect to /subscript
ion3 followed by permanent redirect to /invalid_url", [subscription1, subscripti
on2]); | |
| 507 | |
| 508 server.registerPathHandler("/subscription1", getSubscription); | |
| 509 server.registerPathHandler("/subscription3", getSubscription); | |
| 510 | |
| 511 // | |
| 512 // Behavior on errors | |
| 513 // | |
| 514 | |
| 515 runScheduledTasks(48); // reset error counters | |
| 516 requests = []; | |
| 517 | |
| 518 subscriptionStatus = [404, "Not Found"]; | |
| 519 runScheduledTasks(72); | |
| 520 compareRequests("Requests after 404 error (72 hours)", [ | |
| 521 "0.1: GET /subscription1", | |
| 522 "24.1: GET /subscription1", | |
| 523 "48.1: GET /subscription1" | |
| 524 ]); | |
| 525 | |
| 526 subscriptionStatus = [200, "OK"]; | |
| 527 subscriptionBody = "Not a valid subscription"; | |
| 528 runScheduledTasks(72); | |
| 529 compareRequests("Requests for invalid subscription (72 hours)", [ | |
| 530 "0.1: GET /subscription1", | |
| 531 "24.1: GET /subscription1", | |
| 532 "48.1: GET /subscription1" | |
| 533 ]); | |
| 534 | |
| 535 server.registerPathHandler("/subscription1", function(metadata, response) | |
| 536 { | |
| 537 getSubscription(metadata, response); | |
| 538 response.setStatusLine("1.1", "404", "Not found"); | |
| 539 }); | |
| 540 subscriptionBody = "[Adblock]\nfoo\nbar"; | |
| 541 runScheduledTasks(216); | |
| 542 compareRequests("Requests with fallback calls (216 hours)", [ | |
| 543 "0.1: GET /subscription1", | |
| 544 "0.1: GET /fallback " + subscription1.url + "&" + subscription1.url + "&
0&404", | |
| 545 "24.1: GET /subscription1", | |
| 546 "48.1: GET /subscription1", | |
| 547 "72.1: GET /subscription1", | |
| 548 "96.1: GET /subscription1", | |
| 549 "120.1: GET /subscription1", | |
| 550 "144.1: GET /subscription1", | |
| 551 "168.1: GET /subscription1", | |
| 552 "168.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 553 "192.1: GET /subscription1" | |
| 554 ]); | |
| 555 | |
| 556 fallbackResult = "410 Gone"; | |
| 557 runScheduledTasks(216); | |
| 558 compareRequests("Requests with fallback returning 410 Gone (216 hours)", [ | |
| 559 "0.1: GET /subscription1", | |
| 560 "24.1: GET /subscription1", | |
| 561 "48.1: GET /subscription1", | |
| 562 "72.1: GET /subscription1", | |
| 563 "96.1: GET /subscription1", | |
| 564 "120.1: GET /subscription1", | |
| 565 "120.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 566 ]); | |
| 567 subscription1.autoDownload = true; | |
| 568 | |
| 569 fallbackResult = "301 " + subscription2.url; | |
| 570 runScheduledTasks(216); | |
| 571 compareRequests("Requests with fallback redirecting to /subscription2 (216
hours)", [ | |
| 572 "0.1: GET /subscription1", | |
| 573 "24.1: GET /subscription1", | |
| 574 "48.1: GET /subscription1", | |
| 575 "72.1: GET /subscription1", | |
| 576 "96.1: GET /subscription1", | |
| 577 "120.1: GET /subscription1", | |
| 578 "144.1: GET /subscription1", | |
| 579 "144.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 580 "168.1: GET /subscription2", | |
| 581 "192.1: GET /subscription2" | |
| 582 ]); | |
| 583 compareSubscriptions("Subscriptions after test above", [subscription2]); | |
| 584 subscription1.autoDownload = true; | |
| 585 | |
| 586 fallbackResult = "301 " + subscription3.url; | |
| 587 runScheduledTasks(216); | |
| 588 compareRequests("Requests with fallback redirecting to /subscription3 (216
hours)", [ | |
| 589 "0.1: GET /subscription1", | |
| 590 "24.1: GET /subscription1", | |
| 591 "48.1: GET /subscription1", | |
| 592 "72.1: GET /subscription1", | |
| 593 "96.1: GET /subscription1", | |
| 594 "120.1: GET /subscription1", | |
| 595 "144.1: GET /subscription1", | |
| 596 "144.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 597 "168.1: GET /subscription3", | |
| 598 "192.1: GET /subscription3" | |
| 599 ]); | |
| 600 compareSubscriptions("Subscriptions after test above", [subscription2, sub
scription3]); | |
| 601 subscription1.autoDownload = true; | |
| 602 | |
| 603 fallbackResult = "301 " + subscription2.url.replace("subscription2", "inva
lid_url"); | |
| 604 runScheduledTasks(384); | |
| 605 compareRequests("Requests with fallback redirecting to /invalid_url (384 h
ours)", [ | |
| 606 "0.1: GET /subscription1", | |
| 607 "24.1: GET /subscription1", | |
| 608 "48.1: GET /subscription1", | |
| 609 "72.1: GET /subscription1", | |
| 610 "96.1: GET /subscription1", | |
| 611 "120.1: GET /subscription1", | |
| 612 "144.1: GET /subscription1", | |
| 613 "144.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 614 "192.1: GET /subscription1", | |
| 615 "216.1: GET /subscription1", | |
| 616 "240.1: GET /subscription1", | |
| 617 "264.1: GET /subscription1", | |
| 618 "288.1: GET /subscription1", | |
| 619 "312.1: GET /subscription1", | |
| 620 "312.1: GET /fallback " + subscription1.url + "&" + subscription1.url +
"&0&404", | |
| 621 "360.1: GET /subscription1" | |
| 622 ]); | |
| 623 compareSubscriptions("Subscriptions after test above", [subscription1, sub
scription2]); | |
| 624 subscription1.autoDownload = true; | |
| 625 | |
| 626 server.registerPathHandler("/subscription1", getSubscription); | |
| 627 fallbackResult = ""; | |
| 628 | |
| 629 // | |
| 630 // Checksum verification | |
| 631 // | |
| 632 | |
| 633 subscriptionBody = "[Adblock]\n! Checksum: e/JCmqXny6Fn24b7JHsq/A\nfoo\nba
r\n"; | |
| 634 | |
| 635 runScheduledTasks(48); | |
| 636 is(subscription1.downloadStatus, "synchronize_ok", "Subscription download
with correct checksum succeeded"); | |
| 637 | |
| 638 subscriptionBody = subscriptionBody.replace(/Checksum: /, "$&wrong"); | |
| 639 runScheduledTasks(48); | |
| 640 is(subscription1.downloadStatus, "synchronize_checksum_mismatch", "Subscri
ption download with wrong checksum failed"); | |
| 641 subscriptionBody = subscriptionBody.replace(/wrong/, ""); | |
| 642 | |
| 643 subscriptionBody = subscriptionBody.replace(/\n/g, "\n\n"); | |
| 644 runScheduledTasks(48); | |
| 645 is(subscription1.downloadStatus, "synchronize_ok", "Empty lines are ignore
d for checksum validation"); | |
| 646 subscriptionBody = subscriptionBody.replace(/\n\n/g, "\n"); | |
| 647 | |
| 648 subscriptionBody = subscriptionBody.replace(/\n/g, "\n \n"); | |
| 649 runScheduledTasks(48); | |
| 650 is(subscription1.downloadStatus, "synchronize_checksum_mismatch", "Lines w
ith spaces are not ignored for checksum validation"); | |
| 651 subscriptionBody = subscriptionBody.replace(/\n \n/g, "\n"); | |
| 652 | |
| 653 subscriptionBody = subscriptionBody.replace(/(Checksum[^\r\n]*)/, "extra1
$& extra2"); | |
| 654 runScheduledTasks(48); | |
| 655 is(subscription1.downloadStatus, "synchronize_ok", "Extra content in check
sum line is ignored"); | |
| 656 subscriptionBody = subscriptionBody.replace(/extra1 /, "").replace(/ extra
2/, ""); | |
| 657 | |
| 658 subscriptionBody = subscriptionBody.replace(/\n/g, "\r\n"); | |
| 659 runScheduledTasks(48); | |
| 660 is(subscription1.downloadStatus, "synchronize_ok", "LF symbols are ignored
for checksum validation"); | |
| 661 subscriptionBody = subscriptionBody.replace(/\r\n/g, "\n"); | |
| 662 | |
| 663 subscriptionBody = subscriptionBody.replace(/\n/g, "\r"); | |
| 664 runScheduledTasks(48); | |
| 665 is(subscription1.downloadStatus, "synchronize_ok", "CR symbols are relevan
t for checksum validation"); | |
| 666 subscriptionBody = subscriptionBody.replace(/\r/g, "\n"); | |
| 667 | |
| 668 subscriptionBody = subscriptionBody.replace(/(Checksum[^\r\n]*)/, "$&extra
"); | |
| 669 runScheduledTasks(48); | |
| 670 is(subscription1.downloadStatus, "synchronize_checksum_mismatch", "Extra s
ymbols in the checksum are interpreted as part of the checksum"); | |
| 671 subscriptionBody = subscriptionBody.replace(/extra/, ""); | |
| 672 | |
| 673 subscriptionBody = subscriptionBody.replace(/(Checksum[^\r\n]*)/, "$&===")
; | |
| 674 runScheduledTasks(48); | |
| 675 is(subscription1.downloadStatus, "synchronize_ok", "= symbols after checks
um are ignored"); | |
| 676 subscriptionBody = subscriptionBody.replace(/===/, ""); | |
| 677 | |
| 678 requests = []; | |
| 679 subscriptionBody = subscriptionBody.replace(/Checksum: /, "$&wrong"); | |
| 680 runScheduledTasks(216); | |
| 681 compareRequests("Requests with checksum failures shouldn't trigger fallbac
k URL (27 hours)", [ | |
| 682 "0.1: GET /subscription1", | |
| 683 "24.1: GET /subscription1", | |
| 684 "48.1: GET /subscription1", | |
| 685 "72.1: GET /subscription1", | |
| 686 "96.1: GET /subscription1", | |
| 687 "120.1: GET /subscription1", | |
| 688 "144.1: GET /subscription1", | |
| 689 "168.1: GET /subscription1", | |
| 690 "192.1: GET /subscription1", | |
| 691 ]); | |
| 692 subscriptionBody = subscriptionBody.replace(/wrong/, ""); | |
| 693 | |
| 694 // | |
| 695 // Alternative download locations | |
| 696 // | |
| 697 | |
| 698 subscriptionBody = "[Adblock]\nfoo\nbar\n"; | |
| 699 let alternativeLocations = subscription2.url + ";q=0.5," + subscription3.u
rl + ";q=2"; | |
| 700 subscriptionExtraHeaders = function() [["X-Alternative-Locations", alterna
tiveLocations]]; | |
| 701 | |
| 702 runScheduledTasks(48); | |
| 703 is(subscription1.downloadStatus, "synchronize_ok", "= symbols after checks
um are ignored"); | |
| 704 is(subscription1.alternativeLocations, alternativeLocations, "Alternative
locations header processed on download"); | |
| 705 | |
| 706 requests = []; | |
| 707 SynchronizerGlobal.Math.random = function() 0; | |
| 708 runScheduledTasks(72); | |
| 709 compareRequests("Base URL should be chosen if Math.random() returns 0", [ | |
| 710 "0.1: GET /subscription1", | |
| 711 "24.1: GET /subscription1", | |
| 712 "48.1: GET /subscription1", | |
| 713 ]); | |
| 714 | |
| 715 requests = []; | |
| 716 SynchronizerGlobal.Math.random = function() 0.28; | |
| 717 runScheduledTasks(72); | |
| 718 compareRequests("Base URL should be chosen if Math.random() returns 0.28",
[ | |
| 719 "0.1: GET /subscription1", | |
| 720 "24.1: GET /subscription1", | |
| 721 "48.1: GET /subscription1", | |
| 722 ]); | |
| 723 | |
| 724 requests = []; | |
| 725 SynchronizerGlobal.Math.random = function() 0.29; | |
| 726 runScheduledTasks(72); | |
| 727 compareRequests("First alternative should be chosen if Math.random() retur
ns 0.29", [ | |
| 728 "0.1: GET /subscription2", | |
| 729 "24.1: GET /subscription2", | |
| 730 "48.1: GET /subscription2", | |
| 731 ]); | |
| 732 | |
| 733 requests = []; | |
| 734 SynchronizerGlobal.Math.random = function() 0.42; | |
| 735 runScheduledTasks(72); | |
| 736 compareRequests("First alternative should be chosen if Math.random() retur
ns 0.42", [ | |
| 737 "0.1: GET /subscription2", | |
| 738 "24.1: GET /subscription2", | |
| 739 "48.1: GET /subscription2", | |
| 740 ]); | |
| 741 | |
| 742 requests = []; | |
| 743 SynchronizerGlobal.Math.random = function() 0.43; | |
| 744 runScheduledTasks(72); | |
| 745 compareRequests("Second alternative should be chosen if Math.random() retu
rns 0.43", [ | |
| 746 "0.1: GET /subscription3", | |
| 747 "24.1: GET /subscription3", | |
| 748 "48.1: GET /subscription3", | |
| 749 ]); | |
| 750 | |
| 751 requests = []; | |
| 752 SynchronizerGlobal.Math.random = function() 0.99; // Note: side-effect is
increasing soft expiration interval to 29 hours | |
| 753 runScheduledTasks(87); | |
| 754 compareRequests("Second alternative should be chosen if Math.random() retu
rns 0.99", [ | |
| 755 "0.1: GET /subscription3", | |
| 756 "29.1: GET /subscription3", | |
| 757 "58.1: GET /subscription3", | |
| 758 ]); | |
| 759 | |
| 760 subscriptionStatus = [404, "Not Found"]; | |
| 761 SynchronizerGlobal.Math.random = function() 0; | |
| 762 runScheduledTasks(24); | |
| 763 is(subscription1.alternativeLocations, alternativeLocations, "Alternative
locations shouldn't be reset on download failure for base URL"); | |
| 764 | |
| 765 SynchronizerGlobal.Math.random = function() 0.99; | |
| 766 runScheduledTasks(24); | |
| 767 is(subscription1.alternativeLocations, null, "Alternative locations should
be reset on download failure for alternative URL"); | |
| 768 | |
| 769 requests = []; | |
| 770 subscriptionStatus = [200, "OK"]; | |
| 771 SynchronizerGlobal.Math.random = function() 0.99; // Note: side-effect is
increasing soft expiration interval to 29 hours | |
| 772 runScheduledTasks(87); | |
| 773 compareRequests("Alternative locations should be used again once the base
URL returns a new list", [ | |
| 774 "0.1: GET /subscription1", | |
| 775 "29.1: GET /subscription3", | |
| 776 "58.1: GET /subscription3", | |
| 777 ]); | |
| 778 | |
| 779 server.registerPathHandler("/subscription1", commentRedirectHandler); | |
| 780 redirectURL = subscription2.url; | |
| 781 SynchronizerGlobal.Math.random = function() 0; | |
| 782 runScheduledTasks(24); | |
| 783 is(subscription1.nextURL, subscription2.url, "Redirect comment accepted fr
om base URL"); | |
| 784 subscription1.nextURL = null; | |
| 785 server.registerPathHandler("/subscription1", getSubscription); | |
| 786 | |
| 787 server.registerPathHandler("/subscription3", commentRedirectHandler); | |
| 788 redirectURL = subscription2.url; | |
| 789 SynchronizerGlobal.Math.random = function() 0.99; | |
| 790 runScheduledTasks(29); | |
| 791 is(subscription1.nextURL, null, "Redirect comment ignored from alternative
URL"); | |
| 792 | |
| 793 server.registerPathHandler("/subscription3", redirectHandler); | |
| 794 redirectURL = subscription2.url; | |
| 795 SynchronizerGlobal.Math.random = function() 0.99; | |
| 796 redirectPermanent = true; | |
| 797 runScheduledTasks(29); | |
| 798 compareSubscriptions("Subscriptions after redirect from alternative URL",
[subscription1, subscription2]); | |
| 799 server.registerPathHandler("/subscription3", getSubscription); | |
| 800 | |
| 801 server.registerPathHandler("/subscription1", redirectHandler); | |
| 802 redirectURL = subscription2.url; | |
| 803 SynchronizerGlobal.Math.random = function() 0; | |
| 804 redirectPermanent = true; | |
| 805 runScheduledTasks(24); | |
| 806 compareSubscriptions("Subscriptions after redirect from base URL", [subscr
iption2]); | |
| 807 server.registerPathHandler("/subscription1", getSubscription); | |
| 808 | |
| 809 subscriptionExtraHeaders = redirectExtraHeaders = | |
| 810 function(metadata) [["X-Alternative-Locations", metadata.path == "/subsc
ription1" ? subscription2.url : subscription1.url]]; | |
| 811 server.registerPathHandler("/subscription1", redirectHandler); | |
| 812 redirectURL = subscription2.url; | |
| 813 SynchronizerGlobal.Math.random = function() 0; | |
| 814 redirectPermanent = false; | |
| 815 runScheduledTasks(24); | |
| 816 is(subscription1.alternativeLocations, subscription2.url, "Alternative loc
ations not taken over from redirect target on temporary redirect"); | |
| 817 resetSubscriptions(); | |
| 818 server.registerPathHandler("/subscription1", getSubscription); | |
| 819 | |
| 820 server.registerPathHandler("/subscription1", redirectHandler); | |
| 821 redirectURL = subscription2.url; | |
| 822 SynchronizerGlobal.Math.random = function() 0; | |
| 823 redirectPermanent = true; | |
| 824 runScheduledTasks(24); | |
| 825 is(subscription1.alternativeLocations, subscription1.url, "Alternative loc
ations taken over from redirect target on permanent redirect"); | |
| 826 resetSubscriptions(); | |
| 827 server.registerPathHandler("/subscription1", getSubscription); | |
| 828 | |
| 829 subscriptionExtraHeaders = null; | |
| 830 redirectExtraHeaders = null; | |
| 831 | |
| 832 // @TODO: If-Modified-Since | |
| 833 } | |
| 834 | |
| 835 SimpleTest.waitForExplicitFinish(); | |
| 836 addLoadEvent(function() | |
| 837 { | |
| 838 try | |
| 839 { | |
| 840 server.start(1234); | |
| 841 runTests(); | |
| 842 } | |
| 843 catch (e) | |
| 844 { | |
| 845 ok(false, e); | |
| 846 throw e; | |
| 847 } | |
| 848 finally | |
| 849 { | |
| 850 server.stop(); | |
| 851 SimpleTest.finish(); | |
| 852 } | |
| 853 }); | 612 }); |
| 854 </script> | 613 |
| 855 </pre> | 614 testRunner.runScheduledTasks(100); |
| 856 </body> | 615 deepEqual(requests, [0.1, 24.1, 48.1], "Stop trying if the fallback responds
with Gone"); |
| 857 </html> | 616 equal(fallbackParams, "http://127.0.0.1:1234/subscription&0&404", "Fallback
arguments"); |
| 617 |
| 618 // Fallback redirecting to a missing file |
| 619 |
| 620 subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"); |
| 621 resetSubscription(subscription); |
| 622 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); |
| 623 FilterStorage.addSubscription(subscription); |
| 624 requests = []; |
| 625 |
| 626 server.registerPathHandler("/fallback", function(metadata, response) |
| 627 { |
| 628 response.setStatusLine("1.1", "200", "OK"); |
| 629 |
| 630 let result = "301 http://127.0.0.1:1234/redirected"; |
| 631 response.bodyOutputStream.write(result, result.length); |
| 632 }); |
| 633 testRunner.runScheduledTasks(100); |
| 634 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
n", "Ignore invalid redirect from fallback"); |
| 635 deepEqual(requests, [0.1, 24.1, 48.1, 72.1, 96.1], "Requests not affected by
invalid redirect"); |
| 636 |
| 637 // Fallback redirecting to an existing file |
| 638 |
| 639 resetSubscription(subscription); |
| 640 requests = []; |
| 641 let redirectedRequests = []; |
| 642 server.registerPathHandler("/redirected", function(metadata, response) |
| 643 { |
| 644 redirectedRequests.push(testRunner.getTimeOffset()); |
| 645 |
| 646 response.setStatusLine("1.1", "200", "OK"); |
| 647 response.setHeader("Content-Type", "text/plain"); |
| 648 |
| 649 let result = "[Adblock]\n!Expires: 1day\nfoo\nbar"; |
| 650 response.bodyOutputStream.write(result, result.length); |
| 651 }); |
| 652 |
| 653 testRunner.runScheduledTasks(100); |
| 654 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
, "Valid redirect from fallback is followed"); |
| 655 deepEqual(requests, [0.1, 24.1, 48.1], "Stop polling original URL after a va
lid redirect from fallback"); |
| 656 deepEqual(redirectedRequests, [48.1, 72.1, 96.1], "Request new URL after a v
alid redirect from fallback"); |
| 657 |
| 658 // Checksum mismatch |
| 659 |
| 660 function handler2(metadata, response) |
| 661 { |
| 662 response.setStatusLine("1.1", "200", "OK"); |
| 663 response.setHeader("Content-Type", "text/plain"); |
| 664 |
| 665 let result = "[Adblock]\n! Checksum: wrong\nfoo\nbar"; |
| 666 response.bodyOutputStream.write(result, result.length); |
| 667 } |
| 668 server.registerPathHandler("/subscription", handler2); |
| 669 |
| 670 subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"); |
| 671 resetSubscription(subscription); |
| 672 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); |
| 673 FilterStorage.addSubscription(subscription); |
| 674 |
| 675 testRunner.runScheduledTasks(100); |
| 676 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
, "Wrong checksum produces fallback request"); |
| 677 |
| 678 // Redirect loop |
| 679 |
| 680 server.registerPathHandler("/subscription", function(metadata, response) |
| 681 { |
| 682 response.setStatusLine("1.1", "200", "OK"); |
| 683 response.setHeader("Content-Type", "text/plain"); |
| 684 |
| 685 let result = "[Adblock]\n! Redirect: http://127.0.0.1:1234/subscription2"; |
| 686 response.bodyOutputStream.write(result, result.length); |
| 687 }); |
| 688 server.registerPathHandler("/subscription2", function(metadata, response) |
| 689 { |
| 690 response.setStatusLine("1.1", "200", "OK"); |
| 691 response.setHeader("Content-Type", "text/plain"); |
| 692 |
| 693 let result = "[Adblock]\n! Redirect: http://127.0.0.1:1234/subscription"; |
| 694 response.bodyOutputStream.write(result, result.length); |
| 695 }); |
| 696 |
| 697 subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"); |
| 698 resetSubscription(subscription); |
| 699 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); |
| 700 FilterStorage.addSubscription(subscription); |
| 701 |
| 702 testRunner.runScheduledTasks(100); |
| 703 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
, "Fallback can still redirect even after a redirect loop"); |
| 704 }); |
| 705 |
| 706 test("State fields", function() |
| 707 { |
| 708 // Always use average download interval |
| 709 randomResult = 0.5; |
| 710 |
| 711 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 712 FilterStorage.addSubscription(subscription); |
| 713 |
| 714 server.registerPathHandler("/subscription", function successHandler(metadata
, response) |
| 715 { |
| 716 response.setStatusLine("1.1", "200", "OK"); |
| 717 response.setHeader("Content-Type", "text/plain"); |
| 718 |
| 719 let result = "[Adblock]\n! Expires: 2 hours\nfoo\nbar"; |
| 720 response.bodyOutputStream.write(result, result.length); |
| 721 }); |
| 722 |
| 723 let startTime = testRunner.currentTime; |
| 724 testRunner.runScheduledTasks(2); |
| 725 |
| 726 equal(subscription.downloadStatus, "synchronize_ok", "downloadStatus after s
uccessful download"); |
| 727 equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + 0.1 * MILLIS
_IN_HOUR, "lastDownload after successful download"); |
| 728 equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + 0.1 * MILLIS_
IN_HOUR, "lastSuccess after successful download"); |
| 729 equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + 1.1 * MILLIS_IN
_HOUR, "lastCheck after successful download"); |
| 730 equal(subscription.errors, 0, "errors after successful download"); |
| 731 |
| 732 server.registerPathHandler("/subscription", function errorHandler(metadata,
response) |
| 733 { |
| 734 response.setStatusLine("1.1", "404", "Not Found"); |
| 735 }); |
| 736 |
| 737 testRunner.runScheduledTasks(2); |
| 738 |
| 739 equal(subscription.downloadStatus, "synchronize_connection_error", "download
Status after download error"); |
| 740 equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + 2.1 * MILLIS
_IN_HOUR, "lastDownload after download error"); |
| 741 equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + 0.1 * MILLIS_
IN_HOUR, "lastSuccess after download error"); |
| 742 equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + 3.1 * MILLIS_IN
_HOUR, "lastCheck after download error"); |
| 743 equal(subscription.errors, 1, "errors after download error"); |
| 744 }); |
| 745 })(); |
| OLD | NEW |