UNPKG

9.38 kBJavaScriptView Raw
1import QUnit from 'qunit';
2import {mediaSegmentRequest, REQUEST_ERRORS} from '../src/media-segment-request';
3import xhrFactory from '../src/xhr';
4import {useFakeEnvironment} from './test-helpers';
5import Decrypter from '../src/decrypter-worker';
6import worker from 'webworkify';
7
8QUnit.module('Media Segment Request', {
9 beforeEach(assert) {
10 this.env = useFakeEnvironment(assert);
11 this.clock = this.env.clock;
12 this.requests = this.env.requests;
13 this.xhr = xhrFactory();
14 this.realDecrypter = worker(Decrypter);
15 this.mockDecrypter = {
16 listeners: [],
17 postMessage(message) {
18 const newMessage = Object.create(message);
19
20 newMessage.decrypted = message.encrypted;
21 this.listeners.forEach((fn)=>fn({
22 data: newMessage
23 }));
24 },
25 addEventListener(event, listener) {
26 this.listeners.push(listener);
27 },
28 removeEventListener(event, listener) {
29 this.listeners = this.listeners.filter((fn)=>fn !== listener);
30 }
31 };
32 this.xhrOptions = {
33 timeout: 1000
34 };
35 this.noop = () => {};
36 },
37 afterEach(assert) {
38 this.realDecrypter.terminate();
39 this.env.restore();
40 }
41});
42
43QUnit.test('cancels outstanding segment request on abort', function(assert) {
44 const done = assert.async();
45
46 assert.expect(7);
47
48 const abort = mediaSegmentRequest(
49 this.xhr,
50 this.xhrOptions,
51 this.noop,
52 { resolvedUri: '0-test.ts' },
53 this.noop,
54 (error, segmentData) => {
55 assert.equal(this.requests.length, 1, 'there is only one request');
56 assert.equal(this.requests[0].uri, '0-test.ts', 'the request is for a segment');
57 assert.ok(this.requests[0].aborted, 'aborted the first request');
58 assert.ok(error, 'an error object was generated');
59 assert.equal(error.code, REQUEST_ERRORS.ABORTED, 'request was aborted');
60
61 done();
62 });
63
64 // Simulate Firefox's handling of aborted segments -
65 // Firefox sets the response to an empty array buffer if the xhr type is 'arraybuffer'
66 // and no data was received
67 this.requests[0].response = new ArrayBuffer();
68
69 abort();
70});
71
72QUnit.test('cancels outstanding key requests on abort', function(assert) {
73 let keyReq;
74 const done = assert.async();
75
76 assert.expect(7);
77
78 const abort = mediaSegmentRequest(
79 this.xhr,
80 this.xhrOptions,
81 this.noop,
82 {
83 resolvedUri: '0-test.ts',
84 key: {
85 resolvedUri: '0-key.php'
86 }
87 },
88 this.noop,
89 (error, segmentData) => {
90 assert.ok(keyReq.aborted, 'aborted the key request');
91 assert.equal(error.code, REQUEST_ERRORS.ABORTED, 'key request was aborted');
92
93 done();
94 });
95
96 assert.equal(this.requests.length, 2, 'there are two requests');
97
98 keyReq = this.requests.shift();
99 const segmentReq = this.requests.shift();
100
101 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
102 assert.equal(segmentReq.uri, '0-test.ts', 'the second request is for a segment');
103
104 // Fulfill the segment request
105 segmentReq.response = new Uint8Array(10).buffer;
106 segmentReq.respond(200, null, '');
107
108 abort();
109});
110
111QUnit.test('cancels outstanding key requests on failure', function(assert) {
112 let keyReq;
113 const done = assert.async();
114
115 assert.expect(7);
116 mediaSegmentRequest(
117 this.xhr,
118 this.xhrOptions,
119 this.noop,
120 {
121 resolvedUri: '0-test.ts',
122 key: {
123 resolvedUri: '0-key.php'
124 }
125 },
126 this.noop,
127 (error, segmentData) => {
128 assert.ok(keyReq.aborted, 'aborted the key request');
129 assert.equal(error.code, REQUEST_ERRORS.FAILURE, 'segment request failed');
130
131 done();
132 });
133
134 assert.equal(this.requests.length, 2, 'there are two requests');
135
136 keyReq = this.requests.shift();
137 const segmentReq = this.requests.shift();
138
139 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
140 assert.equal(segmentReq.uri, '0-test.ts', 'the second request is for a segment');
141
142 // Fulfill the segment request
143 segmentReq.respond(500, null, '');
144});
145
146QUnit.test('cancels outstanding key requests on timeout', function(assert) {
147 let keyReq;
148 const done = assert.async();
149
150 assert.expect(7);
151 mediaSegmentRequest(
152 this.xhr,
153 this.xhrOptions,
154 this.noop,
155 {
156 resolvedUri: '0-test.ts',
157 key: {
158 resolvedUri: '0-key.php'
159 }
160 },
161 this.noop,
162 (error, segmentData) => {
163 assert.ok(keyReq.aborted, 'aborted the key request');
164 assert.equal(error.code, REQUEST_ERRORS.TIMEOUT, 'key request failed');
165
166 done();
167 });
168 assert.equal(this.requests.length, 2, 'there are two requests');
169
170 keyReq = this.requests.shift();
171 const segmentReq = this.requests.shift();
172
173 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
174 assert.equal(segmentReq.uri, '0-test.ts', 'the second request is for a segment');
175
176 // Timeout request
177 this.clock.tick(2000);
178});
179
180QUnit.test('the key response is converted to the correct format', function(assert) {
181 let keyReq;
182 const done = assert.async();
183 const postMessage = this.mockDecrypter.postMessage;
184
185 assert.expect(9);
186 this.mockDecrypter.postMessage = (message) => {
187 const key = new Uint32Array(message.key.bytes,
188 message.key.byteOffset,
189 message.key.byteLength / 4);
190
191 assert.deepEqual(key,
192 new Uint32Array([0, 0x01000000, 0x02000000, 0x03000000]),
193 'passed the specified segment key');
194 postMessage.call(this.mockDecrypter, message);
195 };
196
197 mediaSegmentRequest(
198 this.xhr,
199 this.xhrOptions,
200 this.mockDecrypter,
201 {
202 resolvedUri: '0-test.ts',
203 key: {
204 resolvedUri: '0-key.php',
205 IV: [0, 0, 0, 1]
206 }
207 },
208 this.noop,
209 (error, segmentData) => {
210 assert.notOk(error, 'there are no errors');
211 assert.equal(this.mockDecrypter.listeners.length,
212 0,
213 'all decryption webworker listeners are unbound');
214 // verify stats
215 assert.equal(segmentData.stats.bytesReceived, 10, '10 bytes');
216 done();
217 });
218
219 assert.equal(this.requests.length, 2, 'there are two requests');
220
221 keyReq = this.requests.shift();
222 const segmentReq = this.requests.shift();
223
224 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
225 assert.equal(segmentReq.uri, '0-test.ts', 'the second request is for a segment');
226
227 segmentReq.response = new Uint8Array(10).buffer;
228 segmentReq.respond(200, null, '');
229 keyReq.response = new Uint32Array([0, 1, 2, 3]).buffer;
230 keyReq.respond(200, null, '');
231});
232
233QUnit.test('segment with key has bytes decrypted', function(assert) {
234 const done = assert.async();
235
236 assert.expect(8);
237 mediaSegmentRequest(
238 this.xhr,
239 this.xhrOptions,
240 this.realDecrypter,
241 {
242 resolvedUri: '0-test.ts',
243 key: {
244 resolvedUri: '0-key.php',
245 iv: {
246 bytes: new Uint32Array([0, 0, 0, 1])
247 }
248 }
249 },
250 this.noop,
251 (error, segmentData) => {
252 assert.notOk(error, 'there are no errors');
253 assert.ok(segmentData.bytes, 'decrypted bytes in segment');
254
255 // verify stats
256 assert.equal(segmentData.stats.bytesReceived, 8, '8 bytes');
257 done();
258 });
259
260 assert.equal(this.requests.length, 2, 'there are two requests');
261
262 const keyReq = this.requests.shift();
263 const segmentReq = this.requests.shift();
264
265 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
266 assert.equal(segmentReq.uri, '0-test.ts', 'the second request is for a segment');
267
268 segmentReq.response = new Uint8Array(8).buffer;
269 segmentReq.respond(200, null, '');
270 keyReq.response = new Uint32Array([0, 1, 2, 3]).buffer;
271 keyReq.respond(200, null, '');
272
273 // Allow the decrypter to decrypt
274 this.clock.tick(100);
275});
276
277QUnit.test('waits for every request to finish before the callback is run',
278function(assert) {
279 const done = assert.async();
280
281 assert.expect(10);
282 mediaSegmentRequest(
283 this.xhr,
284 this.xhrOptions,
285 this.realDecrypter,
286 {
287 resolvedUri: '0-test.ts',
288 key: {
289 resolvedUri: '0-key.php',
290 iv: {
291 bytes: new Uint32Array([0, 0, 0, 1])
292 }
293 },
294 map: {
295 resolvedUri: '0-init.dat'
296 }
297 },
298 this.noop,
299 (error, segmentData) => {
300 assert.notOk(error, 'there are no errors');
301 assert.ok(segmentData.bytes, 'decrypted bytes in segment');
302 assert.ok(segmentData.map.bytes, 'init segment bytes in map');
303
304 // verify stats
305 assert.equal(segmentData.stats.bytesReceived, 8, '8 bytes');
306 done();
307 });
308
309 assert.equal(this.requests.length, 3, 'there are three requests');
310
311 const keyReq = this.requests.shift();
312 const initReq = this.requests.shift();
313 const segmentReq = this.requests.shift();
314
315 assert.equal(keyReq.uri, '0-key.php', 'the first request is for a key');
316 assert.equal(initReq.uri, '0-init.dat', 'the second request is for the init segment');
317 assert.equal(segmentReq.uri, '0-test.ts', 'the third request is for a segment');
318
319 segmentReq.response = new Uint8Array(8).buffer;
320 segmentReq.respond(200, null, '');
321 this.clock.tick(200);
322
323 initReq.response = new Uint32Array([0, 1, 2, 3]).buffer;
324 initReq.respond(200, null, '');
325 this.clock.tick(200);
326
327 keyReq.response = new Uint32Array([0, 1, 2, 3]).buffer;
328 keyReq.respond(200, null, '');
329
330 // Allow the decrypter to decrypt
331 this.clock.tick(100);
332});