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) {
178 177 (function(request) {
179 // playlist responses 178 var segmentSize;
180 if (/playlist-\d+$/.test(request.url)) { 179
181 // for simplicity, playlist responses have zero trasmission time 180 // playlist responses
182 if (t === arrival) { 181 if (/playlist-\d+$/.test(request.url)) {
183 request.respond(200, null, playlistResponse(+requests[0].url.match(/\d+$/))); 182 // for simplicity, playlist responses have zero trasmission time
184 // the request is completed 183 return setTimeout(function() {
185 return requests.splice(requests.indexOf(request), 1); 184 request.respond(200, null, playlistResponse(+request.url.match(/\d+$/)));
185 }, propagationDelay * 1000);
186 } 186 }
187 return; 187
188 } 188 // segment responses
189 189 segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration;
190 // segment responses 190 // segment response headers arrive after the propogation delay
191 segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration; 191 setTimeout(function() {
192 // segment response headers arrive after the propogation delay 192 var arrival = Math.ceil(+new Date() * 0.001);
193 if (t === arrival) { 193 results.playlists.push({
194 results.playlists.push({ 194 time: arrival,
195 time: t, 195 bitrate: +request.url.match(/(\d+)-\d+$/)[1]
196 bitrate: +request.url.match(/(\d+)-\d+$/)[1] 196 });
197 }); 197 request.setResponseHeaders({
198 request.setResponseHeaders({ 198 'Content-Type': 'video/mp2t'
199 'Content-Type': 'video/mp2t' 199 });
200 }); 200
201 } 201 results.bandwidth.slice(arrival).every(function(value, i) {
202 // send the response body if all bytes have been delivered 202 var remaining = segmentSize - request.delivered;
203 if (request.delivered >= segmentSize) { 203 if (remaining - value.bandwidth <= 0) {
204 buffered += segmentDuration; 204 // send the response body once all bytes have been delivered
205 request.status = 200; 205 setTimeout(function() {
206 request.response = new Uint8Array(segmentSize * 0.125); 206 buffered += segmentDuration;
207 request.setResponseBody(''); 207 request.status = 200;
208 // the request is completed 208 request.response = new Uint8Array(segmentSize * 0.125);
209 return requests.splice(requests.indexOf(request), 1); 209 request.setResponseBody('');
210 } 210 }, ((remaining / value.bandwidth) + i) * 1000);
211 // transmit the bits for this tick 211 return false;
212 if (t >= arrival) { 212 }
213 request.delivered += results.bandwidth[t].bandwidth; 213 // record the bits for this tick
214 214 request.delivered += value.bandwidth;
215 } 215 return true;
216 // response has not arrived fully 216 });
217 return; 217 }, propagationDelay * 1000);
218 }, []); 218 })(requests.shift());
219 }
219 220
220 results.buffered.push({ 221 results.buffered.push({
221 time: t, 222 time: t,
......