| LEFT | RIGHT | 
|---|
| 1 (function() | 1 (function() | 
| 2 { | 2 { | 
| 3   let testRunner = null; | 3   let testRunner = null; | 
| 4   let server = null; | 4   let server = null; | 
| 5   let randomResult = 0.5; | 5   let randomResult = 0.5; | 
| 6 | 6 | 
| 7   const MILLIS_IN_SECOND = 1000; | 7   const MILLIS_IN_SECOND = 1000; | 
| 8   const MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; | 8   const MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; | 
| 9   const MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; | 9   const MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; | 
| 10   const MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; | 10   const MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; | 
| 11 | 11 | 
| 12   module("Synchronizer", { | 12   module("Synchronizer", { | 
| 13     QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRef
     erence]), | 13     QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRef
     erence]), | 
| 14 | 14 | 
| 15     setup: function() | 15     setup: function() | 
| 16     { | 16     { | 
| 17       testRunner = this; | 17       testRunner = this; | 
| 18 | 18 | 
| 19       prepareFilterComponents.call(this); | 19       prepareFilterComponents.call(this); | 
| 20       preparePrefs.call(this); | 20       preparePrefs.call(this); | 
| 21 | 21 | 
| 22       let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 22       let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 
| 23       let SynchronizerModule = getModuleGlobal("synchronizer"); | 23       let SynchronizerModule = getModuleGlobal("synchronizer"); | 
|  | 24       let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.downloader
     ); | 
| 24 | 25 | 
| 25       server = new nsHttpServer(); | 26       server = new nsHttpServer(); | 
| 26       server.start(1234); | 27       server.start(1234); | 
| 27 | 28 | 
| 28       let currentTime = 100000 * MILLIS_IN_HOUR; | 29       let currentTime = 100000 * MILLIS_IN_HOUR; | 
| 29       let startTime = currentTime; | 30       let startTime = currentTime; | 
| 30       let scheduledTasks = []; | 31       let scheduledTasks = []; | 
| 31 | 32 | 
| 32       // Replace Date.now() function | 33       // Replace Date.now() function | 
| 33       this._origNow = SynchronizerGlobal.Date.now; | 34       this._origNow = SynchronizerGlobal.Date.now; | 
| 34       SynchronizerGlobal.Date.now = function() currentTime; | 35       SynchronizerGlobal.Date.now = DownloaderGlobal.Date.now = function() curre
     ntTime; | 
| 35 | 36 | 
| 36       // Replace Math.random() function | 37       // Replace Math.random() function | 
| 37       this._origRandom = SynchronizerGlobal.Math.random; | 38       this._origRandom = DownloaderGlobal.Math.random; | 
| 38       SynchronizerGlobal.Math.random = function() randomResult; | 39       DownloaderGlobal.Math.random = function() randomResult; | 
| 39 | 40 | 
| 40       // Replace global timer variable | 41       // Replace global timer variable | 
| 41       let timer = {__proto__: SynchronizerModule.timer, delay: 0.1 * MILLIS_IN_H
     OUR}; | 42       let timer = {__proto__: SynchronizerModule.downloader._timer, delay: 0.1 *
      MILLIS_IN_HOUR}; | 
| 42       let callback = timer.callback; | 43       let callback = timer.callback; | 
| 43       timer.handler = function() { callback.notify(timer); }; | 44       timer.handler = function() { callback.notify(timer); }; | 
| 44       timer.nextExecution = currentTime + timer.delay; | 45       timer.nextExecution = currentTime + timer.delay; | 
| 45       scheduledTasks.push(timer); | 46       scheduledTasks.push(timer); | 
| 46       SynchronizerModule.timer.cancel(); | 47       SynchronizerModule.downloader._timer.cancel(); | 
| 47       SynchronizerModule.timer = timer; | 48       SynchronizerModule.downloader._timer = timer; | 
| 48 | 49 | 
| 49       // Register observer to track outstanding requests | 50       // Register observer to track outstanding requests | 
| 50       this._outstandingRequests = 0; | 51       this._outstandingRequests = 0; | 
| 51       Services.obs.addObserver(this, "http-on-modify-request", true); | 52       Services.obs.addObserver(this, "http-on-modify-request", true); | 
| 52       Services.obs.addObserver(this, "http-on-examine-response", true); | 53       Services.obs.addObserver(this, "http-on-examine-response", true); | 
| 53 | 54 | 
| 54       this.runScheduledTasks = function(maxHours, initial, skip) | 55       this.runScheduledTasks = function(maxHours, initial, skip) | 
| 55       { | 56       { | 
| 56         if (typeof maxHours != "number") | 57         if (typeof maxHours != "number") | 
| 57           throw new Error("Numerical parameter expected"); | 58           throw new Error("Numerical parameter expected"); | 
| 58         if (typeof initial != "number") | 59         if (typeof initial != "number") | 
| 59           initial = 0; | 60           initial = 0; | 
| 60         if (typeof skip != "number") | 61         if (typeof skip != "number") | 
| 61           skip = 0; | 62           skip = 0; | 
| 62 | 63 | 
| 63         startTime = currentTime; | 64         startTime = currentTime; | 
| 64         if (initial >= 0) | 65         if (initial >= 0) | 
| 65         { | 66         { | 
| 66           this._runScheduledTasks(initial); | 67           this._runScheduledTasks(initial); | 
| 67           maxHours -= initial; | 68           maxHours -= initial; | 
| 68         } | 69         } | 
| 69         if (skip) | 70         if (skip) | 
| 70         { | 71         { | 
| 71           this._skipTasks(skip); | 72           this._skipTasks(skip); | 
| 72           maxHours -= initial; | 73           maxHours -= skip; | 
| 73         } | 74         } | 
| 74         this._runScheduledTasks(maxHours); | 75         this._runScheduledTasks(maxHours); | 
| 75       } | 76       } | 
| 76 | 77 | 
| 77       this._runScheduledTasks = function(maxHours) | 78       this._runScheduledTasks = function(maxHours) | 
| 78       { | 79       { | 
| 79         let endTime = currentTime + maxHours * MILLIS_IN_HOUR; | 80         let endTime = currentTime + maxHours * MILLIS_IN_HOUR; | 
| 80         while (true) | 81         while (true) | 
| 81         { | 82         { | 
| 82           let nextTask = null; | 83           let nextTask = null; | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 108           else | 109           else | 
| 109             nextTask.nextExecution = currentTime + nextTask.delay; | 110             nextTask.nextExecution = currentTime + nextTask.delay; | 
| 110         } | 111         } | 
| 111 | 112 | 
| 112         currentTime = endTime; | 113         currentTime = endTime; | 
| 113       } | 114       } | 
| 114 | 115 | 
| 115       this._skipTasks = function(hours) | 116       this._skipTasks = function(hours) | 
| 116       { | 117       { | 
| 117         let newTasks = []; | 118         let newTasks = []; | 
| 118         let endTime = currentTime + hours * MILLIS_IN_HOUR; | 119         currentTime += hours * MILLIS_IN_HOUR; | 
| 119         for each (let task in scheduledTasks) | 120         for each (let task in scheduledTasks) | 
| 120         { | 121         { | 
| 121           if (task.nextExecution >= endTime) | 122           if (task.nextExecution >= currentTime) | 
| 122             newTasks.push(task); | 123             newTasks.push(task); | 
| 123           else if (task.type != Components.interfaces.nsITimer.TYPE_ONE_SHOT) | 124           else if (task.type != Components.interfaces.nsITimer.TYPE_ONE_SHOT) | 
| 124           { | 125           { | 
| 125             task.nextExecution = endTime; | 126             task.nextExecution = currentTime; | 
| 126             newTasks.push(task); | 127             newTasks.push(task); | 
| 127           } | 128           } | 
| 128         } | 129         } | 
| 129         scheduledTasks = newTasks; | 130         scheduledTasks = newTasks; | 
| 130       } | 131       } | 
| 131 | 132 | 
| 132       this.getTimeOffset = function() (currentTime - startTime) / MILLIS_IN_HOUR
     ; | 133       this.getTimeOffset = function() (currentTime - startTime) / MILLIS_IN_HOUR
     ; | 
| 133 | 134 | 
| 134       this.__defineGetter__("currentTime", function() currentTime); | 135       this.__defineGetter__("currentTime", function() currentTime); | 
| 135     }, | 136     }, | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
| 151       stop(); | 152       stop(); | 
| 152       server.stop(function() | 153       server.stop(function() | 
| 153       { | 154       { | 
| 154         server = null; | 155         server = null; | 
| 155         start(); | 156         start(); | 
| 156       }); | 157       }); | 
| 157 | 158 | 
| 158       if (this._origNow) | 159       if (this._origNow) | 
| 159       { | 160       { | 
| 160         let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 161         let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 
| 161         SynchronizerGlobal.Date.now = this._origNow; | 162         let SynchronizerModule = getModuleGlobal("synchronizer"); | 
|  | 163         let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.download
     er); | 
|  | 164         SynchronizerGlobal.Date.now = DownloaderGlobal.Date.now = this._origNow; | 
| 162         delete this._origNow; | 165         delete this._origNow; | 
| 163       } | 166       } | 
| 164 | 167 | 
| 165       if (this._origRandom) | 168       if (this._origRandom) | 
| 166       { | 169       { | 
| 167         let SynchronizerGlobal = Cu.getGlobalForObject(Synchronizer); | 170         let SynchronizerModule = getModuleGlobal("synchronizer"); | 
| 168         SynchronizerGlobal.Math.random = this._origRandom; | 171         let DownloaderGlobal = Cu.getGlobalForObject(SynchronizerModule.download
     er); | 
|  | 172         DownloaderGlobal.Math.random = this._origRandom; | 
| 169         delete this._origRandom; | 173         delete this._origRandom; | 
| 170       } | 174       } | 
| 171 | 175 | 
| 172       Synchronizer.init(); | 176       Synchronizer.init(); | 
| 173     } | 177     } | 
| 174   }); | 178   }); | 
| 175 | 179 | 
| 176   function resetSubscription(subscription) | 180   function resetSubscription(subscription) | 
| 177   { | 181   { | 
| 178     FilterStorage.updateSubscriptionFilters(subscription, []); | 182     FilterStorage.updateSubscriptionFilters(subscription, []); | 
| 179     subscription.lastCheck =  subscription.lastDownload = | 183     subscription.lastCheck =  subscription.lastDownload = | 
| 180       subscription.lastSuccess = subscription.expires = | 184       subscription.version = subscription.lastSuccess = | 
| 181       subscription.softExpiration = 0; | 185       subscription.expires = subscription.softExpiration = 0; | 
|  | 186     subscription.title = ""; | 
|  | 187     subscription.homepage = null; | 
| 182     subscription.errors = 0; | 188     subscription.errors = 0; | 
| 183     subscription.downloadStatus = null; | 189     subscription.downloadStatus = null; | 
| 184     subscription.requiredVersion = null; | 190     subscription.requiredVersion = null; | 
| 185     subscription.nextURL = null; |  | 
| 186   } | 191   } | 
| 187 | 192 | 
| 188   test("Downloads of one subscription", function() | 193   test("Downloads of one subscription", function() | 
| 189   { | 194   { | 
| 190     // Always use average download interval | 195     // Always use average download interval | 
| 191     randomResult = 0.5; | 196     randomResult = 0.5; | 
| 192 | 197 | 
| 193     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 198     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
| 194     FilterStorage.addSubscription(subscription); | 199     FilterStorage.addSubscription(subscription); | 
| 195 | 200 | 
| 196     let requests = []; | 201     let requests = []; | 
| 197     function handler(metadata, response) | 202     function handler(metadata, response) | 
| 198     { | 203     { | 
| 199       requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
     ); | 204       requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
     ); | 
| 200 | 205 | 
| 201       response.setStatusLine("1.1", "200", "OK"); | 206       response.setStatusLine("1.1", "200", "OK"); | 
| 202       response.setHeader("Content-Type", "text/plain"); | 207       response.setHeader("Content-Type", "text/plain"); | 
| 203 | 208 | 
| 204       let result = "[Adblock]\nfoo\nbar"; | 209       let result = "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"; | 
| 205       response.bodyOutputStream.write(result, result.length); | 210       response.bodyOutputStream.write(result, result.length); | 
| 206     } | 211     } | 
| 207 | 212 | 
| 208     server.registerPathHandler("/subscription", handler); | 213     server.registerPathHandler("/subscription", handler); | 
| 209 | 214 | 
| 210     testRunner.runScheduledTasks(50); | 215     testRunner.runScheduledTasks(50); | 
| 211     deepEqual(requests, [ | 216     deepEqual(requests, [ | 
| 212       [0.1, "GET", "/subscription"], | 217       [0.1, "GET", "/subscription"], | 
| 213       [24.1, "GET", "/subscription"], | 218       [24.1, "GET", "/subscription"], | 
| 214       [48.1, "GET", "/subscription"], | 219       [48.1, "GET", "/subscription"], | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
| 230     FilterStorage.addSubscription(subscription2); | 235     FilterStorage.addSubscription(subscription2); | 
| 231 | 236 | 
| 232     let requests = []; | 237     let requests = []; | 
| 233     function handler(metadata, response) | 238     function handler(metadata, response) | 
| 234     { | 239     { | 
| 235       requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
     ); | 240       requests.push([testRunner.getTimeOffset(), metadata.method, metadata.path]
     ); | 
| 236 | 241 | 
| 237       response.setStatusLine("1.1", "200", "OK"); | 242       response.setStatusLine("1.1", "200", "OK"); | 
| 238       response.setHeader("Content-Type", "text/plain"); | 243       response.setHeader("Content-Type", "text/plain"); | 
| 239 | 244 | 
| 240       let result = "[Adblock]\nfoo\nbar"; | 245       let result = "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"; | 
| 241       response.bodyOutputStream.write(result, result.length); | 246       response.bodyOutputStream.write(result, result.length); | 
| 242     } | 247     } | 
| 243 | 248 | 
| 244     server.registerPathHandler("/subscription1", handler); | 249     server.registerPathHandler("/subscription1", handler); | 
| 245     server.registerPathHandler("/subscription2", handler); | 250     server.registerPathHandler("/subscription2", handler); | 
| 246 | 251 | 
| 247     testRunner.runScheduledTasks(55); | 252     testRunner.runScheduledTasks(55); | 
| 248     deepEqual(requests, [ | 253     deepEqual(requests, [ | 
| 249       [0.1, "GET", "/subscription1"], | 254       [0.1, "GET", "/subscription1"], | 
| 250       [2.1, "GET", "/subscription2"], | 255       [2.1, "GET", "/subscription2"], | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 264     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 269     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
| 265     FilterStorage.addSubscription(subscription); | 270     FilterStorage.addSubscription(subscription); | 
| 266 | 271 | 
| 267     function handler(metadata, response) | 272     function handler(metadata, response) | 
| 268     { | 273     { | 
| 269       response.setStatusLine("1.1", "200", "OK"); | 274       response.setStatusLine("1.1", "200", "OK"); | 
| 270 | 275 | 
| 271       // Wrong content type shouldn't matter | 276       // Wrong content type shouldn't matter | 
| 272       response.setHeader("Content-Type", "text/xml"); | 277       response.setHeader("Content-Type", "text/xml"); | 
| 273 | 278 | 
| 274       let result = test.header + "\nfoo\n!bar\n\n@@bas\n#bam"; | 279       let result = test.header + "\n!Expires: 8 hours\nfoo\n!bar\n\n@@bas\n#bam"
     ; | 
| 275       response.bodyOutputStream.write(result, result.length); | 280       response.bodyOutputStream.write(result, result.length); | 
| 276     } | 281     } | 
| 277     server.registerPathHandler("/subscription", handler); | 282     server.registerPathHandler("/subscription", handler); | 
| 278 | 283 | 
| 279     let tests = [ | 284     let tests = [ | 
| 280       {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: n
     ull}, | 285       {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: n
     ull}, | 
| 281       {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersi
     on: null}, | 286       {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersi
     on: null}, | 
| 282       {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", require
     dVersion: null}, | 287       {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", require
     dVersion: null}, | 
| 283       {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", require
     dVersion: "0.0.1"}, | 288       {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", require
     dVersion: "0.0.1"}, | 
| 284       {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", required
     Version: "99.9"}, | 289       {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", required
     Version: "99.9"}, | 
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 343       response.setStatusLine("1.1", "200", "OK"); | 348       response.setStatusLine("1.1", "200", "OK"); | 
| 344       response.setHeader("Content-Type", "text/plain"); | 349       response.setHeader("Content-Type", "text/plain"); | 
| 345 | 350 | 
| 346       let result = "[Adblock]\nfoo\n!Expires: " + test.expiration + "\nbar"; | 351       let result = "[Adblock]\nfoo\n!Expires: " + test.expiration + "\nbar"; | 
| 347       response.bodyOutputStream.write(result, result.length); | 352       response.bodyOutputStream.write(result, result.length); | 
| 348     } | 353     } | 
| 349     server.registerPathHandler("/subscription", handler); | 354     server.registerPathHandler("/subscription", handler); | 
| 350 | 355 | 
| 351     let tests = [ | 356     let tests = [ | 
| 352       { | 357       { | 
| 353         expiration: "1 hour",   // Too small, will be corrected | 358         expiration: "default", | 
| 354         randomResult: 0.5, | 359         randomResult: 0.5, | 
| 355         requests: [0.1, 24.1] | 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] | 
| 356       }, | 366       }, | 
| 357       { | 367       { | 
| 358         expiration: "26 hours", | 368         expiration: "26 hours", | 
| 359         randomResult: 0.5, | 369         randomResult: 0.5, | 
| 360         requests: [0.1, 26.1] | 370         requests: [0.1, 26.1] | 
| 361       }, | 371       }, | 
| 362       { | 372       { | 
| 363         expiration: "2 days", | 373         expiration: "2 days", | 
| 364         randomResult: 0.5, | 374         randomResult: 0.5, | 
| 365         requests: [0.1, 48.1] | 375         requests: [0.1, 48.1] | 
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 406         requests: [0.1, 85.1] | 416         requests: [0.1, 85.1] | 
| 407       } | 417       } | 
| 408     ] | 418     ] | 
| 409 | 419 | 
| 410     for each (test in tests) | 420     for each (test in tests) | 
| 411     { | 421     { | 
| 412       requests = []; | 422       requests = []; | 
| 413       randomResult = test.randomResult; | 423       randomResult = test.randomResult; | 
| 414       resetSubscription(subscription); | 424       resetSubscription(subscription); | 
| 415 | 425 | 
| 416       let maxHours = Math.round(Math.max.apply(null, test.requests)) + 12; | 426       let maxHours = Math.round(Math.max.apply(null, test.requests)) + 1; | 
| 417       testRunner.runScheduledTasks(maxHours, test.skipAfter, test.skip); | 427       testRunner.runScheduledTasks(maxHours, test.skipAfter, test.skip); | 
| 418 | 428 | 
| 419       let randomAddendum = (randomResult == 0.5 ? "" : " with Math.random() retu
     rning " + randomResult); | 429       let randomAddendum = (randomResult == 0.5 ? "" : " with Math.random() retu
     rning " + randomResult); | 
| 420       let skipAddendum = (typeof test.skip != "number" ? "" : " skipping " + tes
     t.skip + " hours after " + test.skipAfter + " hours"); | 430       let skipAddendum = (typeof test.skip != "number" ? "" : " skipping " + tes
     t.skip + " hours after " + test.skipAfter + " hours"); | 
| 421       deepEqual(requests, test.requests, "Requests for \"" + test.expiration + "
     \"" + randomAddendum + skipAddendum); | 431       deepEqual(requests, test.requests, "Requests for \"" + test.expiration + "
     \"" + randomAddendum + skipAddendum); | 
| 422 | 432     } | 
| 423       if (typeof test.skip == "number") | 433   }); | 
| 424       { | 434 | 
| 425         // Ensure that next time synchronizer triggers at time offset 0.1 again | 435   test("Checksum verification", function() | 
| 426         testRunner.runScheduledTasks(0.1); | 436   { | 
| 427       } | 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"); | 
|  | 463 | 
|  | 464       response.bodyOutputStream.write(subscriptionBody, subscriptionBody.length)
     ; | 
|  | 465     } | 
|  | 466     server.registerPathHandler("/subscription", handler); | 
|  | 467 | 
|  | 468     for each ([testName, subscriptionBody, expectedResult] in tests) | 
|  | 469     { | 
|  | 470       resetSubscription(subscription); | 
|  | 471       testRunner.runScheduledTasks(2); | 
|  | 472       equal(subscription.downloadStatus, expectedResult ? "synchronize_ok" : "sy
     nchronize_checksum_mismatch", testName); | 
|  | 473     } | 
|  | 474   }); | 
|  | 475 | 
|  | 476   test("Special comments", function() | 
|  | 477   { | 
|  | 478     // Always use average download interval | 
|  | 479     randomResult = 0.5; | 
|  | 480 | 
|  | 481     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
|  | 482     FilterStorage.addSubscription(subscription); | 
|  | 483 | 
|  | 484     let comment, check; | 
|  | 485     let tests = [ | 
|  | 486       ["! Homepage: http://example.com/", function() equal(subscription.homepage
     , "http://example.com/", "Valid homepage comment")], | 
|  | 487       ["! Homepage: ssh://example.com/", function() equal(subscription.homepage,
      null, "Invalid homepage comment")], | 
|  | 488       ["! Title: foo", function() | 
|  | 489         { | 
|  | 490           equal(subscription.title, "foo", "Title comment"); | 
|  | 491           equal(subscription.fixedTitle, true, "Fixed title"); | 
|  | 492         }], | 
|  | 493       ["! Version: 1234", function() equal(subscription.version, 1234, "Version 
     comment")] | 
|  | 494     ]; | 
|  | 495 | 
|  | 496     function handler(metadata, response) | 
|  | 497     { | 
|  | 498       response.setStatusLine("1.1", "200", "OK"); | 
|  | 499       response.setHeader("Content-Type", "text/plain"); | 
|  | 500 | 
|  | 501       let result = "[Adblock]\n" + comment + "\nfoo\nbar"; | 
|  | 502       response.bodyOutputStream.write(result, result.length); | 
|  | 503     } | 
|  | 504     server.registerPathHandler("/subscription", handler); | 
|  | 505 | 
|  | 506     for each([comment, check] in tests) | 
|  | 507     { | 
|  | 508       resetSubscription(subscription); | 
|  | 509       testRunner.runScheduledTasks(2); | 
|  | 510       check(); | 
|  | 511       deepEqual(subscription.filters, [Filter.fromText("foo"), Filter.fromText("
     bar")], "Special comment not added to filters"); | 
| 428     } | 512     } | 
| 429   }); | 513   }); | 
| 430 | 514 | 
| 431   test("Redirects", function() | 515   test("Redirects", function() | 
| 432   { | 516   { | 
|  | 517     // Always use average download interval | 
|  | 518     randomResult = 0.5; | 
|  | 519 | 
| 433     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 520     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
| 434     FilterStorage.addSubscription(subscription); | 521     FilterStorage.addSubscription(subscription); | 
| 435 | 522 | 
| 436     function redirect_handler(metadata, response) | 523     function redirect_handler(metadata, response) | 
| 437     { | 524     { | 
| 438       response.setStatusLine("1.1", "200", "OK"); | 525       response.setStatusLine("1.1", "200", "OK"); | 
| 439       response.setHeader("Content-Type", "text/plain"); | 526       response.setHeader("Content-Type", "text/plain"); | 
| 440 | 527 | 
| 441       let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/redirected\
     nbar"; | 528       let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/redirected\
     nbar"; | 
| 442       response.bodyOutputStream.write(result, result.length); | 529       response.bodyOutputStream.write(result, result.length); | 
| 443     } | 530     } | 
| 444     server.registerPathHandler("/subscription", redirect_handler); | 531     server.registerPathHandler("/subscription", redirect_handler); | 
| 445 | 532 | 
| 446     testRunner.runScheduledTasks(50); | 533     testRunner.runScheduledTasks(30); | 
| 447     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
     n", "Invalid redirect ignored"); | 534     equal(FilterStorage.subscriptions[0], subscription, "Invalid redirect ignore
     d"); | 
|  | 535     equal(subscription.downloadStatus, "synchronize_connection_error", "Connecti
     on error recorded"); | 
|  | 536     equal(subscription.errors, 2, "Number of download errors"); | 
| 448 | 537 | 
| 449     let requests = []; | 538     let requests = []; | 
| 450     function handler(metadata, response) | 539     function handler(metadata, response) | 
| 451     { | 540     { | 
| 452       requests.push(testRunner.getTimeOffset()); | 541       requests.push(testRunner.getTimeOffset()); | 
| 453 | 542 | 
| 454       response.setStatusLine("1.1", "200", "OK"); | 543       response.setStatusLine("1.1", "200", "OK"); | 
| 455       response.setHeader("Content-Type", "text/plain"); | 544       response.setHeader("Content-Type", "text/plain"); | 
| 456 | 545 | 
| 457       let result = "[Adblock]\nfoo\nbar"; | 546       let result = "[Adblock]\nfoo\n! Expires: 8 hours\nbar"; | 
| 458       response.bodyOutputStream.write(result, result.length); | 547       response.bodyOutputStream.write(result, result.length); | 
| 459     } | 548     } | 
| 460     server.registerPathHandler("/redirected", handler); | 549     server.registerPathHandler("/redirected", handler); | 
| 461 | 550 | 
| 462     resetSubscription(subscription); | 551     resetSubscription(subscription); | 
| 463     testRunner.runScheduledTasks(50); | 552     testRunner.runScheduledTasks(15); | 
| 464     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
     , "Redirect followed"); | 553     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
     , "Redirect followed"); | 
| 465     deepEqual(requests, [24.1, 48.1], "Resulting requests"); | 554     deepEqual(requests, [0.1, 8.1], "Resulting requests"); | 
|  | 555 | 
|  | 556     server.registerPathHandler("/redirected", function(metadata, response) | 
|  | 557     { | 
|  | 558       response.setStatusLine("1.1", "200", "OK"); | 
|  | 559       response.setHeader("Content-Type", "text/plain"); | 
|  | 560 | 
|  | 561       let result = "[Adblock]\nfoo\n!Redirect: http://127.0.0.1:1234/subscriptio
     n\nbar"; | 
|  | 562       response.bodyOutputStream.write(result, result.length); | 
|  | 563     }) | 
|  | 564 | 
|  | 565     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
|  | 566     resetSubscription(subscription); | 
|  | 567     FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); | 
|  | 568     FilterStorage.addSubscription(subscription); | 
|  | 569 | 
|  | 570     testRunner.runScheduledTasks(2); | 
|  | 571     equal(FilterStorage.subscriptions[0], subscription, "Redirect not followed o
     n redirect loop"); | 
|  | 572     equal(subscription.downloadStatus, "synchronize_connection_error", "Download
      status after redirect loop"); | 
| 466   }); | 573   }); | 
| 467 | 574 | 
| 468   test("Fallback", function() | 575   test("Fallback", function() | 
| 469   { | 576   { | 
|  | 577     // Always use average download interval | 
|  | 578     randomResult = 0.5; | 
|  | 579 | 
| 470     Prefs.subscriptions_fallbackerrors = 3; | 580     Prefs.subscriptions_fallbackerrors = 3; | 
| 471     Prefs.subscriptions_fallbackurl = "http://127.0.0.1:1234/fallback?%URL%&%CHA
     NNELSTATUS%&%RESPONSESTATUS%"; | 581     Prefs.subscriptions_fallbackurl = "http://127.0.0.1:1234/fallback?%SUBSCRIPT
     ION%&%CHANNELSTATUS%&%RESPONSESTATUS%"; | 
| 472 | 582 | 
| 473     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 583     let subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"
     ); | 
| 474     FilterStorage.addSubscription(subscription); | 584     FilterStorage.addSubscription(subscription); | 
|  | 585 | 
|  | 586     // No valid response from fallback | 
| 475 | 587 | 
| 476     let requests = []; | 588     let requests = []; | 
| 477     function handler(metadata, response) | 589     function handler(metadata, response) | 
| 478     { | 590     { | 
| 479       requests.push(testRunner.getTimeOffset()); | 591       requests.push(testRunner.getTimeOffset()); | 
| 480 | 592 | 
| 481       response.setStatusLine("1.1", "404", "Not found"); | 593       response.setStatusLine("1.1", "404", "Not found"); | 
| 482     } | 594     } | 
| 483     server.registerPathHandler("/subscription", handler); | 595     server.registerPathHandler("/subscription", handler); | 
| 484 | 596 | 
| 485     testRunner.runScheduledTasks(100); | 597     testRunner.runScheduledTasks(100); | 
| 486     deepEqual(requests, [0.1, 24.1, 48.1, 72.1, 96.1], "Continue trying if the f
     allback doesn't respond"); | 598     deepEqual(requests, [0.1, 24.1, 48.1, 72.1, 96.1], "Continue trying if the f
     allback doesn't respond"); | 
|  | 599 | 
|  | 600     // Fallback giving "Gone" response | 
| 487 | 601 | 
| 488     resetSubscription(subscription); | 602     resetSubscription(subscription); | 
| 489     requests = []; | 603     requests = []; | 
| 490     fallbackParams = null; | 604     fallbackParams = null; | 
| 491     server.registerPathHandler("/fallback", function(metadata, response) | 605     server.registerPathHandler("/fallback", function(metadata, response) | 
| 492     { | 606     { | 
| 493       response.setStatusLine("1.1", "200", "OK"); | 607       response.setStatusLine("1.1", "200", "OK"); | 
| 494       fallbackParams = decodeURIComponent(metadata.queryString); | 608       fallbackParams = decodeURIComponent(metadata.queryString); | 
| 495 | 609 | 
| 496       let result = "410 Gone"; | 610       let result = "410 Gone"; | 
| 497       response.bodyOutputStream.write(result, result.length); | 611       response.bodyOutputStream.write(result, result.length); | 
| 498     }); | 612     }); | 
| 499 | 613 | 
| 500     testRunner.runScheduledTasks(100); | 614     testRunner.runScheduledTasks(100); | 
| 501     deepEqual(requests, [0.1, 24.1, 48.1], "Stop trying if the fallback responds
      with Gone"); | 615     deepEqual(requests, [0.1, 24.1, 48.1], "Stop trying if the fallback responds
      with Gone"); | 
| 502     equal(fallbackParams, "http://127.0.0.1:1234/subscription&0&404"); | 616     equal(fallbackParams, "http://127.0.0.1:1234/subscription&0&404", "Fallback 
     arguments"); | 
| 503 | 617 | 
|  | 618     // Fallback redirecting to a missing file | 
|  | 619 | 
|  | 620     subscription = Subscription.fromURL("http://127.0.0.1:1234/subscription"); | 
| 504     resetSubscription(subscription); | 621     resetSubscription(subscription); | 
| 505     FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); | 622     FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); | 
| 506     FilterStorage.addSubscription(subscription); | 623     FilterStorage.addSubscription(subscription); | 
| 507     requests = []; | 624     requests = []; | 
| 508 | 625 | 
| 509     server.registerPathHandler("/fallback", function(metadata, response) | 626     server.registerPathHandler("/fallback", function(metadata, response) | 
| 510     { | 627     { | 
| 511       response.setStatusLine("1.1", "200", "OK"); | 628       response.setStatusLine("1.1", "200", "OK"); | 
| 512 | 629 | 
| 513       let result = "301 http://127.0.0.1:1234/redirected"; | 630       let result = "301 http://127.0.0.1:1234/redirected"; | 
| 514       response.bodyOutputStream.write(result, result.length); | 631       response.bodyOutputStream.write(result, result.length); | 
| 515     }); | 632     }); | 
| 516     testRunner.runScheduledTasks(100); | 633     testRunner.runScheduledTasks(100); | 
| 517     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
     n"); | 634     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/subscriptio
     n", "Ignore invalid redirect from fallback"); | 
| 518     deepEqual(requests, [0.1, 24.1, 48.1, 96.1], "Come back after invalid redire
     ct 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 | 
| 519 | 638 | 
| 520     resetSubscription(subscription); | 639     resetSubscription(subscription); | 
| 521     requests = []; | 640     requests = []; | 
| 522     let redirectedRequests = []; | 641     let redirectedRequests = []; | 
| 523     server.registerPathHandler("/redirected", function(metadata, response) | 642     server.registerPathHandler("/redirected", function(metadata, response) | 
| 524     { | 643     { | 
| 525       redirectedRequests.push(testRunner.getTimeOffset()); | 644       redirectedRequests.push(testRunner.getTimeOffset()); | 
| 526 | 645 | 
| 527       response.setStatusLine("1.1", "200", "OK"); | 646       response.setStatusLine("1.1", "200", "OK"); | 
| 528       response.setHeader("Content-Type", "text/plain"); | 647       response.setHeader("Content-Type", "text/plain"); | 
| 529 | 648 | 
| 530       let result = "[Adblock]\nfoo\nbar"; | 649       let result = "[Adblock]\n!Expires: 1day\nfoo\nbar"; | 
| 531       response.bodyOutputStream.write(result, result.length); | 650       response.bodyOutputStream.write(result, result.length); | 
| 532     }); | 651     }); | 
| 533 | 652 | 
| 534     testRunner.runScheduledTasks(100); | 653     testRunner.runScheduledTasks(100); | 
| 535     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
     ); | 654     equal(FilterStorage.subscriptions[0].url, "http://127.0.0.1:1234/redirected"
     , "Valid redirect from fallback is followed"); | 
| 536     deepEqual(requests, [0.1, 24.1, 48.1], "Stop polling original URL after a va
     lid redirect from fallback"); | 655     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"); | 656     deepEqual(redirectedRequests, [48.1, 72.1, 96.1], "Request new URL after a v
     alid redirect from fallback"); | 
| 538   }); | 657 | 
| 539 | 658     // Checksum mismatch | 
| 540   // TODO: Checksum verification | 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   }); | 
| 541 })(); | 745 })(); | 
| LEFT | RIGHT | 
|---|