869d642e by David LaPalomento

Schedule XHR responses instead of delivering them on the second

Bandwidth calculation uses the length of the XHR so rounding delivery up to the nearest second can skew results. Instead, calculate the delivery time of XHRs and schedule a timeout to set the response body.
1 parent fca6d104
...@@ -111,7 +111,7 @@ ...@@ -111,7 +111,7 @@
111 fakeXhr = sinon.useFakeXMLHttpRequest(); 111 fakeXhr = sinon.useFakeXMLHttpRequest();
112 requests = []; 112 requests = [];
113 fakeXhr.onCreate = function(xhr) { 113 fakeXhr.onCreate = function(xhr) {
114 xhr.startTime = t; 114 xhr.startTime = +new Date();
115 xhr.delivered = 0; 115 xhr.delivered = 0;
116 requests.push(xhr); 116 requests.push(xhr);
117 }; 117 };
...@@ -159,10 +159,8 @@ ...@@ -159,10 +159,8 @@
159 return left.time - right.time; 159 return left.time - right.time;
160 }); 160 });
161 161
162 // advance time and collect simulation results 162 // pre-calculate the bandwidth at each second
163 for (t = i = 0; t < duration; clock.tick(1 * 1000), t++) { 163 for (t = i = 0; t < duration; t++) {
164
165 // determine the bandwidth value at this moment
166 while (bandwidths[i + 1] && bandwidths[i + 1].time <= t) { 164 while (bandwidths[i + 1] && bandwidths[i + 1].time <= t) {
167 i++; 165 i++;
168 } 166 }
...@@ -170,52 +168,55 @@ ...@@ -170,52 +168,55 @@
170 time: t, 168 time: t,
171 bandwidth: bandwidths[i].bandwidth 169 bandwidth: bandwidths[i].bandwidth
172 }); 170 });
171 }
173 172
174 // deliver responses if they're ready 173 // advance time and collect simulation results
175 requests.forEach(function(request, i) { 174 for (t = 0; t < duration; clock.tick(1000), t++) {
176 var arrival = request.startTime + propagationDelay, 175 // schedule response deliveries
177 segmentSize; 176 while (requests.length) {
177 (function(request) {
178 var segmentSize;
178 179
179 // playlist responses 180 // playlist responses
180 if (/playlist-\d+$/.test(request.url)) { 181 if (/playlist-\d+$/.test(request.url)) {
181 // for simplicity, playlist responses have zero trasmission time 182 // for simplicity, playlist responses have zero trasmission time
182 if (t === arrival) { 183 return setTimeout(function() {
183 request.respond(200, null, playlistResponse(+requests[0].url.match(/\d+$/))); 184 request.respond(200, null, playlistResponse(+request.url.match(/\d+$/)));
184 // the request is completed 185 }, propagationDelay * 1000);
185 return requests.splice(requests.indexOf(request), 1);
186 }
187 return;
188 } 186 }
189 187
190 // segment responses 188 // segment responses
191 segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration; 189 segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration;
192 // segment response headers arrive after the propogation delay 190 // segment response headers arrive after the propogation delay
193 if (t === arrival) { 191 setTimeout(function() {
192 var arrival = Math.ceil(+new Date() * 0.001);
194 results.playlists.push({ 193 results.playlists.push({
195 time: t, 194 time: arrival,
196 bitrate: +request.url.match(/(\d+)-\d+$/)[1] 195 bitrate: +request.url.match(/(\d+)-\d+$/)[1]
197 }); 196 });
198 request.setResponseHeaders({ 197 request.setResponseHeaders({
199 'Content-Type': 'video/mp2t' 198 'Content-Type': 'video/mp2t'
200 }); 199 });
201 } 200
202 // send the response body if all bytes have been delivered 201 results.bandwidth.slice(arrival).every(function(value, i) {
203 if (request.delivered >= segmentSize) { 202 var remaining = segmentSize - request.delivered;
203 if (remaining - value.bandwidth <= 0) {
204 // send the response body once all bytes have been delivered
205 setTimeout(function() {
204 buffered += segmentDuration; 206 buffered += segmentDuration;
205 request.status = 200; 207 request.status = 200;
206 request.response = new Uint8Array(segmentSize * 0.125); 208 request.response = new Uint8Array(segmentSize * 0.125);
207 request.setResponseBody(''); 209 request.setResponseBody('');
208 // the request is completed 210 }, ((remaining / value.bandwidth) + i) * 1000);
209 return requests.splice(requests.indexOf(request), 1); 211 return false;
210 } 212 }
211 // transmit the bits for this tick 213 // record the bits for this tick
212 if (t >= arrival) { 214 request.delivered += value.bandwidth;
213 request.delivered += results.bandwidth[t].bandwidth; 215 return true;
214 216 });
217 }, propagationDelay * 1000);
218 })(requests.shift());
215 } 219 }
216 // response has not arrived fully
217 return;
218 }, []);
219 220
220 results.buffered.push({ 221 results.buffered.push({
221 time: t, 222 time: t,
......