| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <https://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-present eyeo GmbH |    3  * Copyright (C) 2006-present eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|    6  * it under the terms of the GNU General Public License version 3 as |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   12  * GNU General Public License for more details. |   12  * GNU General Public License for more details. | 
|   13  * |   13  * | 
|   14  * You should have received a copy of the GNU General Public License |   14  * You should have received a copy of the GNU General Public License | 
|   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   16  */ |   16  */ | 
|   17  |   17  | 
|   18 "use strict"; |   18 "use strict"; | 
|   19  |   19  | 
 |   20 const assert = require("assert"); | 
|   20 let { |   21 let { | 
|   21   createSandbox, setupTimerAndFetch, setupRandomResult, unexpectedError, |   22   createSandbox, setupTimerAndFetch, setupRandomResult, | 
|   22   MILLIS_IN_SECOND, MILLIS_IN_HOUR |   23   MILLIS_IN_SECOND, MILLIS_IN_HOUR | 
|   23 } = require("./_common"); |   24 } = require("./_common"); | 
|   24  |   25  | 
|   25 let filterStorage = null; |   26 let filterStorage = null; | 
|   26 let Prefs = null; |   27 let Prefs = null; | 
|   27 let Subscription = null; |   28 let Subscription = null; | 
|   28  |   29  | 
|   29 exports.setUp = function(callback) |   30 describe("Synchronizer", () => | 
|   30 { |   31 { | 
|   31   let globals = Object.assign({}, setupTimerAndFetch.call(this), |   32   let runner = {}; | 
|   32                               setupRandomResult.call(this)); |   33  | 
|   33  |   34   beforeEach(() => | 
|   34   let sandboxedRequire = createSandbox({globals}); |   35   { | 
|   35   ( |   36     runner = {}; | 
|   36     {filterStorage} = sandboxedRequire("../lib/filterStorage"), |   37     let globals = Object.assign({}, setupTimerAndFetch.call(runner), | 
|   37     {Prefs} = sandboxedRequire("./stub-modules/prefs"), |   38                                 setupRandomResult.call(runner)); | 
|   38     {Subscription} = sandboxedRequire("../lib/subscriptionClasses"), |   39  | 
|   39     sandboxedRequire("../lib/synchronizer") |   40     let sandboxedRequire = createSandbox({globals}); | 
|   40   ); |   41     ( | 
|   41  |   42       {filterStorage} = sandboxedRequire("../lib/filterStorage"), | 
|   42   callback(); |   43       {Prefs} = sandboxedRequire("./stub-modules/prefs"), | 
|   43 }; |   44       {Subscription} = sandboxedRequire("../lib/subscriptionClasses"), | 
|   44  |   45       sandboxedRequire("../lib/synchronizer") | 
|   45 function resetSubscription(subscription) |   46     ); | 
|   46 { |   47   }); | 
|   47   filterStorage.updateSubscriptionFilters(subscription, []); |   48  | 
|   48   subscription.lastCheck = subscription.lastDownload = |   49   function resetSubscription(subscription) | 
|   49     subscription.version = subscription.lastSuccess = |   50   { | 
|   50     subscription.expires = subscription.softExpiration = 0; |   51     filterStorage.updateSubscriptionFilters(subscription, []); | 
|   51   subscription.title = ""; |   52     subscription.lastCheck = subscription.lastDownload = | 
|   52   subscription.homepage = null; |   53       subscription.version = subscription.lastSuccess = | 
|   53   subscription.errors = 0; |   54       subscription.expires = subscription.softExpiration = 0; | 
|   54   subscription.downloadStatus = null; |   55     subscription.title = ""; | 
|   55   subscription.requiredVersion = null; |   56     subscription.homepage = null; | 
|   56 } |   57     subscription.errors = 0; | 
|   57  |   58     subscription.downloadStatus = null; | 
|   58 let initialDelay = 1 / 60; |   59     subscription.requiredVersion = null; | 
|   59  |   60   } | 
|   60 exports.testOneSubscriptionDownloads = function(test) |   61  | 
|   61 { |   62   let initialDelay = 1 / 60; | 
|   62   let subscription = Subscription.fromURL("http://example.com/subscription"); |   63  | 
|   63   filterStorage.addSubscription(subscription); |   64   describe("Downloads", () => | 
|   64  |   65   { | 
|   65   let requests = []; |   66     it("One Subscription", () => | 
|   66   this.registerHandler("/subscription", metadata => |   67     { | 
|   67   { |   68       let subscription = Subscription.fromURL("http://example.com/subscription")
     ; | 
|   68     requests.push([this.getTimeOffset(), metadata.method, metadata.path]); |   69       filterStorage.addSubscription(subscription); | 
|   69     return [200, "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"]; |   70  | 
|   70   }); |   71       let requests = []; | 
|   71  |   72       runner.registerHandler("/subscription", metadata => | 
|   72   this.runScheduledTasks(50).then(() => |   73       { | 
|   73   { |   74         requests.push([runner.getTimeOffset(), metadata.method, metadata.path]); | 
|   74     test.deepEqual(requests, [ |   75         return [200, "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"]; | 
|   75       [0 + initialDelay, "GET", "/subscription"], |   76       }); | 
|   76       [24 + initialDelay, "GET", "/subscription"], |   77  | 
|   77       [48 + initialDelay, "GET", "/subscription"] |   78       return runner.runScheduledTasks(50).then(() => | 
|   78     ], "Requests after 50 hours"); |   79       { | 
|   79   }).catch(unexpectedError.bind(test)).then(() => test.done()); |   80         assert.deepEqual(requests, [ | 
|   80 }; |   81           [0 + initialDelay, "GET", "/subscription"], | 
|   81  |   82           [24 + initialDelay, "GET", "/subscription"], | 
|   82 exports.testTwoSubscriptionsDownloads = function(test) |   83           [48 + initialDelay, "GET", "/subscription"] | 
|   83 { |   84         ], "Requests after 50 hours"); | 
|   84   let subscription1 = Subscription.fromURL("http://example.com/subscription1"); |   85       }); | 
|   85   filterStorage.addSubscription(subscription1); |   86     }); | 
|   86  |   87  | 
|   87   let subscription2 = Subscription.fromURL("http://example.com/subscription2"); |   88     it("Two Subscriptions", () => | 
|   88   subscription2.expires = |   89     { | 
|   89     subscription2.softExpiration = |   90       let subscription1 = Subscription.fromURL("http://example.com/subscription1
     "); | 
|   90     (this.currentTime + 2 * MILLIS_IN_HOUR) / MILLIS_IN_SECOND; |   91       filterStorage.addSubscription(subscription1); | 
|   91   filterStorage.addSubscription(subscription2); |   92  | 
|   92  |   93       let subscription2 = Subscription.fromURL("http://example.com/subscription2
     "); | 
|   93   let requests = []; |   94       subscription2.expires = | 
|   94   let handler = metadata => |   95         subscription2.softExpiration = | 
|   95   { |   96         (runner.currentTime + 2 * MILLIS_IN_HOUR) / MILLIS_IN_SECOND; | 
|   96     requests.push([this.getTimeOffset(), metadata.method, metadata.path]); |   97       filterStorage.addSubscription(subscription2); | 
|   97     return [200, "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"]; |   98  | 
|   98   }; |   99       let requests = []; | 
|   99  |  100       let handler = metadata => | 
|  100   this.registerHandler("/subscription1", handler); |  101       { | 
|  101   this.registerHandler("/subscription2", handler); |  102         requests.push([runner.getTimeOffset(), metadata.method, metadata.path]); | 
|  102  |  103         return [200, "[Adblock]\n! ExPiREs: 1day\nfoo\nbar"]; | 
|  103   this.runScheduledTasks(55).then(() => |  104       }; | 
|  104   { |  105  | 
|  105     test.deepEqual(requests, [ |  106       runner.registerHandler("/subscription1", handler); | 
|  106       [0 + initialDelay, "GET", "/subscription1"], |  107       runner.registerHandler("/subscription2", handler); | 
|  107       [2 + initialDelay, "GET", "/subscription2"], |  108  | 
|  108       [24 + initialDelay, "GET", "/subscription1"], |  109       return runner.runScheduledTasks(55).then(() => | 
|  109       [26 + initialDelay, "GET", "/subscription2"], |  110       { | 
|  110       [48 + initialDelay, "GET", "/subscription1"], |  111         assert.deepEqual(requests, [ | 
|  111       [50 + initialDelay, "GET", "/subscription2"] |  112           [0 + initialDelay, "GET", "/subscription1"], | 
|  112     ], "Requests after 55 hours"); |  113           [2 + initialDelay, "GET", "/subscription2"], | 
|  113   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  114           [24 + initialDelay, "GET", "/subscription1"], | 
|  114 }; |  115           [26 + initialDelay, "GET", "/subscription2"], | 
|  115  |  116           [48 + initialDelay, "GET", "/subscription1"], | 
|  116 exports.testSubscriptionHeaders = {}; |  117           [50 + initialDelay, "GET", "/subscription2"] | 
|  117  |  118         ], "Requests after 55 hours"); | 
|  118 for (let currentTest of [ |  119       }); | 
|  119   {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: null}
     , |  120     }); | 
|  120   {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersion: 
     null}, |  121   }); | 
|  121   {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", requiredVer
     sion: null}, |  122  | 
|  122   {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", requiredVer
     sion: "0.0.1"}, |  123   describe("Subscription Headers", () => | 
|  123   {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", requiredVers
     ion: "99.9"}, |  124   { | 
|  124   {header: "[Foo]", downloadStatus: "synchronize_invalid_data", requiredVersion:
      null} |  125     for (let currentTest of [ | 
|  125 ]) |  126       {header: "[Adblock]", downloadStatus: "synchronize_ok", requiredVersion: n
     ull}, | 
|  126 { |  127       {header: "[Adblock Plus]", downloadStatus: "synchronize_ok", requiredVersi
     on: null}, | 
|  127   exports.testSubscriptionHeaders[currentTest.header] = function(test) |  128       {header: "(something)[Adblock]", downloadStatus: "synchronize_ok", require
     dVersion: null}, | 
|  128   { |  129       {header: "[Adblock Plus 0.0.1]", downloadStatus: "synchronize_ok", require
     dVersion: "0.0.1"}, | 
 |  130       {header: "[Adblock Plus 99.9]", downloadStatus: "synchronize_ok", required
     Version: "99.9"}, | 
 |  131       {header: "[Foo]", downloadStatus: "synchronize_invalid_data", requiredVers
     ion: null} | 
 |  132     ]) | 
 |  133     { | 
 |  134       it(currentTest.header, () => | 
 |  135       { | 
 |  136         let subscription = Subscription.fromURL("http://example.com/subscription
     "); | 
 |  137         filterStorage.addSubscription(subscription); | 
 |  138  | 
 |  139         runner.registerHandler("/subscription", metadata => | 
 |  140         { | 
 |  141           return [200, currentTest.header + "\n!Expires: 8 hours\nfoo\n!bar\n\n@
     @bas\n#bam"]; | 
 |  142         }); | 
 |  143  | 
 |  144         return runner.runScheduledTasks(2).then(() => | 
 |  145         { | 
 |  146           assert.equal(subscription.downloadStatus, currentTest.downloadStatus, 
     "Download status"); | 
 |  147           assert.equal(subscription.requiredVersion, currentTest.requiredVersion
     , "Required version"); | 
 |  148  | 
 |  149           if (currentTest.downloadStatus == "synchronize_ok") | 
 |  150           { | 
 |  151             assert.deepEqual([...subscription.filterText()], ["foo", "!bar", "@@
     bas", "#bam"], "Resulting subscription filters"); | 
 |  152           } | 
 |  153           else | 
 |  154           { | 
 |  155             assert.deepEqual([...subscription.filterText()], [ | 
 |  156             ], "Resulting subscription filters"); | 
 |  157           } | 
 |  158         }); | 
 |  159       }); | 
 |  160     } | 
 |  161   }); | 
 |  162  | 
 |  163   it("Disable Updates", () => | 
 |  164   { | 
 |  165     Prefs.subscriptions_autoupdate = false; | 
 |  166  | 
|  129     let subscription = Subscription.fromURL("http://example.com/subscription"); |  167     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  130     filterStorage.addSubscription(subscription); |  168     filterStorage.addSubscription(subscription); | 
|  131  |  169  | 
|  132     this.registerHandler("/subscription", metadata => |  170     let requests = 0; | 
|  133     { |  171     runner.registerHandler("/subscription", metadata => | 
|  134       return [200, currentTest.header + "\n!Expires: 8 hours\nfoo\n!bar\n\n@@bas
     \n#bam"]; |  172     { | 
|  135     }); |  173       requests++; | 
|  136  |  174       throw new Error("Unexpected request"); | 
|  137     this.runScheduledTasks(2).then(() => |  175     }); | 
|  138     { |  176  | 
|  139       test.equal(subscription.downloadStatus, currentTest.downloadStatus, "Downl
     oad status"); |  177     return runner.runScheduledTasks(50).then(() => | 
|  140       test.equal(subscription.requiredVersion, currentTest.requiredVersion, "Req
     uired version"); |  178     { | 
|  141  |  179       assert.equal(requests, 0, "Request count"); | 
|  142       if (currentTest.downloadStatus == "synchronize_ok") |  180     }); | 
|  143       { |  181   }); | 
|  144         test.deepEqual([...subscription.filterText()], ["foo", "!bar", "@@bas", 
     "#bam"], "Resulting subscription filters"); |  182  | 
 |  183   describe("Expiration time", () => | 
 |  184   { | 
 |  185     for (let currentTest of [ | 
 |  186       { | 
 |  187         expiration: "default", | 
 |  188         randomResult: 0.5, | 
 |  189         requests: [0 + initialDelay, 5 * 24 + initialDelay] | 
 |  190       }, | 
 |  191       { | 
 |  192         expiration: "1 hours",  // Minimal expiration interval | 
 |  193         randomResult: 0.5, | 
 |  194         requests: [0 + initialDelay, 1 + initialDelay, 2 + initialDelay, 3 + ini
     tialDelay] | 
 |  195       }, | 
 |  196       { | 
 |  197         expiration: "26 hours", | 
 |  198         randomResult: 0.5, | 
 |  199         requests: [0 + initialDelay, 26 + initialDelay] | 
 |  200       }, | 
 |  201       { | 
 |  202         expiration: "2 days", | 
 |  203         randomResult: 0.5, | 
 |  204         requests: [0 + initialDelay, 48 + initialDelay] | 
 |  205       }, | 
 |  206       { | 
 |  207         expiration: "20 days",  // Too large, will be corrected | 
 |  208         randomResult: 0.5, | 
 |  209         requests: [0 + initialDelay, 14 * 24 + initialDelay] | 
 |  210       }, | 
 |  211       { | 
 |  212         expiration: "35 hours", | 
 |  213         randomResult: 0,        // Changes interval by factor 0.8 | 
 |  214         requests: [0 + initialDelay, 28 + initialDelay] | 
 |  215       }, | 
 |  216       { | 
 |  217         expiration: "35 hours", | 
 |  218         randomResult: 1,        // Changes interval by factor 1.2 | 
 |  219         requests: [0 + initialDelay, 42 + initialDelay] | 
 |  220       }, | 
 |  221       { | 
 |  222         expiration: "35 hours", | 
 |  223         randomResult: 0.25,     // Changes interval by factor 0.9 | 
 |  224         requests: [0 + initialDelay, 32 + initialDelay] | 
 |  225       }, | 
 |  226       { | 
 |  227         expiration: "40 hours", | 
 |  228         randomResult: 0.5, | 
 |  229         skipAfter: 5 + initialDelay, | 
 |  230         skip: 10,               // Short break should not increase soft expirati
     on | 
 |  231         requests: [0 + initialDelay, 40 + initialDelay] | 
 |  232       }, | 
 |  233       { | 
 |  234         expiration: "40 hours", | 
 |  235         randomResult: 0.5, | 
 |  236         skipAfter: 5 + initialDelay, | 
 |  237         skip: 30,               // Long break should increase soft expiration | 
 |  238         requests: [0 + initialDelay, 70 + initialDelay] | 
 |  239       }, | 
 |  240       { | 
 |  241         expiration: "40 hours", | 
 |  242         randomResult: 0.5, | 
 |  243         skipAfter: 5 + initialDelay, | 
 |  244         skip: 80,               // Hitting hard expiration, immediate download | 
 |  245         requests: [0 + initialDelay, 85 + initialDelay] | 
|  145       } |  246       } | 
|  146       else |  247     ]) | 
|  147       { |  248     { | 
|  148         test.deepEqual([...subscription.filterText()], [ |  249       let testId = `"${currentTest.expiration}"`; | 
|  149         ], "Resulting subscription filters"); |  250       if (currentTest.randomResult != 0.5) | 
|  150       } |  251         testId += ` with Math.random() returning ${currentTest.randomResult}`; | 
|  151     }).catch(unexpectedError.bind(test)).then(() => test.done()); |  252       if (currentTest.skip) | 
|  152   }; |  253         testId += ` skipping ${currentTest.skip} hours after ${currentTest.skipA
     fter} hours`; | 
|  153 } |  254  | 
|  154  |  255       it(testId, () => | 
|  155 exports.testsDisabledUpdates = function(test) |  256       { | 
|  156 { |  257         let subscription = Subscription.fromURL("http://example.com/subscription
     "); | 
|  157   Prefs.subscriptions_autoupdate = false; |  258         filterStorage.addSubscription(subscription); | 
|  158  |  259  | 
|  159   let subscription = Subscription.fromURL("http://example.com/subscription"); |  260         let requests = []; | 
|  160   filterStorage.addSubscription(subscription); |  261         runner.registerHandler("/subscription", metadata => | 
|  161  |  262         { | 
|  162   let requests = 0; |  263           requests.push(runner.getTimeOffset()); | 
|  163   this.registerHandler("/subscription", metadata => |  264           return [200, "[Adblock]\n!Expires: " + currentTest.expiration + "\nbar
     "]; | 
|  164   { |  265         }); | 
|  165     requests++; |  266  | 
|  166     throw new Error("Unexpected request"); |  267         runner.randomResult = currentTest.randomResult; | 
|  167   }); |  268  | 
|  168  |  269         let maxHours = Math.round(Math.max.apply(null, currentTest.requests)) + 
     1; | 
|  169   this.runScheduledTasks(50).then(() => |  270         return runner.runScheduledTasks(maxHours, currentTest.skipAfter, current
     Test.skip).then(() => | 
|  170   { |  271         { | 
|  171     test.equal(requests, 0, "Request count"); |  272           assert.deepEqual(requests, currentTest.requests, "Requests"); | 
|  172   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  273         }); | 
|  173 }; |  274       }); | 
|  174  |  275     } | 
|  175 exports.testExpirationTime = {}; |  276   }); | 
|  176  |  277  | 
|  177 for (let currentTest of [ |  278   describe("Special Comments", () => | 
|  178   { |  279   { | 
|  179     expiration: "default", |  280     for (let [comment, check] of [ | 
|  180     randomResult: 0.5, |  281       ["! Homepage: http://example.com/", subscription => | 
|  181     requests: [0 + initialDelay, 5 * 24 + initialDelay] |  282       { | 
|  182   }, |  283         assert.equal(subscription.homepage, "http://example.com/", "Valid homepa
     ge comment"); | 
|  183   { |  284       }], | 
|  184     expiration: "1 hours",  // Minimal expiration interval |  285       ["! Homepage: ssh://example.com/", subscription => | 
|  185     randomResult: 0.5, |  286       { | 
|  186     requests: [0 + initialDelay, 1 + initialDelay, 2 + initialDelay, 3 + initial
     Delay] |  287         assert.equal(subscription.homepage, null, "Invalid homepage comment"); | 
|  187   }, |  288       }], | 
|  188   { |  289       ["! Title: foo", subscription => | 
|  189     expiration: "26 hours", |  290       { | 
|  190     randomResult: 0.5, |  291         assert.equal(subscription.title, "foo", "Title comment"); | 
|  191     requests: [0 + initialDelay, 26 + initialDelay] |  292         assert.equal(subscription.fixedTitle, true, "Fixed title"); | 
|  192   }, |  293       }], | 
|  193   { |  294       ["! Version: 1234", subscription => | 
|  194     expiration: "2 days", |  295       { | 
|  195     randomResult: 0.5, |  296         assert.equal(subscription.version, 1234, "Version comment"); | 
|  196     requests: [0 + initialDelay, 48 + initialDelay] |  297       }] | 
|  197   }, |  298     ]) | 
|  198   { |  299     { | 
|  199     expiration: "20 days",  // Too large, will be corrected |  300       it(comment, () => | 
|  200     randomResult: 0.5, |  301       { | 
|  201     requests: [0 + initialDelay, 14 * 24 + initialDelay] |  302         let subscription = Subscription.fromURL("http://example.com/subscription
     "); | 
|  202   }, |  303         filterStorage.addSubscription(subscription); | 
|  203   { |  304  | 
|  204     expiration: "35 hours", |  305         runner.registerHandler("/subscription", metadata => | 
|  205     randomResult: 0,        // Changes interval by factor 0.8 |  306         { | 
|  206     requests: [0 + initialDelay, 28 + initialDelay] |  307           return [200, "[Adblock]\n" + comment + "\nfoo\nbar"]; | 
|  207   }, |  308         }); | 
|  208   { |  309  | 
|  209     expiration: "35 hours", |  310         return runner.runScheduledTasks(2).then(() => | 
|  210     randomResult: 1,        // Changes interval by factor 1.2 |  311         { | 
|  211     requests: [0 + initialDelay, 42 + initialDelay] |  312           check(subscription); | 
|  212   }, |  313           assert.deepEqual([...subscription.filterText()], ["foo", "bar"], "Spec
     ial comment not added to filters"); | 
|  213   { |  314         }); | 
|  214     expiration: "35 hours", |  315       }); | 
|  215     randomResult: 0.25,     // Changes interval by factor 0.9 |  316     } | 
|  216     requests: [0 + initialDelay, 32 + initialDelay] |  317   }); | 
|  217   }, |  318  | 
|  218   { |  319   it("Redirects", () => | 
|  219     expiration: "40 hours", |  | 
|  220     randomResult: 0.5, |  | 
|  221     skipAfter: 5 + initialDelay, |  | 
|  222     skip: 10,               // Short break should not increase soft expiration |  | 
|  223     requests: [0 + initialDelay, 40 + initialDelay] |  | 
|  224   }, |  | 
|  225   { |  | 
|  226     expiration: "40 hours", |  | 
|  227     randomResult: 0.5, |  | 
|  228     skipAfter: 5 + initialDelay, |  | 
|  229     skip: 30,               // Long break should increase soft expiration |  | 
|  230     requests: [0 + initialDelay, 70 + initialDelay] |  | 
|  231   }, |  | 
|  232   { |  | 
|  233     expiration: "40 hours", |  | 
|  234     randomResult: 0.5, |  | 
|  235     skipAfter: 5 + initialDelay, |  | 
|  236     skip: 80,               // Hitting hard expiration, immediate download |  | 
|  237     requests: [0 + initialDelay, 85 + initialDelay] |  | 
|  238   } |  | 
|  239 ]) |  | 
|  240 { |  | 
|  241   let testId = `"${currentTest.expiration}"`; |  | 
|  242   if (currentTest.randomResult != 0.5) |  | 
|  243     testId += " with Math.random() returning " + currentTest.randomResult; |  | 
|  244   if (currentTest.skip) |  | 
|  245     testId += " skipping " + currentTest.skip + " hours after " + currentTest.sk
     ipAfter + " hours"; |  | 
|  246   exports.testExpirationTime[testId] = function(test) |  | 
|  247   { |  320   { | 
|  248     let subscription = Subscription.fromURL("http://example.com/subscription"); |  321     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  249     filterStorage.addSubscription(subscription); |  322     filterStorage.addSubscription(subscription); | 
|  250  |  323  | 
|  251     let requests = []; |  324     runner.registerHandler("/subscription", metadata => | 
|  252     this.registerHandler("/subscription", metadata => |  325     { | 
|  253     { |  326       return [200, "[Adblock]\n!Redirect: http://example.com/redirected\nbar"]; | 
|  254       requests.push(this.getTimeOffset()); |  327     }); | 
|  255       return [200, "[Adblock]\n!Expires: " + currentTest.expiration + "\nbar"]; |  328  | 
|  256     }); |  329     let requests; | 
|  257  |  330  | 
|  258     this.randomResult = currentTest.randomResult; |  331     return runner.runScheduledTasks(30).then(() => | 
|  259  |  332     { | 
|  260     let maxHours = Math.round(Math.max.apply(null, currentTest.requests)) + 1; |  333       assert.equal([...filterStorage.subscriptions()][0], subscription, "Invalid
      redirect ignored"); | 
|  261     this.runScheduledTasks(maxHours, currentTest.skipAfter, currentTest.skip).th
     en(() => |  334       assert.equal(subscription.downloadStatus, "synchronize_connection_error", 
     "Connection error recorded"); | 
|  262     { |  335       assert.equal(subscription.errors, 2, "Number of download errors"); | 
|  263       test.deepEqual(requests, currentTest.requests, "Requests"); |  336  | 
|  264     }).catch(unexpectedError.bind(test)).then(() => test.done()); |  337       requests = []; | 
|  265   }; |  338  | 
|  266 } |  339       runner.registerHandler("/redirected", metadata => | 
|  267  |  340       { | 
|  268 exports.testSpecialComments = {}; |  341         requests.push(runner.getTimeOffset()); | 
|  269  |  342         return [200, "[Adblock]\n! Expires: 8 hours\nbar"]; | 
|  270 for (let [comment, check] of [ |  343       }); | 
|  271   ["! Homepage: http://example.com/", (test, subscription) => |  344  | 
|  272   { |  345       resetSubscription(subscription); | 
|  273     test.equal(subscription.homepage, "http://example.com/", "Valid homepage com
     ment"); |  346       return runner.runScheduledTasks(15); | 
|  274   }], |  347     }).then(() => | 
|  275   ["! Homepage: ssh://example.com/", (test, subscription) => |  348     { | 
|  276   { |  349       assert.equal([...filterStorage.subscriptions()][0].url, "http://example.co
     m/redirected", "Redirect followed"); | 
|  277     test.equal(subscription.homepage, null, "Invalid homepage comment"); |  350       assert.deepEqual(requests, [0 + initialDelay, 8 + initialDelay], "Resultin
     g requests"); | 
|  278   }], |  351  | 
|  279   ["! Title: foo", (test, subscription) => |  352       runner.registerHandler("/redirected", metadata => | 
|  280   { |  353       { | 
|  281     test.equal(subscription.title, "foo", "Title comment"); |  354         return [200, "[Adblock]\n!Redirect: http://example.com/subscription\nbar
     "]; | 
|  282     test.equal(subscription.fixedTitle, true, "Fixed title"); |  355       }); | 
|  283   }], |  356  | 
|  284   ["! Version: 1234", (test, subscription) => |  357       subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  285   { |  358       resetSubscription(subscription); | 
|  286     test.equal(subscription.version, 1234, "Version comment"); |  359       filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); | 
|  287   }] |  360       filterStorage.addSubscription(subscription); | 
|  288 ]) |  361  | 
|  289 { |  362       return runner.runScheduledTasks(2); | 
|  290   exports.testSpecialComments[comment] = function(test) |  363     }).then(() => | 
|  291   { |  364     { | 
 |  365       assert.equal([...filterStorage.subscriptions()][0], subscription, "Redirec
     t not followed on redirect loop"); | 
 |  366       assert.equal(subscription.downloadStatus, "synchronize_connection_error", 
     "Download status after redirect loop"); | 
 |  367     }); | 
 |  368   }); | 
 |  369  | 
 |  370   it("Fallback", () => | 
 |  371   { | 
 |  372     Prefs.subscriptions_fallbackerrors = 3; | 
 |  373     Prefs.subscriptions_fallbackurl = "http://example.com/fallback?%SUBSCRIPTION
     %&%RESPONSESTATUS%"; | 
 |  374  | 
|  292     let subscription = Subscription.fromURL("http://example.com/subscription"); |  375     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  293     filterStorage.addSubscription(subscription); |  376     filterStorage.addSubscription(subscription); | 
|  294  |  377  | 
|  295     this.registerHandler("/subscription", metadata => |  378     // No valid response from fallback | 
|  296     { |  379  | 
|  297       return [200, "[Adblock]\n" + comment + "\nfoo\nbar"]; |  380     let requests = []; | 
|  298     }); |  381     let fallbackParams; | 
|  299  |  382     let redirectedRequests; | 
|  300     this.runScheduledTasks(2).then(() => |  383     runner.registerHandler("/subscription", metadata => | 
|  301     { |  384     { | 
|  302       check(test, subscription); |  385       requests.push(runner.getTimeOffset()); | 
|  303       test.deepEqual([...subscription.filterText()], ["foo", "bar"], "Special co
     mment not added to filters"); |  386       return [404, ""]; | 
|  304     }).catch(unexpectedError.bind(test)).then(() => test.done()); |  387     }); | 
|  305   }; |  388  | 
|  306 } |  389     return runner.runScheduledTasks(100).then(() => | 
|  307  |  390     { | 
|  308 exports.testRedirects = function(test) |  391       assert.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + init
     ialDelay, 72 + initialDelay, 96 + initialDelay], "Continue trying if the fallbac
     k doesn't respond"); | 
|  309 { |  392  | 
|  310   let subscription = Subscription.fromURL("http://example.com/subscription"); |  393       // Fallback giving "Gone" response | 
|  311   filterStorage.addSubscription(subscription); |  394  | 
|  312  |  395       resetSubscription(subscription); | 
|  313   this.registerHandler("/subscription", metadata => |  396       requests = []; | 
|  314   { |  397       fallbackParams = null; | 
|  315     return [200, "[Adblock]\n!Redirect: http://example.com/redirected\nbar"]; |  398       runner.registerHandler("/fallback", metadata => | 
|  316   }); |  399       { | 
|  317  |  400         fallbackParams = decodeURIComponent(metadata.queryString); | 
|  318   let requests; |  401         return [200, "410 Gone"]; | 
|  319  |  402       }); | 
|  320   this.runScheduledTasks(30).then(() => |  403  | 
|  321   { |  404       return runner.runScheduledTasks(100); | 
|  322     test.equal([...filterStorage.subscriptions()][0], subscription, "Invalid red
     irect ignored"); |  405     }).then(() => | 
|  323     test.equal(subscription.downloadStatus, "synchronize_connection_error", "Con
     nection error recorded"); |  406     { | 
|  324     test.equal(subscription.errors, 2, "Number of download errors"); |  407       assert.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + init
     ialDelay], "Stop trying if the fallback responds with Gone"); | 
|  325  |  408       assert.equal(fallbackParams, "http://example.com/subscription&404", "Fallb
     ack arguments"); | 
|  326     requests = []; |  409  | 
|  327  |  410       // Fallback redirecting to a missing file | 
|  328     this.registerHandler("/redirected", metadata => |  411  | 
|  329     { |  412       subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  330       requests.push(this.getTimeOffset()); |  413       resetSubscription(subscription); | 
|  331       return [200, "[Adblock]\n! Expires: 8 hours\nbar"]; |  414       filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); | 
|  332     }); |  415       filterStorage.addSubscription(subscription); | 
|  333  |  416       requests = []; | 
|  334     resetSubscription(subscription); |  417  | 
|  335     return this.runScheduledTasks(15); |  418       runner.registerHandler("/fallback", metadata => | 
|  336   }).then(() => |  419       { | 
|  337   { |  420         return [200, "301 http://example.com/redirected"]; | 
|  338     test.equal([...filterStorage.subscriptions()][0].url, "http://example.com/re
     directed", "Redirect followed"); |  421       }); | 
|  339     test.deepEqual(requests, [0 + initialDelay, 8 + initialDelay], "Resulting re
     quests"); |  422       return runner.runScheduledTasks(100); | 
|  340  |  423     }).then(() => | 
|  341     this.registerHandler("/redirected", metadata => |  424     { | 
|  342     { |  425       assert.equal([...filterStorage.subscriptions()][0].url, "http://example.co
     m/subscription", "Ignore invalid redirect from fallback"); | 
|  343       return [200, "[Adblock]\n!Redirect: http://example.com/subscription\nbar"]
     ; |  426       assert.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + init
     ialDelay, 72 + initialDelay, 96 + initialDelay], "Requests not affected by inval
     id redirect"); | 
|  344     }); |  427  | 
|  345  |  428       // Fallback redirecting to an existing file | 
|  346     subscription = Subscription.fromURL("http://example.com/subscription"); |  429  | 
|  347     resetSubscription(subscription); |  430       resetSubscription(subscription); | 
|  348     filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); |  431       requests = []; | 
 |  432       redirectedRequests = []; | 
 |  433       runner.registerHandler("/redirected", metadata => | 
 |  434       { | 
 |  435         redirectedRequests.push(runner.getTimeOffset()); | 
 |  436         return [200, "[Adblock]\n!Expires: 1day\nfoo\nbar"]; | 
 |  437       }); | 
 |  438  | 
 |  439       return runner.runScheduledTasks(100); | 
 |  440     }).then(() => | 
 |  441     { | 
 |  442       assert.equal([...filterStorage.subscriptions()][0].url, "http://example.co
     m/redirected", "Valid redirect from fallback is followed"); | 
 |  443       assert.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + init
     ialDelay], "Stop polling original URL after a valid redirect from fallback"); | 
 |  444       assert.deepEqual(redirectedRequests, [48 + initialDelay, 72 + initialDelay
     , 96 + initialDelay], "Request new URL after a valid redirect from fallback"); | 
 |  445  | 
 |  446       // Redirect loop | 
 |  447  | 
 |  448       runner.registerHandler("/subscription", metadata => | 
 |  449       { | 
 |  450         return [200, "[Adblock]\n! Redirect: http://example.com/subscription2"]; | 
 |  451       }); | 
 |  452       runner.registerHandler("/subscription2", metadata => | 
 |  453       { | 
 |  454         return [200, "[Adblock]\n! Redirect: http://example.com/subscription"]; | 
 |  455       }); | 
 |  456  | 
 |  457       subscription = Subscription.fromURL("http://example.com/subscription"); | 
 |  458       resetSubscription(subscription); | 
 |  459       filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); | 
 |  460       filterStorage.addSubscription(subscription); | 
 |  461  | 
 |  462       return runner.runScheduledTasks(100); | 
 |  463     }).then(() => | 
 |  464     { | 
 |  465       assert.equal([...filterStorage.subscriptions()][0].url, "http://example.co
     m/redirected", "Fallback can still redirect even after a redirect loop"); | 
 |  466     }); | 
 |  467   }); | 
 |  468  | 
 |  469   it("State Fields", () => | 
 |  470   { | 
 |  471     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  349     filterStorage.addSubscription(subscription); |  472     filterStorage.addSubscription(subscription); | 
|  350  |  473  | 
|  351     return this.runScheduledTasks(2); |  474     runner.registerHandler("/subscription", metadata => | 
|  352   }).then(() => |  475     { | 
|  353   { |  476       return [200, "[Adblock]\n! Expires: 2 hours\nfoo\nbar"]; | 
|  354     test.equal([...filterStorage.subscriptions()][0], subscription, "Redirect no
     t followed on redirect loop"); |  477     }); | 
|  355     test.equal(subscription.downloadStatus, "synchronize_connection_error", "Dow
     nload status after redirect loop"); |  478  | 
|  356   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  479     let startTime = runner.currentTime; | 
|  357 }; |  480     return runner.runScheduledTasks(2).then(() => | 
|  358  |  481     { | 
|  359 exports.testFallback = function(test) |  482       assert.equal(subscription.downloadStatus, "synchronize_ok", "downloadStatu
     s after successful download"); | 
|  360 { |  483       assert.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + ini
     tialDelay * MILLIS_IN_HOUR, "lastDownload after successful download"); | 
|  361   Prefs.subscriptions_fallbackerrors = 3; |  484       assert.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + init
     ialDelay * MILLIS_IN_HOUR, "lastSuccess after successful download"); | 
|  362   Prefs.subscriptions_fallbackurl = "http://example.com/fallback?%SUBSCRIPTION%&
     %RESPONSESTATUS%"; |  485       assert.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (1 + i
     nitialDelay) * MILLIS_IN_HOUR, "lastCheck after successful download"); | 
|  363  |  486       assert.equal(subscription.errors, 0, "errors after successful download"); | 
|  364   let subscription = Subscription.fromURL("http://example.com/subscription"); |  487  | 
|  365   filterStorage.addSubscription(subscription); |  488       runner.registerHandler("/subscription", metadata => | 
|  366  |  489       { | 
|  367   // No valid response from fallback |  490         return [0, ""]; | 
|  368  |  491       }); | 
|  369   let requests = []; |  492  | 
|  370   let fallbackParams; |  493       return runner.runScheduledTasks(2); | 
|  371   let redirectedRequests; |  494     }).then(() => | 
|  372   this.registerHandler("/subscription", metadata => |  495     { | 
|  373   { |  496       assert.equal(subscription.downloadStatus, "synchronize_connection_error", 
     "downloadStatus after connection error"); | 
|  374     requests.push(this.getTimeOffset()); |  497       assert.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + (2 
     + initialDelay) * MILLIS_IN_HOUR, "lastDownload after connection error"); | 
|  375     return [404, ""]; |  498       assert.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + init
     ialDelay * MILLIS_IN_HOUR, "lastSuccess after connection error"); | 
|  376   }); |  499       assert.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (3 + i
     nitialDelay) * MILLIS_IN_HOUR, "lastCheck after connection error"); | 
|  377  |  500       assert.equal(subscription.errors, 1, "errors after connection error"); | 
|  378   this.runScheduledTasks(100).then(() => |  501  | 
|  379   { |  502       runner.registerHandler("/subscription", metadata => | 
|  380     test.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + initialD
     elay, 72 + initialDelay, 96 + initialDelay], "Continue trying if the fallback do
     esn't respond"); |  503       { | 
|  381  |  504         return [404, ""]; | 
|  382     // Fallback giving "Gone" response |  505       }); | 
|  383  |  506  | 
|  384     resetSubscription(subscription); |  507       return runner.runScheduledTasks(24); | 
|  385     requests = []; |  508     }).then(() => | 
|  386     fallbackParams = null; |  509     { | 
|  387     this.registerHandler("/fallback", metadata => |  510       assert.equal(subscription.downloadStatus, "synchronize_connection_error", 
     "downloadStatus after download error"); | 
|  388     { |  511       assert.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + (26
      + initialDelay) * MILLIS_IN_HOUR, "lastDownload after download error"); | 
|  389       fallbackParams = decodeURIComponent(metadata.queryString); |  512       assert.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + init
     ialDelay * MILLIS_IN_HOUR, "lastSuccess after download error"); | 
|  390       return [200, "410 Gone"]; |  513       assert.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (27 + 
     initialDelay) * MILLIS_IN_HOUR, "lastCheck after download error"); | 
|  391     }); |  514       assert.equal(subscription.errors, 2, "errors after download error"); | 
|  392  |  515     }); | 
|  393     return this.runScheduledTasks(100); |  516   }); | 
|  394   }).then(() => |  517  | 
|  395   { |  518   it("Special Comment Ordering", () => | 
|  396     test.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + initialD
     elay], "Stop trying if the fallback responds with Gone"); |  519   { | 
|  397     test.equal(fallbackParams, "http://example.com/subscription&404", "Fallback 
     arguments"); |  520     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  398  |  | 
|  399     // Fallback redirecting to a missing file |  | 
|  400  |  | 
|  401     subscription = Subscription.fromURL("http://example.com/subscription"); |  | 
|  402     resetSubscription(subscription); |  | 
|  403     filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); |  | 
|  404     filterStorage.addSubscription(subscription); |  521     filterStorage.addSubscription(subscription); | 
|  405     requests = []; |  522  | 
|  406  |  523     runner.registerHandler("/subscription", metadata => | 
|  407     this.registerHandler("/fallback", metadata => |  524     { | 
|  408     { |  525       return [0, ""]; | 
|  409       return [200, "301 http://example.com/redirected"]; |  526     }); | 
|  410     }); |  527  | 
|  411     return this.runScheduledTasks(100); |  528     return runner.runScheduledTasks(1).then(() => | 
|  412   }).then(() => |  529     { | 
|  413   { |  530       assert.equal(subscription.title, "http://example.com/subscription", "make 
     sure title was not found"); | 
|  414     test.equal([...filterStorage.subscriptions()][0].url, "http://example.com/su
     bscription", "Ignore invalid redirect from fallback"); |  531     }); | 
|  415     test.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + initialD
     elay, 72 + initialDelay, 96 + initialDelay], "Requests not affected by invalid r
     edirect"); |  532   }); | 
|  416  |  533  | 
|  417     // Fallback redirecting to an existing file |  534   it("Unkown Special Comments", () => | 
|  418  |  535   { | 
|  419     resetSubscription(subscription); |  536     let subscription = Subscription.fromURL("http://example.com/subscription"); | 
|  420     requests = []; |  | 
|  421     redirectedRequests = []; |  | 
|  422     this.registerHandler("/redirected", metadata => |  | 
|  423     { |  | 
|  424       redirectedRequests.push(this.getTimeOffset()); |  | 
|  425       return [200, "[Adblock]\n!Expires: 1day\nfoo\nbar"]; |  | 
|  426     }); |  | 
|  427  |  | 
|  428     return this.runScheduledTasks(100); |  | 
|  429   }).then(() => |  | 
|  430   { |  | 
|  431     test.equal([...filterStorage.subscriptions()][0].url, "http://example.com/re
     directed", "Valid redirect from fallback is followed"); |  | 
|  432     test.deepEqual(requests, [0 + initialDelay, 24 + initialDelay, 48 + initialD
     elay], "Stop polling original URL after a valid redirect from fallback"); |  | 
|  433     test.deepEqual(redirectedRequests, [48 + initialDelay, 72 + initialDelay, 96
      + initialDelay], "Request new URL after a valid redirect from fallback"); |  | 
|  434  |  | 
|  435     // Redirect loop |  | 
|  436  |  | 
|  437     this.registerHandler("/subscription", metadata => |  | 
|  438     { |  | 
|  439       return [200, "[Adblock]\n! Redirect: http://example.com/subscription2"]; |  | 
|  440     }); |  | 
|  441     this.registerHandler("/subscription2", metadata => |  | 
|  442     { |  | 
|  443       return [200, "[Adblock]\n! Redirect: http://example.com/subscription"]; |  | 
|  444     }); |  | 
|  445  |  | 
|  446     subscription = Subscription.fromURL("http://example.com/subscription"); |  | 
|  447     resetSubscription(subscription); |  | 
|  448     filterStorage.removeSubscription([...filterStorage.subscriptions()][0]); |  | 
|  449     filterStorage.addSubscription(subscription); |  537     filterStorage.addSubscription(subscription); | 
|  450  |  538  | 
|  451     return this.runScheduledTasks(100); |  539     runner.registerHandler("/subscription", metadata => | 
|  452   }).then(() => |  540     { | 
|  453   { |  541       // To test allowing unknown special comments like `! :`, `!!@#$%^&*() : `,
      and `! Some Unknown Comment : ` | 
|  454     test.equal([...filterStorage.subscriptions()][0].url, "http://example.com/re
     directed", "Fallback can still redirect even after a redirect loop"); |  542       return [200, "[Adblock]\n! :\n! !@#$%^&*() :\n! Some Unknown Comment :\n! 
     Title: foobar\nfoo\nbar"]; | 
|  455   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  543     }); | 
|  456 }; |  544  | 
|  457  |  545     return runner.runScheduledTasks(1).then(() => | 
|  458 exports.testStateFields = function(test) |  546     { | 
|  459 { |  547       assert.equal(subscription.title, "foobar", "make sure title was found"); | 
|  460   let subscription = Subscription.fromURL("http://example.com/subscription"); |  548     }); | 
|  461   filterStorage.addSubscription(subscription); |  549   }); | 
|  462  |  550 }); | 
|  463   this.registerHandler("/subscription", metadata => |  | 
|  464   { |  | 
|  465     return [200, "[Adblock]\n! Expires: 2 hours\nfoo\nbar"]; |  | 
|  466   }); |  | 
|  467  |  | 
|  468   let startTime = this.currentTime; |  | 
|  469   this.runScheduledTasks(2).then(() => |  | 
|  470   { |  | 
|  471     test.equal(subscription.downloadStatus, "synchronize_ok", "downloadStatus af
     ter successful download"); |  | 
|  472     test.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + initial
     Delay * MILLIS_IN_HOUR, "lastDownload after successful download"); |  | 
|  473     test.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + initialD
     elay * MILLIS_IN_HOUR, "lastSuccess after successful download"); |  | 
|  474     test.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (1 + initi
     alDelay) * MILLIS_IN_HOUR, "lastCheck after successful download"); |  | 
|  475     test.equal(subscription.errors, 0, "errors after successful download"); |  | 
|  476  |  | 
|  477     this.registerHandler("/subscription", metadata => |  | 
|  478     { |  | 
|  479       return [0, ""]; |  | 
|  480     }); |  | 
|  481  |  | 
|  482     return this.runScheduledTasks(2); |  | 
|  483   }).then(() => |  | 
|  484   { |  | 
|  485     test.equal(subscription.downloadStatus, "synchronize_connection_error", "dow
     nloadStatus after connection error"); |  | 
|  486     test.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + (2 + in
     itialDelay) * MILLIS_IN_HOUR, "lastDownload after connection error"); |  | 
|  487     test.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + initialD
     elay * MILLIS_IN_HOUR, "lastSuccess after connection error"); |  | 
|  488     test.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (3 + initi
     alDelay) * MILLIS_IN_HOUR, "lastCheck after connection error"); |  | 
|  489     test.equal(subscription.errors, 1, "errors after connection error"); |  | 
|  490  |  | 
|  491     this.registerHandler("/subscription", metadata => |  | 
|  492     { |  | 
|  493       return [404, ""]; |  | 
|  494     }); |  | 
|  495  |  | 
|  496     return this.runScheduledTasks(24); |  | 
|  497   }).then(() => |  | 
|  498   { |  | 
|  499     test.equal(subscription.downloadStatus, "synchronize_connection_error", "dow
     nloadStatus after download error"); |  | 
|  500     test.equal(subscription.lastDownload * MILLIS_IN_SECOND, startTime + (26 + i
     nitialDelay) * MILLIS_IN_HOUR, "lastDownload after download error"); |  | 
|  501     test.equal(subscription.lastSuccess * MILLIS_IN_SECOND, startTime + initialD
     elay * MILLIS_IN_HOUR, "lastSuccess after download error"); |  | 
|  502     test.equal(subscription.lastCheck * MILLIS_IN_SECOND, startTime + (27 + init
     ialDelay) * MILLIS_IN_HOUR, "lastCheck after download error"); |  | 
|  503     test.equal(subscription.errors, 2, "errors after download error"); |  | 
|  504   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  | 
|  505 }; |  | 
|  506  |  | 
|  507 exports.testSpecialCommentOrdering = function(test) |  | 
|  508 { |  | 
|  509   let subscription = Subscription.fromURL("http://example.com/subscription"); |  | 
|  510   filterStorage.addSubscription(subscription); |  | 
|  511  |  | 
|  512   this.registerHandler("/subscription", metadata => |  | 
|  513   { |  | 
|  514     return [200, "[Adblock]\n! Special Comment: x\n!foo\n! Title: foobar\nfoo\nb
     ar"]; |  | 
|  515   }); |  | 
|  516  |  | 
|  517   this.runScheduledTasks(1).then(() => |  | 
|  518   { |  | 
|  519     test.equal(subscription.title, "http://example.com/subscription", "make sure
      title was not found"); |  | 
|  520   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  | 
|  521 }; |  | 
|  522  |  | 
|  523 exports.testUnknownSpecialComments = function(test) |  | 
|  524 { |  | 
|  525   let subscription = Subscription.fromURL("http://example.com/subscription"); |  | 
|  526   filterStorage.addSubscription(subscription); |  | 
|  527  |  | 
|  528   this.registerHandler("/subscription", metadata => |  | 
|  529   { |  | 
|  530     // To test allowing unknown special comments like `! :`, `!!@#$%^&*() : `, a
     nd `! Some Unknown Comment : ` |  | 
|  531     return [200, "[Adblock]\n! :\n! !@#$%^&*() :\n! Some Unknown Comment :\n! Ti
     tle: foobar\nfoo\nbar"]; |  | 
|  532   }); |  | 
|  533  |  | 
|  534   this.runScheduledTasks(1).then(() => |  | 
|  535   { |  | 
|  536     test.equal(subscription.title, "foobar", "make sure title was found"); |  | 
|  537   }).catch(unexpectedError.bind(test)).then(() => test.done()); |  | 
|  538 }; |  | 
| OLD | NEW |