|
1 /* Notes: |
|
2 - All times are expressed in milliseconds in this test suite. |
|
3 - Test harness code is at the end of this file. |
|
4 - We generate only one request at a time, to avoid overloading the HTTP |
|
5 request handlers. |
|
6 */ |
|
7 |
|
8 var inWorker = false; |
|
9 try { |
|
10 inWorker = !(self instanceof Window); |
|
11 } catch (e) { |
|
12 inWorker = true; |
|
13 } |
|
14 |
|
15 function message(data) { |
|
16 if (inWorker) |
|
17 self.postMessage(data); |
|
18 else |
|
19 self.postMessage(data, "*"); |
|
20 } |
|
21 |
|
22 function is(got, expected, msg) { |
|
23 var obj = {}; |
|
24 obj.type = "is"; |
|
25 obj.got = got; |
|
26 obj.expected = expected; |
|
27 obj.msg = msg; |
|
28 |
|
29 message(obj); |
|
30 } |
|
31 |
|
32 function ok(bool, msg) { |
|
33 var obj = {}; |
|
34 obj.type = "ok"; |
|
35 obj.bool = bool; |
|
36 obj.msg = msg; |
|
37 |
|
38 message(obj); |
|
39 } |
|
40 |
|
41 /** |
|
42 * Generate and track results from a XMLHttpRequest with regards to timeouts. |
|
43 * |
|
44 * @param {String} id The test description. |
|
45 * @param {Number} timeLimit The initial setting for the request timeout. |
|
46 * @param {Number} resetAfter (Optional) The time after sending the request, to |
|
47 * reset the timeout. |
|
48 * @param {Number} resetTo (Optional) The delay to reset the timeout to. |
|
49 * |
|
50 * @note The actual testing takes place in handleEvent(event). |
|
51 * The requests are generated in startXHR(). |
|
52 * |
|
53 * @note If resetAfter and resetTo are omitted, only the initial timeout setting |
|
54 * applies. |
|
55 * |
|
56 * @constructor |
|
57 * @implements DOMEventListener |
|
58 */ |
|
59 function RequestTracker(async, id, timeLimit /*[, resetAfter, resetTo]*/) { |
|
60 this.async = async; |
|
61 this.id = id; |
|
62 this.timeLimit = timeLimit; |
|
63 |
|
64 if (arguments.length > 3) { |
|
65 this.mustReset = true; |
|
66 this.resetAfter = arguments[3]; |
|
67 this.resetTo = arguments[4]; |
|
68 } |
|
69 |
|
70 this.hasFired = false; |
|
71 } |
|
72 RequestTracker.prototype = { |
|
73 /** |
|
74 * Start the XMLHttpRequest! |
|
75 */ |
|
76 startXHR: function() { |
|
77 var req = new XMLHttpRequest(); |
|
78 this.request = req; |
|
79 req.open("GET", "file_XHR_timeout.sjs", this.async); |
|
80 var me = this; |
|
81 function handleEvent(e) { return me.handleEvent(e); }; |
|
82 req.onerror = handleEvent; |
|
83 req.onload = handleEvent; |
|
84 req.onabort = handleEvent; |
|
85 req.ontimeout = handleEvent; |
|
86 |
|
87 req.timeout = this.timeLimit; |
|
88 |
|
89 if (this.mustReset) { |
|
90 var resetTo = this.resetTo; |
|
91 self.setTimeout(function() { |
|
92 req.timeout = resetTo; |
|
93 }, this.resetAfter); |
|
94 } |
|
95 |
|
96 req.send(null); |
|
97 }, |
|
98 |
|
99 /** |
|
100 * Get a message describing this test. |
|
101 * |
|
102 * @returns {String} The test description. |
|
103 */ |
|
104 getMessage: function() { |
|
105 var rv = this.id + ", "; |
|
106 if (this.mustReset) { |
|
107 rv += "original timeout at " + this.timeLimit + ", "; |
|
108 rv += "reset at " + this.resetAfter + " to " + this.resetTo; |
|
109 } |
|
110 else { |
|
111 rv += "timeout scheduled at " + this.timeLimit; |
|
112 } |
|
113 return rv; |
|
114 }, |
|
115 |
|
116 /** |
|
117 * Check the event received, and if it's the right (and only) one we get. |
|
118 * |
|
119 * @param {DOMProgressEvent} evt An event of type "load" or "timeout". |
|
120 */ |
|
121 handleEvent: function(evt) { |
|
122 if (this.hasFired) { |
|
123 ok(false, "Only one event should fire: " + this.getMessage()); |
|
124 return; |
|
125 } |
|
126 this.hasFired = true; |
|
127 |
|
128 var type = evt.type, expectedType; |
|
129 // The XHR responds after 3000 milliseconds with a load event. |
|
130 var timeLimit = this.mustReset && (this.resetAfter < Math.min(3000, this.timeLimit)) ? |
|
131 this.resetTo : |
|
132 this.timeLimit; |
|
133 if ((timeLimit == 0) || (timeLimit >= 3000)) { |
|
134 expectedType = "load"; |
|
135 } |
|
136 else { |
|
137 expectedType = "timeout"; |
|
138 } |
|
139 is(type, expectedType, this.getMessage()); |
|
140 TestCounter.testComplete(); |
|
141 } |
|
142 }; |
|
143 |
|
144 /** |
|
145 * Generate and track XMLHttpRequests which will have abort() called on. |
|
146 * |
|
147 * @param shouldAbort {Boolean} True if we should call abort at all. |
|
148 * @param abortDelay {Number} The time in ms to wait before calling abort(). |
|
149 */ |
|
150 function AbortedRequest(shouldAbort, abortDelay) { |
|
151 this.shouldAbort = shouldAbort; |
|
152 this.abortDelay = abortDelay; |
|
153 this.hasFired = false; |
|
154 } |
|
155 AbortedRequest.prototype = { |
|
156 /** |
|
157 * Start the XMLHttpRequest! |
|
158 */ |
|
159 startXHR: function() { |
|
160 var req = new XMLHttpRequest(); |
|
161 this.request = req; |
|
162 req.open("GET", "file_XHR_timeout.sjs"); |
|
163 var me = this; |
|
164 function handleEvent(e) { return me.handleEvent(e); }; |
|
165 req.onerror = handleEvent; |
|
166 req.onload = handleEvent; |
|
167 req.onabort = handleEvent; |
|
168 req.ontimeout = handleEvent; |
|
169 |
|
170 req.timeout = 2000; |
|
171 var _this = this; |
|
172 |
|
173 function abortReq() { |
|
174 req.abort(); |
|
175 } |
|
176 |
|
177 if (!this.shouldAbort) { |
|
178 self.setTimeout(function() { |
|
179 try { |
|
180 _this.noEventsFired(); |
|
181 } |
|
182 catch (e) { |
|
183 ok(false, "Unexpected error: " + e); |
|
184 TestCounter.testComplete(); |
|
185 } |
|
186 }, 5000); |
|
187 } |
|
188 else { |
|
189 // Abort events can only be triggered on sent requests. |
|
190 req.send(); |
|
191 if (this.abortDelay == -1) { |
|
192 abortReq(); |
|
193 } |
|
194 else { |
|
195 self.setTimeout(abortReq, this.abortDelay); |
|
196 } |
|
197 } |
|
198 }, |
|
199 |
|
200 /** |
|
201 * Ensure that no events fired at all, especially not our timeout event. |
|
202 */ |
|
203 noEventsFired: function() { |
|
204 ok(!this.hasFired, "No events should fire for an unsent, unaborted request"); |
|
205 // We're done; if timeout hasn't fired by now, it never will. |
|
206 TestCounter.testComplete(); |
|
207 }, |
|
208 |
|
209 /** |
|
210 * Get a message describing this test. |
|
211 * |
|
212 * @returns {String} The test description. |
|
213 */ |
|
214 getMessage: function() { |
|
215 return "time to abort is " + this.abortDelay + ", timeout set at 2000"; |
|
216 }, |
|
217 |
|
218 /** |
|
219 * Check the event received, and if it's the right (and only) one we get. |
|
220 * |
|
221 * @param {DOMProgressEvent} evt An event of type "load" or "timeout". |
|
222 */ |
|
223 handleEvent: function(evt) { |
|
224 if (this.hasFired) { |
|
225 ok(false, "Only abort event should fire: " + this.getMessage()); |
|
226 return; |
|
227 } |
|
228 this.hasFired = true; |
|
229 |
|
230 var expectedEvent = (this.abortDelay >= 2000) ? "timeout" : "abort"; |
|
231 is(evt.type, expectedEvent, this.getMessage()); |
|
232 TestCounter.testComplete(); |
|
233 } |
|
234 }; |
|
235 |
|
236 var SyncRequestSettingTimeoutAfterOpen = { |
|
237 startXHR: function() { |
|
238 var pass = false; |
|
239 var req = new XMLHttpRequest(); |
|
240 req.open("GET", "file_XHR_timeout.sjs", false); |
|
241 try { |
|
242 req.timeout = 1000; |
|
243 } |
|
244 catch (e) { |
|
245 pass = true; |
|
246 } |
|
247 ok(pass, "Synchronous XHR must not allow a timeout to be set"); |
|
248 TestCounter.testComplete(); |
|
249 } |
|
250 }; |
|
251 |
|
252 var SyncRequestSettingTimeoutBeforeOpen = { |
|
253 startXHR: function() { |
|
254 var pass = false; |
|
255 var req = new XMLHttpRequest(); |
|
256 req.timeout = 1000; |
|
257 try { |
|
258 req.open("GET", "file_XHR_timeout.sjs", false); |
|
259 } |
|
260 catch (e) { |
|
261 pass = true; |
|
262 } |
|
263 ok(pass, "Synchronous XHR must not allow a timeout to be set"); |
|
264 TestCounter.testComplete(); |
|
265 } |
|
266 }; |
|
267 |
|
268 var TestRequests = [ |
|
269 // Simple timeouts. |
|
270 new RequestTracker(true, "no time out scheduled, load fires normally", 0), |
|
271 new RequestTracker(true, "load fires normally", 5000), |
|
272 new RequestTracker(true, "timeout hit before load", 2000), |
|
273 |
|
274 // Timeouts reset after a certain delay. |
|
275 new RequestTracker(true, "load fires normally with no timeout set, twice", 0, 2000, 0), |
|
276 new RequestTracker(true, "load fires normally with same timeout set twice", 5000, 2000, 5000), |
|
277 new RequestTracker(true, "timeout fires normally with same timeout set twice", 2000, 1000, 2000), |
|
278 |
|
279 new RequestTracker(true, "timeout disabled after initially set", 5000, 2000, 0), |
|
280 new RequestTracker(true, "timeout overrides load after a delay", 5000, 1000, 2000), |
|
281 new RequestTracker(true, "timeout enabled after initially disabled", 0, 2000, 5000), |
|
282 |
|
283 new RequestTracker(true, "timeout set to expiring value after load fires", 5000, 4000, 1000), |
|
284 new RequestTracker(true, "timeout set to expired value before load fires", 5000, 2000, 1000), |
|
285 new RequestTracker(true, "timeout set to non-expiring value after timeout fires", 1000, 2000, 5000), |
|
286 |
|
287 // Aborted requests. |
|
288 new AbortedRequest(false), |
|
289 new AbortedRequest(true, -1), |
|
290 new AbortedRequest(true, 5000), |
|
291 ]; |
|
292 |
|
293 var MainThreadTestRequests = [ |
|
294 new AbortedRequest(true, 0), |
|
295 new AbortedRequest(true, 1000), |
|
296 |
|
297 // Synchronous requests. |
|
298 SyncRequestSettingTimeoutAfterOpen, |
|
299 SyncRequestSettingTimeoutBeforeOpen |
|
300 ]; |
|
301 |
|
302 var WorkerThreadTestRequests = [ |
|
303 // Simple timeouts. |
|
304 new RequestTracker(false, "no time out scheduled, load fires normally", 0), |
|
305 new RequestTracker(false, "load fires normally", 5000), |
|
306 new RequestTracker(false, "timeout hit before load", 2000), |
|
307 |
|
308 // Reset timeouts don't make much sense with a sync request ... |
|
309 ]; |
|
310 |
|
311 if (inWorker) { |
|
312 TestRequests = TestRequests.concat(WorkerThreadTestRequests); |
|
313 } else { |
|
314 TestRequests = TestRequests.concat(MainThreadTestRequests); |
|
315 } |
|
316 |
|
317 // This code controls moving from one test to another. |
|
318 var TestCounter = { |
|
319 testComplete: function() { |
|
320 // Allow for the possibility there are other events coming. |
|
321 self.setTimeout(function() { |
|
322 TestCounter.next(); |
|
323 }, 5000); |
|
324 }, |
|
325 |
|
326 next: function() { |
|
327 var test = TestRequests.shift(); |
|
328 |
|
329 if (test) { |
|
330 test.startXHR(); |
|
331 } |
|
332 else { |
|
333 message("done"); |
|
334 } |
|
335 } |
|
336 }; |
|
337 |
|
338 self.addEventListener("message", function (event) { |
|
339 if (event.data == "start") { |
|
340 TestCounter.next(); |
|
341 } |
|
342 }); |