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