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.
Showing
1 changed file
with
30 additions
and
29 deletions
... | @@ -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, | ... | ... |
-
Please register or sign in to post a comment