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