Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: chrome/content/tests/synchronizer.js

Issue 10964120: Made Synchronizer tests work again (Closed)
Patch Set: Updated for Synchronizer changes Created July 17, 2013, 12:33 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/content/tests/subscriptionClasses.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 })();
OLDNEW
« no previous file with comments | « chrome/content/tests/subscriptionClasses.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld