1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/test/test_XHR_timeout.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,342 @@ 1.4 +/* Notes: 1.5 + - All times are expressed in milliseconds in this test suite. 1.6 + - Test harness code is at the end of this file. 1.7 + - We generate only one request at a time, to avoid overloading the HTTP 1.8 + request handlers. 1.9 + */ 1.10 + 1.11 +var inWorker = false; 1.12 +try { 1.13 + inWorker = !(self instanceof Window); 1.14 +} catch (e) { 1.15 + inWorker = true; 1.16 +} 1.17 + 1.18 +function message(data) { 1.19 + if (inWorker) 1.20 + self.postMessage(data); 1.21 + else 1.22 + self.postMessage(data, "*"); 1.23 +} 1.24 + 1.25 +function is(got, expected, msg) { 1.26 + var obj = {}; 1.27 + obj.type = "is"; 1.28 + obj.got = got; 1.29 + obj.expected = expected; 1.30 + obj.msg = msg; 1.31 + 1.32 + message(obj); 1.33 +} 1.34 + 1.35 +function ok(bool, msg) { 1.36 + var obj = {}; 1.37 + obj.type = "ok"; 1.38 + obj.bool = bool; 1.39 + obj.msg = msg; 1.40 + 1.41 + message(obj); 1.42 +} 1.43 + 1.44 +/** 1.45 + * Generate and track results from a XMLHttpRequest with regards to timeouts. 1.46 + * 1.47 + * @param {String} id The test description. 1.48 + * @param {Number} timeLimit The initial setting for the request timeout. 1.49 + * @param {Number} resetAfter (Optional) The time after sending the request, to 1.50 + * reset the timeout. 1.51 + * @param {Number} resetTo (Optional) The delay to reset the timeout to. 1.52 + * 1.53 + * @note The actual testing takes place in handleEvent(event). 1.54 + * The requests are generated in startXHR(). 1.55 + * 1.56 + * @note If resetAfter and resetTo are omitted, only the initial timeout setting 1.57 + * applies. 1.58 + * 1.59 + * @constructor 1.60 + * @implements DOMEventListener 1.61 + */ 1.62 +function RequestTracker(async, id, timeLimit /*[, resetAfter, resetTo]*/) { 1.63 + this.async = async; 1.64 + this.id = id; 1.65 + this.timeLimit = timeLimit; 1.66 + 1.67 + if (arguments.length > 3) { 1.68 + this.mustReset = true; 1.69 + this.resetAfter = arguments[3]; 1.70 + this.resetTo = arguments[4]; 1.71 + } 1.72 + 1.73 + this.hasFired = false; 1.74 +} 1.75 +RequestTracker.prototype = { 1.76 + /** 1.77 + * Start the XMLHttpRequest! 1.78 + */ 1.79 + startXHR: function() { 1.80 + var req = new XMLHttpRequest(); 1.81 + this.request = req; 1.82 + req.open("GET", "file_XHR_timeout.sjs", this.async); 1.83 + var me = this; 1.84 + function handleEvent(e) { return me.handleEvent(e); }; 1.85 + req.onerror = handleEvent; 1.86 + req.onload = handleEvent; 1.87 + req.onabort = handleEvent; 1.88 + req.ontimeout = handleEvent; 1.89 + 1.90 + req.timeout = this.timeLimit; 1.91 + 1.92 + if (this.mustReset) { 1.93 + var resetTo = this.resetTo; 1.94 + self.setTimeout(function() { 1.95 + req.timeout = resetTo; 1.96 + }, this.resetAfter); 1.97 + } 1.98 + 1.99 + req.send(null); 1.100 + }, 1.101 + 1.102 + /** 1.103 + * Get a message describing this test. 1.104 + * 1.105 + * @returns {String} The test description. 1.106 + */ 1.107 + getMessage: function() { 1.108 + var rv = this.id + ", "; 1.109 + if (this.mustReset) { 1.110 + rv += "original timeout at " + this.timeLimit + ", "; 1.111 + rv += "reset at " + this.resetAfter + " to " + this.resetTo; 1.112 + } 1.113 + else { 1.114 + rv += "timeout scheduled at " + this.timeLimit; 1.115 + } 1.116 + return rv; 1.117 + }, 1.118 + 1.119 + /** 1.120 + * Check the event received, and if it's the right (and only) one we get. 1.121 + * 1.122 + * @param {DOMProgressEvent} evt An event of type "load" or "timeout". 1.123 + */ 1.124 + handleEvent: function(evt) { 1.125 + if (this.hasFired) { 1.126 + ok(false, "Only one event should fire: " + this.getMessage()); 1.127 + return; 1.128 + } 1.129 + this.hasFired = true; 1.130 + 1.131 + var type = evt.type, expectedType; 1.132 + // The XHR responds after 3000 milliseconds with a load event. 1.133 + var timeLimit = this.mustReset && (this.resetAfter < Math.min(3000, this.timeLimit)) ? 1.134 + this.resetTo : 1.135 + this.timeLimit; 1.136 + if ((timeLimit == 0) || (timeLimit >= 3000)) { 1.137 + expectedType = "load"; 1.138 + } 1.139 + else { 1.140 + expectedType = "timeout"; 1.141 + } 1.142 + is(type, expectedType, this.getMessage()); 1.143 + TestCounter.testComplete(); 1.144 + } 1.145 +}; 1.146 + 1.147 +/** 1.148 + * Generate and track XMLHttpRequests which will have abort() called on. 1.149 + * 1.150 + * @param shouldAbort {Boolean} True if we should call abort at all. 1.151 + * @param abortDelay {Number} The time in ms to wait before calling abort(). 1.152 + */ 1.153 +function AbortedRequest(shouldAbort, abortDelay) { 1.154 + this.shouldAbort = shouldAbort; 1.155 + this.abortDelay = abortDelay; 1.156 + this.hasFired = false; 1.157 +} 1.158 +AbortedRequest.prototype = { 1.159 + /** 1.160 + * Start the XMLHttpRequest! 1.161 + */ 1.162 + startXHR: function() { 1.163 + var req = new XMLHttpRequest(); 1.164 + this.request = req; 1.165 + req.open("GET", "file_XHR_timeout.sjs"); 1.166 + var me = this; 1.167 + function handleEvent(e) { return me.handleEvent(e); }; 1.168 + req.onerror = handleEvent; 1.169 + req.onload = handleEvent; 1.170 + req.onabort = handleEvent; 1.171 + req.ontimeout = handleEvent; 1.172 + 1.173 + req.timeout = 2000; 1.174 + var _this = this; 1.175 + 1.176 + function abortReq() { 1.177 + req.abort(); 1.178 + } 1.179 + 1.180 + if (!this.shouldAbort) { 1.181 + self.setTimeout(function() { 1.182 + try { 1.183 + _this.noEventsFired(); 1.184 + } 1.185 + catch (e) { 1.186 + ok(false, "Unexpected error: " + e); 1.187 + TestCounter.testComplete(); 1.188 + } 1.189 + }, 5000); 1.190 + } 1.191 + else { 1.192 + // Abort events can only be triggered on sent requests. 1.193 + req.send(); 1.194 + if (this.abortDelay == -1) { 1.195 + abortReq(); 1.196 + } 1.197 + else { 1.198 + self.setTimeout(abortReq, this.abortDelay); 1.199 + } 1.200 + } 1.201 + }, 1.202 + 1.203 + /** 1.204 + * Ensure that no events fired at all, especially not our timeout event. 1.205 + */ 1.206 + noEventsFired: function() { 1.207 + ok(!this.hasFired, "No events should fire for an unsent, unaborted request"); 1.208 + // We're done; if timeout hasn't fired by now, it never will. 1.209 + TestCounter.testComplete(); 1.210 + }, 1.211 + 1.212 + /** 1.213 + * Get a message describing this test. 1.214 + * 1.215 + * @returns {String} The test description. 1.216 + */ 1.217 + getMessage: function() { 1.218 + return "time to abort is " + this.abortDelay + ", timeout set at 2000"; 1.219 + }, 1.220 + 1.221 + /** 1.222 + * Check the event received, and if it's the right (and only) one we get. 1.223 + * 1.224 + * @param {DOMProgressEvent} evt An event of type "load" or "timeout". 1.225 + */ 1.226 + handleEvent: function(evt) { 1.227 + if (this.hasFired) { 1.228 + ok(false, "Only abort event should fire: " + this.getMessage()); 1.229 + return; 1.230 + } 1.231 + this.hasFired = true; 1.232 + 1.233 + var expectedEvent = (this.abortDelay >= 2000) ? "timeout" : "abort"; 1.234 + is(evt.type, expectedEvent, this.getMessage()); 1.235 + TestCounter.testComplete(); 1.236 + } 1.237 +}; 1.238 + 1.239 +var SyncRequestSettingTimeoutAfterOpen = { 1.240 + startXHR: function() { 1.241 + var pass = false; 1.242 + var req = new XMLHttpRequest(); 1.243 + req.open("GET", "file_XHR_timeout.sjs", false); 1.244 + try { 1.245 + req.timeout = 1000; 1.246 + } 1.247 + catch (e) { 1.248 + pass = true; 1.249 + } 1.250 + ok(pass, "Synchronous XHR must not allow a timeout to be set"); 1.251 + TestCounter.testComplete(); 1.252 + } 1.253 +}; 1.254 + 1.255 +var SyncRequestSettingTimeoutBeforeOpen = { 1.256 + startXHR: function() { 1.257 + var pass = false; 1.258 + var req = new XMLHttpRequest(); 1.259 + req.timeout = 1000; 1.260 + try { 1.261 + req.open("GET", "file_XHR_timeout.sjs", false); 1.262 + } 1.263 + catch (e) { 1.264 + pass = true; 1.265 + } 1.266 + ok(pass, "Synchronous XHR must not allow a timeout to be set"); 1.267 + TestCounter.testComplete(); 1.268 + } 1.269 +}; 1.270 + 1.271 +var TestRequests = [ 1.272 + // Simple timeouts. 1.273 + new RequestTracker(true, "no time out scheduled, load fires normally", 0), 1.274 + new RequestTracker(true, "load fires normally", 5000), 1.275 + new RequestTracker(true, "timeout hit before load", 2000), 1.276 + 1.277 + // Timeouts reset after a certain delay. 1.278 + new RequestTracker(true, "load fires normally with no timeout set, twice", 0, 2000, 0), 1.279 + new RequestTracker(true, "load fires normally with same timeout set twice", 5000, 2000, 5000), 1.280 + new RequestTracker(true, "timeout fires normally with same timeout set twice", 2000, 1000, 2000), 1.281 + 1.282 + new RequestTracker(true, "timeout disabled after initially set", 5000, 2000, 0), 1.283 + new RequestTracker(true, "timeout overrides load after a delay", 5000, 1000, 2000), 1.284 + new RequestTracker(true, "timeout enabled after initially disabled", 0, 2000, 5000), 1.285 + 1.286 + new RequestTracker(true, "timeout set to expiring value after load fires", 5000, 4000, 1000), 1.287 + new RequestTracker(true, "timeout set to expired value before load fires", 5000, 2000, 1000), 1.288 + new RequestTracker(true, "timeout set to non-expiring value after timeout fires", 1000, 2000, 5000), 1.289 + 1.290 + // Aborted requests. 1.291 + new AbortedRequest(false), 1.292 + new AbortedRequest(true, -1), 1.293 + new AbortedRequest(true, 5000), 1.294 +]; 1.295 + 1.296 +var MainThreadTestRequests = [ 1.297 + new AbortedRequest(true, 0), 1.298 + new AbortedRequest(true, 1000), 1.299 + 1.300 + // Synchronous requests. 1.301 + SyncRequestSettingTimeoutAfterOpen, 1.302 + SyncRequestSettingTimeoutBeforeOpen 1.303 +]; 1.304 + 1.305 +var WorkerThreadTestRequests = [ 1.306 + // Simple timeouts. 1.307 + new RequestTracker(false, "no time out scheduled, load fires normally", 0), 1.308 + new RequestTracker(false, "load fires normally", 5000), 1.309 + new RequestTracker(false, "timeout hit before load", 2000), 1.310 + 1.311 + // Reset timeouts don't make much sense with a sync request ... 1.312 +]; 1.313 + 1.314 +if (inWorker) { 1.315 + TestRequests = TestRequests.concat(WorkerThreadTestRequests); 1.316 +} else { 1.317 + TestRequests = TestRequests.concat(MainThreadTestRequests); 1.318 +} 1.319 + 1.320 +// This code controls moving from one test to another. 1.321 +var TestCounter = { 1.322 + testComplete: function() { 1.323 + // Allow for the possibility there are other events coming. 1.324 + self.setTimeout(function() { 1.325 + TestCounter.next(); 1.326 + }, 5000); 1.327 + }, 1.328 + 1.329 + next: function() { 1.330 + var test = TestRequests.shift(); 1.331 + 1.332 + if (test) { 1.333 + test.startXHR(); 1.334 + } 1.335 + else { 1.336 + message("done"); 1.337 + } 1.338 + } 1.339 +}; 1.340 + 1.341 +self.addEventListener("message", function (event) { 1.342 + if (event.data == "start") { 1.343 + TestCounter.next(); 1.344 + } 1.345 +});