| 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 |
| 25 <script type="application/x-javascript;version=1.7"> | 25 server = new nsHttpServer(); |
| 26 let {Synchronizer} = require("synchronizer"); | 26 server.start(1234); |
| 27 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 27 |
| 28 prepareFilterComponents(); | 28 let currentTime = 100000 * MILLIS_IN_HOUR; |
| 29 preparePrefs(); | 29 let startTime = currentTime; |
| 30 | 30 let scheduledTasks = []; |
| 31 let currentTime = 20000 * 24 * 60 * 60 * 1000; | 31 |
| 32 let startTime = 0; | 32 // Replace Date.now() function |
| 33 let scheduledTasks = []; | 33 this._origNow = SynchronizerGlobal.Date.now; |
| 34 | 34 SynchronizerGlobal.Date.now = function() currentTime; |
| 35 let oldRandom = SynchronizerGlobal.Math.random; | 35 |
| 36 let oldNow = SynchronizerGlobal.Date.now; | 36 // Replace Math.random() function |
| 37 SynchronizerGlobal.Date.now = function() | 37 this._origRandom = SynchronizerGlobal.Math.random; |
| 38 { | 38 SynchronizerGlobal.Math.random = function() randomResult; |
| 39 return currentTime; | 39 |
| 40 }; | 40 // Replace global timer variable |
| 41 Date.now = SynchronizerGlobal.Date.now; // Override for httpd Date header | 41 let timer = {__proto__: SynchronizerModule.timer, delay: 0.1 * MILLIS_IN_H
OUR}; |
| 42 | |
| 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; | 42 let callback = timer.callback; |
| 95 timer.handler = function() { callback.notify(timer); }; | 43 timer.handler = function() { callback.notify(timer); }; |
| 96 timer.nextExecution = currentTime + timer.delay; | 44 timer.nextExecution = currentTime + timer.delay; |
| 97 | |
| 98 scheduledTasks.push(timer); | 45 scheduledTasks.push(timer); |
| 99 | 46 SynchronizerModule.timer.cancel(); |
| 100 SynchronizerGlobal.timer.cancel(); | 47 SynchronizerModule.timer = timer; |
| 101 SynchronizerGlobal.timer = timer; | 48 |
| 102 } | 49 // Register observer to track outstanding requests |
| 103 | 50 this._outstandingRequests = 0; |
| 104 // Track requests initiated by Synchronizer object by hooking its | 51 Services.obs.addObserver(this, "http-on-modify-request", true); |
| 105 // XMLHttpRequest constructor. | 52 Services.obs.addObserver(this, "http-on-examine-response", true); |
| 106 let oldXMLHttp = SynchronizerGlobal.XMLHttpRequest; | 53 |
| 107 SynchronizerGlobal.XMLHttpRequest = function() | 54 this.runScheduledTasks = function(maxHours, initial, skip) |
| 108 { | 55 { |
| 109 let inner = new oldXMLHttp(); | 56 if (typeof maxHours != "number") |
| 110 | 57 throw new Error("Numerical parameter expected"); |
| 111 return { | 58 if (typeof initial != "number") |
| 112 __proto__: inner, | 59 initial = 0; |
| 113 send: function() | 60 if (typeof skip != "number") |
| 61 skip = 0; |
| 62 |
| 63 startTime = currentTime; |
| 64 if (initial >= 0) |
| 114 { | 65 { |
| 115 outstandingRequests++; | 66 this._runScheduledTasks(initial); |
| 116 function finished() | 67 maxHours -= initial; |
| 68 } |
| 69 if (skip) |
| 70 { |
| 71 this._skipTasks(skip); |
| 72 maxHours -= initial; |
| 73 } |
| 74 this._runScheduledTasks(maxHours); |
| 75 } |
| 76 |
| 77 this._runScheduledTasks = function(maxHours) |
| 78 { |
| 79 let endTime = currentTime + maxHours * MILLIS_IN_HOUR; |
| 80 while (true) |
| 81 { |
| 82 let nextTask = null; |
| 83 for each (let task in scheduledTasks) |
| 117 { | 84 { |
| 118 outstandingRequests--; | 85 if (!nextTask || nextTask.nextExecution > task.nextExecution) |
| 86 nextTask = task; |
| 119 } | 87 } |
| 120 inner.addEventListener("load", finished, false); | 88 if (!nextTask || nextTask.nextExecution > endTime) |
| 121 inner.addEventListener("error", finished, false); | 89 break; |
| 122 | 90 |
| 123 inner.send.apply(inner, arguments); | 91 currentTime = nextTask.nextExecution; |
| 92 nextTask.handler(); |
| 93 |
| 94 // Let all asynchronous actions finish |
| 95 let thread = Services.tm.currentThread; |
| 96 let loopStartTime = Date.now(); |
| 97 |
| 98 while (this._outstandingRequests > 0 || thread.hasPendingEvents()) |
| 99 { |
| 100 thread.processNextEvent(true); |
| 101 |
| 102 if (Date.now() - loopStartTime > 5000) |
| 103 throw new Error("Synchronizer stuck downloading subscriptions"); |
| 104 } |
| 105 |
| 106 if (nextTask.type == Components.interfaces.nsITimer.TYPE_ONE_SHOT) |
| 107 scheduledTasks = scheduledTasks.filter(function(task) task != nextTa
sk); |
| 108 else |
| 109 nextTask.nextExecution = currentTime + nextTask.delay; |
| 124 } | 110 } |
| 125 } | 111 |
| 126 } | 112 currentTime = endTime; |
| 127 | 113 } |
| 128 // Make sure to restore everything when this document unloads | 114 |
| 129 window.addEventListener("unload", function() | 115 this._skipTasks = function(hours) |
| 130 { | 116 { |
| 131 SynchronizerGlobal.Date.now = oldNow; | 117 let newTasks = []; |
| 132 SynchronizerGlobal.XMLHttpRequest = oldXMLHttp; | 118 let endTime = currentTime + hours * MILLIS_IN_HOUR; |
| 133 SynchronizerGlobal.Math.random = oldRandom; | 119 for each (let task in scheduledTasks) |
| 134 Synchronizer.startup(); | 120 { |
| 135 }, false); | 121 if (task.nextExecution >= endTime) |
| 136 | 122 newTasks.push(task); |
| 137 let server = new nsHttpServer(); | 123 else if (task.type != Components.interfaces.nsITimer.TYPE_ONE_SHOT) |
| 124 { |
| 125 task.nextExecution = endTime; |
| 126 newTasks.push(task); |
| 127 } |
| 128 } |
| 129 scheduledTasks = newTasks; |
| 130 } |
| 131 |
| 132 this.getTimeOffset = function() (currentTime - startTime) / MILLIS_IN_HOUR
; |
| 133 |
| 134 this.__defineGetter__("currentTime", function() currentTime); |
| 135 }, |
| 136 |
| 137 observe: function(subject, topic, data) |
| 138 { |
| 139 let orig = this._outstandingRequests; |
| 140 if (topic == "http-on-modify-request") |
| 141 this._outstandingRequests++; |
| 142 else if (topic == "http-on-examine-response") |
| 143 this._outstandingRequests--; |
| 144 }, |
| 145 |
| 146 teardown: function() |
| 147 { |
| 148 restoreFilterComponents.call(this); |
| 149 restorePrefs.call(this); |
| 150 |
| 151 stop(); |
| 152 server.stop(function() |
| 153 { |
| 154 server = null; |
| 155 start(); |
| 156 }); |
| 157 |
| 158 if (this._origNow) |
| 159 { |
| 160 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); |
| 161 SynchronizerGlobal.Date.now = this._origNow; |
| 162 delete this._origNow; |
| 163 } |
| 164 |
| 165 if (this._origRandom) |
| 166 { |
| 167 let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); |
| 168 SynchronizerGlobal.Math.random = this._origRandom; |
| 169 delete this._origRandom; |
| 170 } |
| 171 |
| 172 Synchronizer.init(); |
| 173 } |
| 174 }); |
| 175 |
| 176 function resetSubscription(subscription) |
| 177 { |
| 178 FilterStorage.updateSubscriptionFilters(subscription, []); |
| 179 subscription.lastCheck = subscription.lastDownload = |
| 180 subscription.lastSuccess = subscription.expires = |
| 181 subscription.softExpiration = 0; |
| 182 subscription.errors = 0; |
| 183 subscription.downloadStatus = null; |
| 184 subscription.requiredVersion = null; |
| 185 subscription.nextURL = null; |
| 186 } |
| 187 |
| 188 test("Downloads of one subscription", function() |
| 189 { |
| 190 // Always use average download interval |
| 191 randomResult = 0.5; |
| 192 |
| 193 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 194 FilterStorage.addSubscription(subscription); |
| 138 | 195 |
| 139 let requests = []; | 196 let requests = []; |
| 197 function handler(metadata, response) |
| 198 { |
| 199 requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
); |
| 200 |
| 201 response.setStatusLine("1.1", "200", "OK"); |
| 202 response.setHeader("Content-Type", "text/plain"); |
| 203 |
| 204 let result = "[Adblock]\nfoo\nbar"; |
| 205 response.bodyOutputStream.write(result, result.length); |
| 206 } |
| 207 |
| 208 server.registerPathHandler("/subscription", handler); |
| 209 |
| 210 testRunner.runScheduledTasks(50); |
| 211 deepEqual(requests, [ |
| 212 [0.1, "GET", "/subscription"], |
| 213 [24.1, "GET", "/subscription"], |
| 214 [48.1, "GET", "/subscription"], |
| 215 ], "Requests after 50 hours"); |
| 216 }); |
| 217 |
| 218 test("Downloads of two subscriptions", function() |
| 219 { |
| 220 // Always use average download interval |
| 221 randomResult = 0.5; |
| 140 | 222 |
| 141 let subscription1 = Subscription.fromURL("http://127.0.0.1:1234/subscription
1"); | 223 let subscription1 = Subscription.fromURL("http://127.0.0.1:1234/subscription
1"); |
| 224 FilterStorage.addSubscription(subscription1); |
| 225 |
| 142 let subscription2 = Subscription.fromURL("http://127.0.0.1:1234/subscription
2"); | 226 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"); | 227 subscription2.expires = |
| 144 | 228 subscription2.softExpiration = |
| 145 let subscriptionStatus = [200, "OK"]; | 229 (testRunner.currentTime + 2 * MILLIS_IN_HOUR) / MILLIS_IN_SECOND; |
| 146 let subscriptionExtraHeaders = null; | 230 FilterStorage.addSubscription(subscription2); |
| 147 let subscriptionBody = "[Adblock]\nfoo\nbar"; | 231 |
| 148 function getSubscription(metadata, response) | 232 let requests = []; |
| 149 { | 233 function handler(metadata, response) |
| 150 requests.push((currentTime - startTime) / 3600000 + ": " + metadata.method
+ " " + metadata.path); | 234 { |
| 151 | 235 requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
); |
| 152 response.setStatusLine("1.1", subscriptionStatus[0], subscriptionStatus[1]
); | 236 |
| 153 // Return wrong MIME type, client should be able to handle it | 237 response.setStatusLine("1.1", "200", "OK"); |
| 238 response.setHeader("Content-Type", "text/plain"); |
| 239 |
| 240 let result = "[Adblock]\nfoo\nbar"; |
| 241 response.bodyOutputStream.write(result, result.length); |
| 242 } |
| 243 |
| 244 server.registerPathHandler("/subscription1", handler); |
| 245 server.registerPathHandler("/subscription2", handler); |
| 246 |
| 247 testRunner.runScheduledTasks(55); |
| 248 deepEqual(requests, [ |
| 249 [0.1, "GET", "/subscription1"], |
| 250 [2.1, "GET", "/subscription2"], |
| 251 [24.1, "GET", "/subscription1"], |
| 252 [26.1, "GET", "/subscription2"], |
| 253 [48.1, "GET", "/subscription1"], |
| 254 [50.1, "GET", "/subscription2"], |
| 255 ], "Requests after 55 hours"); |
| 256 }); |
| 257 |
| 258 test("Download result, various subscription headers", function() |
| 259 { |
| 260 // Always use average download interval |
| 261 randomResult = 0.5; |
| 262 |
| 263 let test; |
| 264 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 265 FilterStorage.addSubscription(subscription); |
| 266 |
| 267 function handler(metadata, response) |
| 268 { |
| 269 response.setStatusLine("1.1", "200", "OK"); |
| 270 |
| 271 // Wrong content type shouldn't matter |
| 154 response.setHeader("Content-Type", "text/xml"); | 272 response.setHeader("Content-Type", "text/xml"); |
| 155 | 273 |
| 156 if (subscriptionExtraHeaders) | 274 let result = test.header + "\nfoo\n!bar\n\n@@bas\n#bam"; |
| 157 { | 275 response.bodyOutputStream.write(result, result.length); |
| 158 for each (let [header, value] in subscriptionExtraHeaders(metadata)) | 276 } |
| 159 response.setHeader(header, value); | 277 server.registerPathHandler("/subscription", handler); |
| 160 } | 278 |
| 161 | 279 let tests = [ |
| 162 response.bodyOutputStream.write(subscriptionBody, subscriptionBody.length)
; | 280 {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: n
ull}, |
| 163 } | 281 {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersi
on: null}, |
| 164 | 282 {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", require
dVersion: null}, |
| 165 let redirectPermanent = null; | 283 {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", require
dVersion: "0.0.1"}, |
| 166 let redirectURL = null; | 284 {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", required
Version: "99.9"}, |
| 167 let redirectExtraHeaders = null; | 285 {header: "[Foo]", downloadStatus: "synchronize_invalid_data", requiredVers
ion: null} |
| 168 function redirectHandler(metadata, response) | 286 ]; |
| 169 { | 287 for each (test in tests) |
| 170 response.setStatusLine("1.1", redirectPermanent ? 301 : 302, redirectPerma
nent ? "Moved Permanently" : "Moved Temporarily"); | 288 { |
| 171 response.setHeader("Location", redirectURL); | 289 resetSubscription(subscription) |
| 172 | 290 testRunner.runScheduledTasks(2); |
| 173 if (redirectExtraHeaders) | 291 |
| 174 { | 292 equal(subscription.downloadStatus, test.downloadStatus, "Download status f
or " + test.header) |
| 175 for each (let [header, value] in redirectExtraHeaders(metadata)) | 293 equal(subscription.requiredVersion, test.requiredVersion, "Required versio
n for " + test.header) |
| 176 response.setHeader(header, value); | 294 |
| 177 } | 295 if (test.downloadStatus == "synchronize_ok") |
| 178 } | 296 { |
| 179 function commentRedirectHandler(metadata, response) | 297 deepEqual(subscription.filters, [ |
| 180 { | 298 Filter.fromText("foo"), |
| 181 getSubscription(metadata, response); | 299 Filter.fromText("!bar"), |
| 182 | 300 Filter.fromText("@@bas"), |
| 183 if (redirectExtraHeaders) | 301 Filter.fromText("#bam"), |
| 184 { | 302 ], "Resulting subscription filters for " + test.header); |
| 185 for each (let [header, value] in redirectExtraHeaders(metadata)) | 303 } |
| 186 response.setHeader(header, value); | 304 else |
| 187 } | 305 { |
| 188 | 306 deepEqual(subscription.filters, [ |
| 189 var comment = "\n! Redirect: " + redirectURL; | 307 ], "Resulting subscription filters for " + test.header); |
| 190 response.bodyOutputStream.write(comment, comment.length); | 308 } |
| 191 } | 309 } |
| 192 | 310 }) |
| 193 var fallbackResult = ""; | 311 |
| 194 function fallbackHandler(metadata, response) | 312 test("Automatic updates disabled", function() |
| 195 { | 313 { |
| 196 requests.push((currentTime - startTime) / 3600000 + ": " + metadata.method
+ " " + metadata.path + " " + decodeURIComponent(metadata.queryString)); | 314 Prefs.subscriptions_autoupdate = false; |
| 197 | 315 |
| 198 response.setStatusLine("1.1", 200, "OK"); | 316 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 199 // Return wrong MIME type, client should be able to handle it | 317 FilterStorage.addSubscription(subscription); |
| 200 response.setHeader("Content-Type", "application/x-foo-bar"); | 318 |
| 201 | 319 let requests = 0; |
| 202 if (subscriptionExtraHeaders) | 320 function handler(metadata, response) |
| 203 { | 321 { |
| 204 for each (let [header, value] in subscriptionExtraHeaders()) | 322 requests++; |
| 205 response.setHeader(header, value); | 323 throw new Error("Unexpected request"); |
| 206 } | 324 } |
| 207 | 325 |
| 208 response.bodyOutputStream.write(fallbackResult, fallbackResult.length); | 326 server.registerPathHandler("/subscription", handler); |
| 209 } | 327 |
| 210 | 328 testRunner.runScheduledTasks(50); |
| 211 function compareRequests(test, expected) | 329 equal(requests, 0, "Request count"); |
| 212 { | 330 }); |
| 213 is(requests.join("\n"), expected.join("\n"), test); | 331 |
| 332 test("Expiration time", function() |
| 333 { |
| 334 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 335 FilterStorage.addSubscription(subscription); |
| 336 |
| 337 let test; |
| 338 let requests = []; |
| 339 function handler(metadata, response) |
| 340 { |
| 341 requests.push(testRunner.getTimeOffset()); |
| 342 |
| 343 response.setStatusLine("1.1", "200", "OK"); |
| 344 response.setHeader("Content-Type", "text/plain"); |
| 345 |
| 346 let result = "[Adblock]\nfoo\n!Expires: " + test.expiration + "\nbar"; |
| 347 response.bodyOutputStream.write(result, result.length); |
| 348 } |
| 349 server.registerPathHandler("/subscription", handler); |
| 350 |
| 351 let tests = [ |
| 352 { |
| 353 expiration: "1 hour", // Too small, will be corrected |
| 354 randomResult: 0.5, |
| 355 requests: [0.1, 24.1] |
| 356 }, |
| 357 { |
| 358 expiration: "26 hours", |
| 359 randomResult: 0.5, |
| 360 requests: [0.1, 26.1] |
| 361 }, |
| 362 { |
| 363 expiration: "2 days", |
| 364 randomResult: 0.5, |
| 365 requests: [0.1, 48.1] |
| 366 }, |
| 367 { |
| 368 expiration: "20 days", // Too large, will be corrected |
| 369 randomResult: 0.5, |
| 370 requests: [0.1, 14 * 24 + 0.1] |
| 371 }, |
| 372 { |
| 373 expiration: "35 hours", |
| 374 randomResult: 0, // Changes interval by factor 0.8 |
| 375 requests: [0.1, 28.1] |
| 376 }, |
| 377 { |
| 378 expiration: "35 hours", |
| 379 randomResult: 1, // Changes interval by factor 1.2 |
| 380 requests: [0.1, 42.1] |
| 381 }, |
| 382 { |
| 383 expiration: "35 hours", |
| 384 randomResult: 0.25, // Changes interval by factor 0.9 |
| 385 requests: [0.1, 32.1] |
| 386 }, |
| 387 { |
| 388 expiration: "40 hours", |
| 389 randomResult: 0.5, |
| 390 skipAfter: 5.1, |
| 391 skip: 10, // Short break should not increase soft expirati
on |
| 392 requests: [0.1, 40.1] |
| 393 }, |
| 394 { |
| 395 expiration: "40 hours", |
| 396 randomResult: 0.5, |
| 397 skipAfter: 5.1, |
| 398 skip: 30, // Long break should increase soft expiration |
| 399 requests: [0.1, 70.1] |
| 400 }, |
| 401 { |
| 402 expiration: "40 hours", |
| 403 randomResult: 0.5, |
| 404 skipAfter: 5.1, |
| 405 skip: 80, // Hitting hard expiration, immediate download |
| 406 requests: [0.1, 85.1] |
| 407 } |
| 408 ] |
| 409 |
| 410 for each (test in tests) |
| 411 { |
| 214 requests = []; | 412 requests = []; |
| 215 } | 413 randomResult = test.randomResult; |
| 216 | 414 resetSubscription(subscription); |
| 217 function compareFilters(test, expected, expectedStatus, expectedVersion) | 415 |
| 218 { | 416 let maxHours = Math.round(Math.max.apply(null, test.requests)) + 12; |
| 219 let result = subscription1.filters.map(function(filter) filter.text).join(
"\n"); | 417 testRunner.runScheduledTasks(maxHours, test.skipAfter, test.skip); |
| 220 is(result, expected, test); | 418 |
| 221 is(subscription1.downloadStatus, expectedStatus, "Subscription status afte
r previous test"); | 419 let randomAddendum = (randomResult == 0.5 ? "" : " with Math.random() retu
rning " + randomResult); |
| 222 is(subscription1.requiredVersion, expectedVersion, "Required version after
previous test"); | 420 let skipAddendum = (typeof test.skip != "number" ? "" : " skipping " + tes
t.skip + " hours after " + test.skipAfter + " hours"); |
| 223 requests = []; | 421 deepEqual(requests, test.requests, "Requests for \"" + test.expiration + "
\"" + randomAddendum + skipAddendum); |
| 224 } | 422 |
| 225 | 423 if (typeof test.skip == "number") |
| 226 function resetSubscriptions() | 424 { |
| 227 { | 425 // Ensure that next time synchronizer triggers at time offset 0.1 again |
| 228 FilterStorage.removeSubscription(subscription1); | 426 testRunner.runScheduledTasks(0.1); |
| 229 FilterStorage.removeSubscription(subscription2); | 427 } |
| 230 FilterStorage.removeSubscription(subscription3); | 428 } |
| 231 FilterStorage.addSubscription(subscription1); | 429 }); |
| 232 FilterStorage.addSubscription(subscription2); | 430 |
| 233 subscription2.autoDownload = false; | 431 test("Redirects", function() |
| 234 } | 432 { |
| 235 | 433 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 236 function compareSubscriptions(test, expectedSubscriptions) | 434 FilterStorage.addSubscription(subscription); |
| 237 { | 435 |
| 238 let result = FilterStorage.subscriptions.map(function(subscription) subscr
iption.url).join("\n"); | 436 function redirect_handler(metadata, response) |
| 239 let expected = expectedSubscriptions.map(function(subscription) subscripti
on.url).join("\n"); | 437 { |
| 240 is(result, expected, test); | 438 response.setStatusLine("1.1", "200", "OK"); |
| 241 requests = []; | 439 response.setHeader("Content-Type", "text/plain"); |
| 242 resetSubscriptions(); | 440 |
| 243 } | 441 let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/redirected\
nbar"; |
| 244 | 442 response.bodyOutputStream.write(result, result.length); |
| 245 function runTests() | 443 } |
| 246 { | 444 server.registerPathHandler("/subscription", redirect_handler); |
| 247 is(typeof Synchronizer, "object", "typeof Synchronizer"); | 445 |
| 248 | 446 testRunner.runScheduledTasks(50); |
| 249 server.registerPathHandler("/subscription1", getSubscription); | 447 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
n", "Invalid redirect ignored"); |
| 250 server.registerPathHandler("/subscription2", getSubscription); | 448 |
| 251 server.registerPathHandler("/subscription3", getSubscription); | 449 let requests = []; |
| 252 server.registerPathHandler("/fallback", fallbackHandler); | 450 function handler(metadata, response) |
| 253 | 451 { |
| 254 FilterStorage.addSubscription(subscription1); | 452 requests.push(testRunner.getTimeOffset()); |
| 255 | 453 |
| 256 subscription2.autoDownload = false; | 454 response.setStatusLine("1.1", "200", "OK"); |
| 257 FilterStorage.addSubscription(subscription2); | 455 response.setHeader("Content-Type", "text/plain"); |
| 258 | 456 |
| 259 // | 457 let result = "[Adblock]\nfoo\nbar"; |
| 260 // General subscription download testing | 458 response.bodyOutputStream.write(result, result.length); |
| 261 // | 459 } |
| 262 | 460 server.registerPathHandler("/redirected", handler); |
| 263 SynchronizerGlobal.Math.random = function() 0.5; | 461 |
| 264 | 462 resetSubscription(subscription); |
| 265 runScheduledTasks(50); | 463 testRunner.runScheduledTasks(50); |
| 266 compareRequests("Downloads of one subscription (50 hours)", [ | 464 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
, "Redirect followed"); |
| 267 "0.1: GET /subscription1", | 465 deepEqual(requests, [24.1, 48.1], "Resulting requests"); |
| 268 "24.1: GET /subscription1", | 466 }); |
| 269 "48.1: GET /subscription1" | 467 |
| 270 ]); | 468 test("Fallback", function() |
| 271 | 469 { |
| 272 subscription2.autoDownload = true; | 470 Prefs.subscriptions_fallbackerrors = 3; |
| 273 runScheduledTasks(70); | 471 Prefs.subscriptions_fallbackurl = "http://127.0.0.1:1234/fallback?%URL%&%CHA
NNELSTATUS%&%RESPONSESTATUS%"; |
| 274 compareRequests("Downloads with second subscription switched on (48 hours)
", [ | 472 |
| 275 "0.1: GET /subscription2", | 473 let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
); |
| 276 "22.1: GET /subscription1", | 474 FilterStorage.addSubscription(subscription); |
| 277 "24.1: GET /subscription2", | 475 |
| 278 "46.1: GET /subscription1", | 476 let requests = []; |
| 279 "48.1: GET /subscription2" | 477 function handler(metadata, response) |
| 280 ]); | 478 { |
| 281 subscription2.autoDownload = false; | 479 requests.push(testRunner.getTimeOffset()); |
| 282 | 480 |
| 283 // | 481 response.setStatusLine("1.1", "404", "Not found"); |
| 284 // Header variations testing | 482 } |
| 285 // | 483 server.registerPathHandler("/subscription", handler); |
| 286 | 484 |
| 287 subscriptionBody = "[Adblock]\nfoo\n!bar\n\n\n@@bas\n#bam"; | 485 testRunner.runScheduledTasks(100); |
| 288 runScheduledTasks(24); | 486 deepEqual(requests, [0.1, 24.1, 48.1, 72.1, 96.1], "Continue trying if the f
allback doesn't respond"); |
| 289 compareFilters("Filters of downloaded subscription", "foo\n!bar\n@@bas\n#b
am", "synchronize_ok", null); | 487 |
| 290 | 488 resetSubscription(subscription); |
| 291 subscriptionBody = "[Adblock Plus]\nfoo2\n!bar2\n@@bas2\n#bam2"; | 489 requests = []; |
| 292 runScheduledTasks(24); | 490 fallbackParams = null; |
| 293 compareFilters("Filters of downloaded subscription with [Adblock Plus] hea
der", "foo2\n!bar2\n@@bas2\n#bam2", "synchronize_ok", null); | 491 server.registerPathHandler("/fallback", function(metadata, response) |
| 294 | 492 { |
| 295 subscriptionBody = "[Adblock Plus 0.0.1]\nfoo3\n!bar3\n@@bas3\n#bam3"; | 493 response.setStatusLine("1.1", "200", "OK"); |
| 296 runScheduledTasks(24); | 494 fallbackParams = decodeURIComponent(metadata.queryString); |
| 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"); | 495 |
| 298 | 496 let result = "410 Gone"; |
| 299 subscriptionBody = "(something)[Adblock]\nfoo4\n!bar4\n@@bas4\n#bam4"; | 497 response.bodyOutputStream.write(result, result.length); |
| 300 runScheduledTasks(24); | |
| 301 compareFilters("Filters of downloaded subscription with (something)[Adbloc
k] header", "foo4\n!bar4\n@@bas4\n#bam4", "synchronize_ok", null); | |
| 302 | |
| 303 subscriptionBody = "[Foo]\nthis should not be accepted"; | |
| 304 runScheduledTasks(24); | |
| 305 compareFilters("Filters of downloaded subscription with [Foo] header", "fo
o4\n!bar4\n@@bas4\n#bam4", "synchronize_invalid_data", null); | |
| 306 | |
| 307 subscriptionBody = "[Adblock Plus 99.9]\nsome_new_syntax"; | |
| 308 runScheduledTasks(24); | |
| 309 compareFilters("Filters of downloaded subscription with [Adblock Plus 99.9
] header", "some_new_syntax", "synchronize_ok", "99.9"); | |
| 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 }); | 498 }); |
| 854 </script> | 499 |
| 855 </pre> | 500 testRunner.runScheduledTasks(100); |
| 856 </body> | 501 deepEqual(requests, [0.1, 24.1, 48.1], "Stop trying if the fallback responds
with Gone"); |
| 857 </html> | 502 equal(fallbackParams, "http://127.0.0.1:1234/subscription&0&404"); |
| 503 |
| 504 resetSubscription(subscription); |
| 505 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); |
| 506 FilterStorage.addSubscription(subscription); |
| 507 requests = []; |
| 508 |
| 509 server.registerPathHandler("/fallback", function(metadata, response) |
| 510 { |
| 511 response.setStatusLine("1.1", "200", "OK"); |
| 512 |
| 513 let result = "301 http://127.0.0.1:1234/redirected"; |
| 514 response.bodyOutputStream.write(result, result.length); |
| 515 }); |
| 516 testRunner.runScheduledTasks(100); |
| 517 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
n"); |
| 518 deepEqual(requests, [0.1, 24.1, 48.1, 96.1], "Come back after invalid redire
ct from fallback"); |
| 519 |
| 520 resetSubscription(subscription); |
| 521 requests = []; |
| 522 let redirectedRequests = []; |
| 523 server.registerPathHandler("/redirected", function(metadata, response) |
| 524 { |
| 525 redirectedRequests.push(testRunner.getTimeOffset()); |
| 526 |
| 527 response.setStatusLine("1.1", "200", "OK"); |
| 528 response.setHeader("Content-Type", "text/plain"); |
| 529 |
| 530 let result = "[Adblock]\nfoo\nbar"; |
| 531 response.bodyOutputStream.write(result, result.length); |
| 532 }); |
| 533 |
| 534 testRunner.runScheduledTasks(100); |
| 535 equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
); |
| 536 deepEqual(requests, [0.1, 24.1, 48.1], "Stop polling original URL after a va
lid redirect from fallback"); |
| 537 deepEqual(redirectedRequests, [72.1, 96.1], "Request new URL after a valid r
edirect from fallback"); |
| 538 }); |
| 539 |
| 540 // TODO: Checksum verification |
| 541 })(); |
| OLD | NEW |